1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-27 13:48:56 +08:00
hybridgroup.gobot/drivers/i2c/mcp23017_driver.go

374 lines
11 KiB
Go
Raw Normal View History

package i2c
import (
"log"
"strings"
"gobot.io/x/gobot"
)
const (
// default address for device when a2/a1/a0 pins are all tied to ground
mcp23017Address = 0x20
)
var (
debug = false // toggle debugging information
)
// port contains all the registers for the device.
type port struct {
2016-03-07 23:51:45 -07:00
IODIR uint8 // I/O direction register: 0=output / 1=input
IPOL uint8 // input polarity register: 0=normal polarity / 1=inversed
GPINTEN uint8 // interrupt on change control register: 0=disabled / 1=enabled
DEFVAL uint8 // default compare register for interrupt on change
INTCON uint8 // interrupt control register: bit set to 0= use defval bit value to compare pin value/ bit set to 1= pin value compared to previous pin value
IOCON uint8 // configuration register
GPPU uint8 // pull-up resistor configuration register: 0=enabled / 1=disabled
INTF uint8 // interrupt flag register: 0=no interrupt / 1=pin caused interrupt
INTCAP uint8 // interrupt capture register, captures pin values during interrupt: 0=logic low / 1=logic high
GPIO uint8 // port register, reading from this register reads the port
OLAT uint8 // output latch register, write modifies the pins: 0=logic low / 1=logic high
}
// A bank is made up of PortA and PortB pins.
// Port B pins are on the left side of the chip (starting with pin 1), while port A pins are on the right side.
type bank struct {
PortA port
PortB port
}
2016-03-07 23:51:45 -07:00
// MCP23017Config contains the device configuration for the IOCON register.
// These fields should only be set with values 0 or 1.
type MCP23017Config struct {
Bank uint8
Mirror uint8
Seqop uint8
Disslw uint8
Haen uint8
Odr uint8
Intpol uint8
}
2016-03-07 23:51:45 -07:00
// MCP23017Driver contains the driver configuration parameters.
type MCP23017Driver struct {
name string
connector Connector
connection Connection
Config
MCPConf MCP23017Config
gobot.Commander
gobot.Eventer
}
// WithMCP23017Bank option sets the MCP23017Driver bank option
func WithMCP23017Bank(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Bank = val
} else {
panic("trying to set bank for non-MCP23017Driver")
}
}
}
// WithMCP23017Mirror option sets the MCP23017Driver Mirror option
func WithMCP23017Mirror(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Mirror = val
} else {
panic("Trying to set Mirror for non-MCP23017Driver")
}
}
}
// WithMCP23017Seqop option sets the MCP23017Driver Seqop option
func WithMCP23017Seqop(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Seqop = val
} else {
panic("Trying to set Seqop for non-MCP23017Driver")
}
}
}
// WithMCP23017Disslw option sets the MCP23017Driver Disslw option
func WithMCP23017Disslw(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Disslw = val
} else {
panic("Trying to set Disslw for non-MCP23017Driver")
}
}
}
// WithMCP23017Haen option sets the MCP23017Driver Haen option
func WithMCP23017Haen(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Haen = val
} else {
panic("Trying to set Haen for non-MCP23017Driver")
}
}
}
// WithMCP23017Odr option sets the MCP23017Driver Odr option
func WithMCP23017Odr(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Odr = val
} else {
panic("Trying to set Odr for non-MCP23017Driver")
}
}
}
// WithMCP23017Intpol option sets the MCP23017Driver Intpol option
func WithMCP23017Intpol(val uint8) func(Config) {
return func(c Config) {
d, ok := c.(*MCP23017Driver)
if ok {
d.MCPConf.Intpol = val
} else {
panic("Trying to set Intpol for non-MCP23017Driver")
}
}
}
// NewMCP23017Driver creates a new Gobot Driver to the MCP23017 i2c port expander.
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional params:
// i2c.WithBus(int): bus to use with this driver
// i2c.WithAddress(int): address to use with this driver
// i2c.WithMCP23017Bank(int): MCP23017 bank to use with this driver
// i2c.WithMCP23017Mirror(int): MCP23017 mirror to use with this driver
// i2c.WithMCP23017Seqop(int): MCP23017 seqop to use with this driver
// i2c.WithMCP23017Disslw(int): MCP23017 disslw to use with this driver
// i2c.WithMCP23017Haen(int): MCP23017 haen to use with this driver
// i2c.WithMCP23017Odr(int): MCP23017 odr to use with this driver
// i2c.WithMCP23017Intpol(int): MCP23017 intpol to use with this driver
//
func NewMCP23017Driver(a Connector, options ...func(Config)) *MCP23017Driver {
m := &MCP23017Driver{
name: gobot.DefaultName("MCP23017"),
connector: a,
Config: NewConfig(),
MCPConf: MCP23017Config{},
Commander: gobot.NewCommander(),
Eventer: gobot.NewEventer(),
}
for _, option := range options {
option(m)
}
m.AddCommand("WriteGPIO", func(params map[string]interface{}) interface{} {
2016-03-07 23:51:45 -07:00
pin := params["pin"].(uint8)
val := params["val"].(uint8)
port := params["port"].(string)
2016-03-07 23:51:45 -07:00
err := m.WriteGPIO(pin, val, port)
return map[string]interface{}{"err": err}
})
m.AddCommand("ReadGPIO", func(params map[string]interface{}) interface{} {
2016-03-07 23:51:45 -07:00
pin := params["pin"].(uint8)
port := params["port"].(string)
val, err := m.ReadGPIO(pin, port)
return map[string]interface{}{"val": val, "err": err}
})
return m
}
2016-03-07 23:51:45 -07:00
// Name return the driver name.
func (m *MCP23017Driver) Name() string { return m.name }
// SetName set the driver name.
func (m *MCP23017Driver) SetName(n string) { m.name = n }
2016-03-07 23:51:45 -07:00
// Connection returns the I2c connection.
func (m *MCP23017Driver) Connection() gobot.Connection { return m.connector.(gobot.Connection) }
2016-03-07 23:51:45 -07:00
// Halt stops the driver.
func (m *MCP23017Driver) Halt() (err error) { return }
2016-03-07 23:51:45 -07:00
// Start writes the device configuration.
func (m *MCP23017Driver) Start() (err error) {
bus := m.GetBusOrDefault(m.connector.GetDefaultBus())
address := m.GetAddressOrDefault(mcp23017Address)
m.connection, err = m.connector.GetConnection(address, bus)
if err != nil {
return err
}
2016-03-07 23:51:45 -07:00
// Set IOCON register with MCP23017 configuration.
ioconReg := m.getPort("A").IOCON // IOCON address is the same for Port A or B.
ioconVal := m.MCPConf.getUint8Value()
if _, err := m.connection.Write([]uint8{ioconReg, ioconVal}); err != nil {
return err
}
return
}
2016-03-07 23:51:45 -07:00
// WriteGPIO writes a value to a gpio pin (0-7) and a port (A or B).
func (m *MCP23017Driver) WriteGPIO(pin uint8, val uint8, portStr string) (err error) {
selectedPort := m.getPort(portStr)
// read current value of IODIR register
iodir, err := m.read(selectedPort.IODIR)
if err != nil {
return err
}
2018-06-30 15:09:33 -06:00
// set pin as output by clearing bit
iodirVal := clearBit(iodir, uint8(pin))
// write IODIR register bit
err = m.write(selectedPort.IODIR, uint8(pin), uint8(iodirVal))
if err != nil {
return err
}
// read current value of OLAT register
olat, err := m.read(selectedPort.OLAT)
if err != nil {
return err
}
// set or clear olat value, 0 is no output, 1 is an output
var olatVal uint8
if val == 0 {
olatVal = clearBit(olat, uint8(pin))
} else {
olatVal = setBit(olat, uint8(pin))
}
// write OLAT register bit
err = m.write(selectedPort.OLAT, uint8(pin), uint8(olatVal))
if err != nil {
return err
}
return nil
}
// ReadGPIO reads a value from a given gpio pin (0-7) and a
// port (A or B).
2016-03-07 23:51:45 -07:00
func (m *MCP23017Driver) ReadGPIO(pin uint8, portStr string) (val uint8, err error) {
selectedPort := m.getPort(portStr)
2018-06-30 15:09:33 -06:00
// read current value of IODIR register
iodir, err := m.read(selectedPort.IODIR)
if err != nil {
return 0, err
}
// set pin as input by setting bit
iodirVal := setBit(iodir, uint8(pin))
// write IODIR register bit
err = m.write(selectedPort.IODIR, uint8(pin), uint8(iodirVal))
if err != nil {
return 0, err
}
2016-03-07 23:51:45 -07:00
val, err = m.read(selectedPort.GPIO)
if err != nil {
2016-03-07 23:51:45 -07:00
return val, err
}
val = 1 << uint8(pin) & val
if val > 1 {
val = 1
}
return val, nil
}
// SetPullUp sets the pull up state of a given pin based on the value:
// val = 1 pull up enabled.
// val = 0 pull up disabled.
2016-03-07 23:51:45 -07:00
func (m *MCP23017Driver) SetPullUp(pin uint8, val uint8, portStr string) error {
selectedPort := m.getPort(portStr)
2016-07-13 09:32:22 -06:00
return m.write(selectedPort.GPPU, pin, val)
}
// SetGPIOPolarity will change a given pin's polarity based on the value:
// val = 1 opposite logic state of the input pin.
// val = 0 same logic state of the input pin.
2016-03-07 23:51:45 -07:00
func (m *MCP23017Driver) SetGPIOPolarity(pin uint8, val uint8, portStr string) (err error) {
selectedPort := m.getPort(portStr)
2016-07-13 09:32:22 -06:00
return m.write(selectedPort.IPOL, pin, val)
}
// write gets the value of the passed in register, and then overwrites
// the bit specified by the pin, with the given value.
2016-03-07 23:51:45 -07:00
func (m *MCP23017Driver) write(reg uint8, pin uint8, val uint8) (err error) {
if debug {
log.Printf("write: MCP address: 0x%X, register:0x%X,value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, val)
2016-03-07 23:51:45 -07:00
}
if _, err = m.connection.Write([]uint8{reg, val}); err != nil {
return err
}
return nil
}
// read get the data from a given register
2016-03-07 23:51:45 -07:00
func (m *MCP23017Driver) read(reg uint8) (val uint8, err error) {
buf := []byte{0}
if _, err := m.connection.Write([]uint8{reg}); err != nil {
return val, err
}
bytesRead, err := m.connection.Read(buf)
if err != nil {
return val, err
}
if bytesRead != 1 {
err = ErrNotEnoughBytes
return
2016-03-07 23:51:45 -07:00
}
if debug {
log.Printf("reading: MCP address: 0x%X, register:0x%X,value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, buf)
}
return buf[0], nil
}
// getPort return the port (A or B) given a string and the bank.
// Port A is the default if an incorrect or no port is specified.
func (m *MCP23017Driver) getPort(portStr string) (selectedPort port) {
portStr = strings.ToUpper(portStr)
switch {
case portStr == "A":
return getBank(m.MCPConf.Bank).PortA
case portStr == "B":
return getBank(m.MCPConf.Bank).PortB
default:
return getBank(m.MCPConf.Bank).PortA
}
}
// getUint8Value returns the configuration data as a packed value.
func (mc *MCP23017Config) getUint8Value() uint8 {
return mc.Bank<<7 | mc.Mirror<<6 | mc.Seqop<<5 | mc.Disslw<<4 | mc.Haen<<3 | mc.Odr<<2 | mc.Intpol<<1
}
// setBit is used to set a bit at a given position to 1.
func setBit(n uint8, pos uint8) uint8 {
n |= (1 << pos)
return n
}
// clearBit is used to set a bit at a given position to 0.
func clearBit(n uint8, pos uint8) uint8 {
mask := ^uint8(1 << pos)
n &= mask
return n
}
// getBank returns a bank's PortA and PortB registers given a bank number (0/1).
func getBank(bnk uint8) bank {
if bnk == 0 {
return bank{PortA: port{0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14}, PortB: port{0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x11, 0x13, 0x15}}
}
return bank{PortA: port{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, PortB: port{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A}}
}