mirror of https://github.com/kidoman/embd
213 lines
4.7 KiB
Go
213 lines
4.7 KiB
Go
package hd44780
|
|
|
|
import (
|
|
"github.com/golang/glog"
|
|
"github.com/kidoman/embd"
|
|
)
|
|
|
|
// DefaultModes are the default initialization modes for a CharacterDisplay.
|
|
var DefaultModes []ModeSetter = []ModeSetter{
|
|
FourBitMode,
|
|
OneLine,
|
|
Dots5x8,
|
|
EntryIncrement,
|
|
EntryShiftOff,
|
|
DisplayOn,
|
|
CursorOff,
|
|
BlinkOff,
|
|
}
|
|
|
|
// CharacterDisplay represents an abstract character display and provides a
|
|
// convenience layer on top of the basic HD44780 library.
|
|
type CharacterDisplay struct {
|
|
*HD44780
|
|
Cols int
|
|
Rows int
|
|
p *position
|
|
}
|
|
|
|
type position struct {
|
|
col int
|
|
row int
|
|
}
|
|
|
|
// NewGPIOCharacterDisplay creates a new CharacterDisplay connected by a 4-bit GPIO bus.
|
|
func NewGPIOCharacterDisplay(
|
|
rs, en, d4, d5, d6, d7, backlight interface{},
|
|
blPolarity Polarity,
|
|
cols, rows int,
|
|
modes ...ModeSetter,
|
|
) (*CharacterDisplay, error) {
|
|
pinKeys := []interface{}{rs, en, d4, d5, d6, d7, backlight}
|
|
pins := [7]embd.DigitalPin{}
|
|
for idx, key := range pinKeys {
|
|
if key == nil {
|
|
continue
|
|
}
|
|
var digitalPin embd.DigitalPin
|
|
if pin, ok := key.(embd.DigitalPin); ok {
|
|
digitalPin = pin
|
|
} else {
|
|
var err error
|
|
digitalPin, err = embd.NewDigitalPin(key)
|
|
if err != nil {
|
|
glog.V(1).Infof("hd44780: error creating digital pin %+v: %s", key, err)
|
|
return nil, err
|
|
}
|
|
}
|
|
pins[idx] = digitalPin
|
|
}
|
|
hd, err := NewGPIO(
|
|
pins[0],
|
|
pins[1],
|
|
pins[2],
|
|
pins[3],
|
|
pins[4],
|
|
pins[5],
|
|
pins[6],
|
|
blPolarity,
|
|
append(DefaultModes, modes...)...,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewCharacterDisplay(hd, cols, rows)
|
|
}
|
|
|
|
// NewI2CCharacterDisplay creates a new CharacterDisplay connected by an I²C bus.
|
|
func NewI2CCharacterDisplay(
|
|
i2c embd.I2CBus,
|
|
addr byte,
|
|
pinMap I2CPinMap,
|
|
cols, rows int,
|
|
modes ...ModeSetter,
|
|
) (*CharacterDisplay, error) {
|
|
hd, err := NewI2C(i2c, addr, pinMap, append(DefaultModes, modes...)...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewCharacterDisplay(hd, cols, rows)
|
|
}
|
|
|
|
// NewCharacterDisplay creates a new character display abstraction for an
|
|
// HD44780-compatible controller.
|
|
func NewCharacterDisplay(hd *HD44780, cols, rows int) (*CharacterDisplay, error) {
|
|
display := &CharacterDisplay{
|
|
HD44780: hd,
|
|
Cols: cols,
|
|
Rows: rows,
|
|
p: &position{0, 0},
|
|
}
|
|
err := display.BacklightOn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return display, nil
|
|
}
|
|
|
|
// Home moves the cursor and all characters to the home position.
|
|
func (disp *CharacterDisplay) Home() error {
|
|
disp.currentPosition(0, 0)
|
|
return disp.HD44780.Home()
|
|
}
|
|
|
|
// Clear clears the display, preserving the mode settings and setting the correct home.
|
|
func (disp *CharacterDisplay) Clear() error {
|
|
disp.currentPosition(0, 0)
|
|
err := disp.HD44780.Clear()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = disp.SetMode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !disp.isLeftToRight() {
|
|
return disp.SetCursor(disp.Cols-1, 0)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Message prints the given string on the display.
|
|
func (disp *CharacterDisplay) Message(message string) error {
|
|
bytes := []byte(message)
|
|
for _, b := range bytes {
|
|
if b == byte('\n') {
|
|
err := disp.Newline()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
err := disp.WriteChar(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if disp.isLeftToRight() {
|
|
disp.p.col++
|
|
} else {
|
|
disp.p.col--
|
|
}
|
|
if disp.p.col >= disp.Cols || disp.p.col < 0 {
|
|
err := disp.Newline()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Newline moves the input cursor to the beginning of the next line.
|
|
func (disp *CharacterDisplay) Newline() error {
|
|
var col int
|
|
if disp.isLeftToRight() {
|
|
col = 0
|
|
} else {
|
|
col = disp.Cols - 1
|
|
}
|
|
return disp.SetCursor(col, disp.p.row+1)
|
|
}
|
|
|
|
func (disp *CharacterDisplay) isLeftToRight() bool {
|
|
// EntryIncrement and EntryShiftOn is right-to-left
|
|
// EntryDecrement and EntryShiftOn is left-to-right
|
|
// EntryIncrement and EntryShiftOff is left-to-right
|
|
// EntryDecrement and EntryShiftOff is right-to-left
|
|
return disp.EntryIncrementEnabled() != disp.EntryShiftEnabled()
|
|
}
|
|
|
|
// SetCursor sets the input cursor to the given position.
|
|
func (disp *CharacterDisplay) SetCursor(col, row int) error {
|
|
if row >= disp.Rows {
|
|
row = disp.Rows - 1
|
|
}
|
|
disp.currentPosition(col, row)
|
|
return disp.HD44780.SetCursor(byte(col) + disp.lcdRowOffset(row))
|
|
}
|
|
|
|
func (disp *CharacterDisplay) lcdRowOffset(row int) byte {
|
|
// Offset for up to 4 rows
|
|
if row > 3 {
|
|
row = 3
|
|
}
|
|
switch disp.Cols {
|
|
case 16:
|
|
// 16-char line mappings
|
|
return []byte{0x00, 0x40, 0x10, 0x50}[row]
|
|
default:
|
|
// default to the 20-char line mappings
|
|
return []byte{0x00, 0x40, 0x14, 0x54}[row]
|
|
}
|
|
}
|
|
|
|
func (disp *CharacterDisplay) currentPosition(col, row int) {
|
|
disp.p.col = col
|
|
disp.p.row = row
|
|
}
|
|
|
|
// Close closes the underlying HD44780 controller.
|
|
func (disp *CharacterDisplay) Close() error {
|
|
return disp.HD44780.Close()
|
|
}
|