1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-24 13:48:49 +08:00
hybridgroup.gobot/drivers/i2c/adafruit1109_driver.go
Thomas Kohler 865e724af0
Build(v2): revert move to v2 subfolder (#932)
* revert move to v2 subfolder
* fix CI and adjust CHANGELOG
2023-05-29 19:23:28 +02:00

303 lines
8.7 KiB
Go

package i2c
import (
"fmt"
"log"
"strconv"
"strings"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/gpio"
)
const adafruit1109Debug = false
type adafruit1109PortPin struct {
port string
pin uint8
}
// Adafruit1109Driver is a driver for the 2x16 LCD display with RGB backlit and 5 keys from adafruit, designed for Pi.
// The display is driven by the HD44780, and all is connected by i2c port expander MCP23017.
// https://www.adafruit.com/product/1109
//
// Have to implement DigitalWriter, DigitalReader interface
type Adafruit1109Driver struct {
name string
*MCP23017Driver
redPin adafruit1109PortPin
greenPin adafruit1109PortPin
bluePin adafruit1109PortPin
selectPin adafruit1109PortPin
upPin adafruit1109PortPin
downPin adafruit1109PortPin
leftPin adafruit1109PortPin
rightPin adafruit1109PortPin
rwPin adafruit1109PortPin
rsPin adafruit1109PortPin
enPin adafruit1109PortPin
dataPinD4 adafruit1109PortPin
dataPinD5 adafruit1109PortPin
dataPinD6 adafruit1109PortPin
dataPinD7 adafruit1109PortPin
*gpio.HD44780Driver
}
// NewAdafruit1109Driver creates is a new driver for the 2x16 LCD display with RGB backlit and 5 keys.
//
// Because HD44780 and MCP23017 are already implemented in gobot, we creates a wrapper for using existing implementation.
// So, for the documentation of the parameters, have a look at this drivers.
//
// Tests are done with a Tinkerboard.
func NewAdafruit1109Driver(a Connector, options ...func(Config)) *Adafruit1109Driver {
options = append(options, WithMCP23017AutoIODirOff(1))
mcp := NewMCP23017Driver(a, options...)
d := &Adafruit1109Driver{
name: gobot.DefaultName("Adafruit1109"),
MCP23017Driver: mcp,
redPin: adafruit1109PortPin{"A", 6},
greenPin: adafruit1109PortPin{"A", 7},
bluePin: adafruit1109PortPin{"B", 0},
selectPin: adafruit1109PortPin{"A", 0},
upPin: adafruit1109PortPin{"A", 3},
downPin: adafruit1109PortPin{"A", 2},
leftPin: adafruit1109PortPin{"A", 4},
rightPin: adafruit1109PortPin{"A", 1},
rwPin: adafruit1109PortPin{"B", 6},
rsPin: adafruit1109PortPin{"B", 7},
enPin: adafruit1109PortPin{"B", 5},
dataPinD4: adafruit1109PortPin{"B", 4},
dataPinD5: adafruit1109PortPin{"B", 3},
dataPinD6: adafruit1109PortPin{"B", 2},
dataPinD7: adafruit1109PortPin{"B", 1},
}
// mapping for HD44780 to MCP23017 port and IO, 4-Bit data
dataPins := gpio.HD44780DataPin{
D4: d.dataPinD4.String(),
D5: d.dataPinD5.String(),
D6: d.dataPinD6.String(),
D7: d.dataPinD7.String(),
}
//rwPin := "B_6" not mapped in HD44780 driver
// at test initialization, there seems rows and columns be switched
// but inside the driver the row is used as row and col as column
rows := 2
columns := 16
lcd := gpio.NewHD44780Driver(d, columns, rows, gpio.HD44780_4BITMODE, d.rsPin.String(), d.enPin.String(), dataPins)
lcd.SetRWPin(d.rwPin.String())
d.HD44780Driver = lcd
return d
}
// Connect implements the adaptor.Connector interface.
// Haven't found any adaptor which implements this with more content.
func (d *Adafruit1109Driver) Connect() error { return nil }
// Finalize implements the adaptor.Connector interface.
// Haven't found any adaptor which implements this with more content.
func (d *Adafruit1109Driver) Finalize() error { return nil }
// Name implements the gobot.Device interface
func (d *Adafruit1109Driver) Name() string {
return fmt.Sprintf("%s_%s_%s", d.name, d.MCP23017Driver.Name(), d.HD44780Driver.Name())
}
// SetName implements the gobot.Device interface.
func (d *Adafruit1109Driver) SetName(n string) { d.name = n }
// Connection implements the gobot.Device interface.
func (d *Adafruit1109Driver) Connection() gobot.Connection { return d.MCP23017Driver.Connection() }
// Start implements the gobot.Device interface.
func (d *Adafruit1109Driver) Start() error {
if adafruit1109Debug {
log.Printf("## MCP.Start ##")
}
if err := d.MCP23017Driver.Start(); err != nil {
return err
}
// set all to output (inputs will be set by initButton)
for pin := uint8(0); pin <= 7; pin++ {
if err := d.SetPinMode(pin, "A", 0); err != nil {
return err
}
if err := d.SetPinMode(pin, "B", 0); err != nil {
return err
}
}
// button pins are inputs, has inverse logic and needs pull up
if err := d.adafruit1109InitButton(d.selectPin); err != nil {
return err
}
if err := d.adafruit1109InitButton(d.upPin); err != nil {
return err
}
if err := d.adafruit1109InitButton(d.downPin); err != nil {
return err
}
if err := d.adafruit1109InitButton(d.leftPin); err != nil {
return err
}
if err := d.adafruit1109InitButton(d.rightPin); err != nil {
return err
}
// lets start with neutral background
if err := d.SetRGB(true, true, true); err != nil {
return err
}
// set rw pin to write
if err := d.writePin(d.rwPin, 0x00); err != nil {
return err
}
if adafruit1109Debug {
log.Printf("## HD.Start ##")
}
return d.HD44780Driver.Start()
}
// Halt implements the gobot.Device interface.
func (d *Adafruit1109Driver) Halt() error {
// we try halt on each device, not stopping on the first error
var errors []string
if err := d.HD44780Driver.Halt(); err != nil {
errors = append(errors, err.Error())
}
// switch off the background light
if err := d.SetRGB(false, false, false); err != nil {
errors = append(errors, err.Error())
}
// must be after HD44780Driver
if err := d.MCP23017Driver.Halt(); err != nil {
errors = append(errors, err.Error())
}
if len(errors) > 0 {
return fmt.Errorf("Halt the driver %s", strings.Join(errors, ", "))
}
return nil
}
// DigitalWrite implements the DigitalWriter interface
// This is called by HD44780 driver to set one gpio output. We redirect the call to the i2c driver MCP23017.
// The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
func (d *Adafruit1109Driver) DigitalWrite(id string, val byte) error {
portio := adafruit1109ParseID(id)
return d.writePin(portio, val)
}
// DigitalRead implements the DigitalReader interface
// This is called by HD44780 driver to read one gpio input. We redirect the call to the i2c driver MCP23017.
// The given id is the same as defined in dataPins and has the syntax "<port>_<pin>".
func (d *Adafruit1109Driver) DigitalRead(id string) (int, error) {
portio := adafruit1109ParseID(id)
uval, err := d.readPin(portio)
if err != nil {
return 0, err
}
return int(uval), err
}
// SetRGB sets the Red Green Blue value of backlit.
// The MCP23017 variant don't support PWM and have inverted logic
func (d *Adafruit1109Driver) SetRGB(r, g, b bool) error {
if adafruit1109Debug {
log.Printf("## SetRGB %t, %t, %t ##", r, g, b)
}
rio := d.redPin
gio := d.greenPin
bio := d.bluePin
rval := uint8(0x1)
gval := uint8(0x1)
bval := uint8(0x1)
if r {
rval = 0x00
}
if g {
gval = 0x00
}
if b {
bval = 0x00
}
if err := d.writePin(rio, rval); err != nil {
return err
}
if err := d.writePin(gio, gval); err != nil {
return err
}
if err := d.writePin(bio, bval); err != nil {
return err
}
return nil
}
// SelectButton reads the state of the "select" button (1=pressed).
func (d *Adafruit1109Driver) SelectButton() (uint8, error) {
return d.readPin(d.selectPin)
}
// UpButton reads the state of the "up" button (1=pressed).
func (d *Adafruit1109Driver) UpButton() (uint8, error) {
return d.readPin(d.upPin)
}
// DownButton reads the state of the "down" button (1=pressed).
func (d *Adafruit1109Driver) DownButton() (uint8, error) {
return d.readPin(d.downPin)
}
// LeftButton reads the state of the "left" button (1=pressed).
func (d *Adafruit1109Driver) LeftButton() (uint8, error) {
return d.readPin(d.leftPin)
}
// RightButton reads the state of the "right" button (1=pressed).
func (d *Adafruit1109Driver) RightButton() (uint8, error) {
return d.readPin(d.rightPin)
}
func (d *Adafruit1109Driver) writePin(ap adafruit1109PortPin, val uint8) error {
return d.WriteGPIO(ap.pin, ap.port, val)
}
func (d *Adafruit1109Driver) readPin(ap adafruit1109PortPin) (uint8, error) {
return d.ReadGPIO(ap.pin, ap.port)
}
func (ap *adafruit1109PortPin) String() string {
return fmt.Sprintf("%s_%d", ap.port, ap.pin)
}
func adafruit1109ParseID(id string) adafruit1109PortPin {
items := strings.Split(id, "_")
io := uint8(0)
if io64, err := strconv.ParseUint(items[1], 10, 32); err == nil {
io = uint8(io64)
}
return adafruit1109PortPin{port: items[0], pin: io}
}
func (d *Adafruit1109Driver) adafruit1109InitButton(p adafruit1109PortPin) error {
// make an input
if err := d.SetPinMode(p.pin, p.port, 1); err != nil {
return err
}
// add pull up resistors
if err := d.SetPullUp(p.pin, p.port, 1); err != nil {
return err
}
// invert polarity
if err := d.SetGPIOPolarity(p.pin, p.port, 1); err != nil {
return err
}
return nil
}