Skip to content

Commit

Permalink
fixed bug in read/write gpio code, and cleaned up some comments
Browse files Browse the repository at this point in the history
  • Loading branch information
ulisesflynn committed Jun 21, 2018
1 parent 1cc5b55 commit 49f7c03
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 246 deletions.
313 changes: 154 additions & 159 deletions drivers/i2c/mcp23017_driver.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
/*
Copyright (c) 2015 Ulises Flynn
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package i2c

import (
"fmt"
"log"
"strings"

"gobot.io/x/gobot"
)

const mcp23017Address = 0x20
const (
// default address for device when a2/a1/a0 pins are all tied to ground
mcp23017Address = 0x20
)

var debug = false // Set this to true to see debugging information
var (
debug = false // toggle debugging information
)

// Port contains all the registers for the device.
// port contains all the registers for the device.
type port struct {
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
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
}

// Registers in the MCP23017 have different address based on which bank is used.
// Each bank is made up of PortA and PortB registers.
// 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
Expand Down Expand Up @@ -73,6 +61,90 @@ type MCP23017Driver struct {
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
Expand Down Expand Up @@ -143,7 +215,7 @@ func (m *MCP23017Driver) Start() (err error) {
}
// 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()
ioconVal := m.MCPConf.getUint8Value()
if _, err := m.connection.Write([]uint8{ioconReg, ioconVal}); err != nil {
return err
}
Expand All @@ -153,27 +225,41 @@ func (m *MCP23017Driver) Start() (err error) {
// 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)
// Set IODIR register bit for given pin to an output.
if err := m.write(selectedPort.IODIR, uint8(pin), 0); err != nil {
// read current value of IODIR register
iodir, err := m.read(selectedPort.IODIR)
if err != nil {
return err
}
// Set OLAT register to write a value to the given pin.
if err := m.write(selectedPort.OLAT, uint8(pin), uint8(val)); err != nil {
// set or clear iodir value, a 1 sets the pin as an input, 0 as an output
var iodirVal uint8
if val == 1 {
iodirVal = clearBit(iodir, uint8(pin))
} else {
iodirVal = setBit(iodir, uint8(pin))
}
// write IODIR register bit
err = m.write(selectedPort.IODIR, uint8(pin), uint8(iodirVal))
if err != nil {
return err
}
return nil
}

// PinMode set pin mode
// val (0 output 1 input)
// port (A or B).
func (m *MCP23017Driver) PinMode(pin, val uint8, portStr string) (err error) {
selectedPort := m.getPort(portStr)
// Set IODIR register bit for given pin to an output/input.
if err = m.write(selectedPort.IODIR, uint8(pin), val); err != nil {
return
// read current value of OLAT register
olat, err := m.read(selectedPort.OLAT)
if err != nil {
return err
}
return
// 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
Expand All @@ -184,7 +270,11 @@ func (m *MCP23017Driver) ReadGPIO(pin uint8, portStr string) (val uint8, err err
if err != nil {
return val, err
}
return (1 << uint8(pin) & val), nil
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:
Expand All @@ -203,131 +293,36 @@ func (m *MCP23017Driver) SetGPIOPolarity(pin uint8, val uint8, portStr string) (
return m.write(selectedPort.IPOL, pin, val)
}

// 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")
}
}
}

// write gets the value of the passed in register, and then overwrites
// the bit specified by the pin, with the given value.
func (m *MCP23017Driver) write(reg uint8, pin uint8, val uint8) (err error) {
var ioval uint8
iodir, err := m.read(reg)
if err != nil {
return err
}
if val == 0 {
ioval = clearBit(iodir, uint8(pin))
} else if val == 1 {
ioval = setBit(iodir, uint8(pin))
}
if debug {
log.Printf("Writing: MCP address: 0x%X, register: 0x%X\t, value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, ioval)
log.Printf("write: MCP address: 0x%X, register:0x%X,value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, val)
}
if _, err = m.connection.Write([]uint8{reg, ioval}); err != nil {
if _, err = m.connection.Write([]uint8{reg, val}); err != nil {
return err
}
return nil
}

// read get the data from a given register. The I2cRead does not read a specific
// register from the device, rather it will read n bytes starting at the base
// device address. To read a specific register, read register + 1 bytes, and then index
// the result with the given register to get the value.
// read get the data from a given register
func (m *MCP23017Driver) read(reg uint8) (val uint8, err error) {
register := int(reg)
bytesToRead := register + 1
buf := make([]byte, bytesToRead)
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 != bytesToRead {
return val, fmt.Errorf("Read was unable to get %d bytes for register: 0x%X\n", bytesToRead, reg)
if bytesRead != 1 {
err = ErrNotEnoughBytes
return
}
if debug {
log.Printf("Reading: MCP address: 0x%X, register:0x%X\t,value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, buf[register])
log.Printf("reading: MCP address: 0x%X, register:0x%X,value: 0x%X\n", m.GetAddressOrDefault(mcp23017Address), reg, buf)
}
return buf[register], nil
return buf[0], nil
}

// getPort return the port (A or B) given a string and the bank.
Expand All @@ -344,8 +339,8 @@ func (m *MCP23017Driver) getPort(portStr string) (selectedPort port) {
}
}

// GetUint8Value returns the configuration data as a packed value.
func (mc *MCP23017Config) GetUint8Value() uint8 {
// 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
}

Expand Down
Loading

0 comments on commit 49f7c03

Please sign in to comment.