Skip to content

Commit

Permalink
Change to make I2C support more than one bus, and use
Browse files Browse the repository at this point in the history
other access methods than block operations.

Signed-off-by: Erik Agsjö <[email protected]>

Added ReadWordData to i2c connection

Signed-off-by: Erik Agsjö <[email protected]>

Code cleanup
  • Loading branch information
erkkah committed Feb 6, 2017
1 parent 3daa17d commit f41021c
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 54 deletions.
53 changes: 53 additions & 0 deletions drivers/i2c/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,74 @@ var blue = castColor("blue")
type i2cTestAdaptor struct {
name string
written []byte
pos int
i2cReadImpl func() ([]byte, error)
i2cWriteImpl func() error
i2cStartImpl func() error
}

func (t *i2cTestAdaptor) I2cStart(int) (err error) {
t.pos = 0
return t.i2cStartImpl()
}

func (t *i2cTestAdaptor) I2cRead(int, int) (data []byte, err error) {
return t.i2cReadImpl()
}

func (t *i2cTestAdaptor) I2cWrite(address int, b []byte) (err error) {
t.written = append(t.written, b...)
return t.i2cWriteImpl()
}

func (t *i2cTestAdaptor) ReadByte() (val uint8, err error) {
bytes, err := t.i2cReadImpl()
val = bytes[t.pos]
t.pos++
return
}

func (t *i2cTestAdaptor) ReadByteData(reg uint8) (val uint8, err error) {
bytes, err := t.i2cReadImpl()
return bytes[reg], err
}

func (t *i2cTestAdaptor) ReadWordData(reg uint8) (val uint16, err error) {
bytes, err := t.i2cReadImpl()
low, high := bytes[reg], bytes[reg + 1]
return (uint16(high) << 8) | uint16(low), err
}

func (t *i2cTestAdaptor) ReadBlockData(b []byte) (n int, err error) {
reg := b[0]
bytes, err := t.i2cReadImpl()
copy(b, bytes[reg:])
return len(b), err
}

func (t *i2cTestAdaptor) WriteByte(val uint8) (err error) {
t.pos = int(val)
t.written = append(t.written, val)
return t.i2cWriteImpl()
}

func (t *i2cTestAdaptor) WriteByteData(reg uint8, val uint8) (err error) {
t.pos = int(reg)
t.written = append(t.written, reg)
t.written = append(t.written, val)
return t.i2cWriteImpl()
}

func (t *i2cTestAdaptor) WriteBlockData(b []byte) (err error) {
t.pos = int(b[0])
t.written = append(t.written, b...)
return t.i2cWriteImpl()
}

func (t *i2cTestAdaptor) I2cGetConnection( /* address */ int /* bus */, int) (connection I2cConnection, err error) {
return t, nil
}

func (t *i2cTestAdaptor) Name() string { return t.name }
func (t *i2cTestAdaptor) SetName(n string) { t.name = n }
func (t *i2cTestAdaptor) Connect() (err error) { return }
Expand Down
86 changes: 86 additions & 0 deletions drivers/i2c/i2c.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"

"gobot.io/x/gobot"
"gobot.io/x/gobot/sysfs"
)

var (
Expand All @@ -21,14 +22,27 @@ const (
)

type I2cStarter interface {
// I2cStart initializes I2C communication to a device at a specified address
// on the default bus for further access using I2cRead() and I2cWrite().
I2cStart(address int) (err error)
}

type I2cReader interface {
// I2cStart reads len bytes from a device at the specified address using
// block reads.
//
// Note: There is no way to specify command/register to read from using
// this interface, it always starts reading from register 0.
// To read register 42 you have to read 43 bytes (0 to 42)
// and throw away the first 42 bytes.
I2cRead(address int, len int) (data []byte, err error)
}

type I2cWriter interface {
// I2cWrite writes a buffer of bytes to the device at the specified address
// using block writes.
// Note that the first byte in the buffer is interpreted as the target
// command/register.
I2cWrite(address int, buf []byte) (err error)
}

Expand All @@ -38,3 +52,75 @@ type I2c interface {
I2cReader
I2cWriter
}

// I2cConnection is a connection to an I2C device with a specified address
// on a specific bus. Used as an alternative to the I2c interface.
// Implements sysfs.SMBusOperations to talk to the device, wrapping the
// calls in SetAddress to always target the specified device.
// Provided by an Adaptor by implementing the I2cConnector interface.
type I2cConnection sysfs.SMBusOperations

type i2cConnection struct {
bus sysfs.I2cDevice
address int
}

func NewI2cConnection(bus sysfs.I2cDevice, address int) (connection *i2cConnection) {
return &i2cConnection{bus: bus, address: address}
}

func (c *i2cConnection) ReadByte() (val uint8, err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return 0, err
}
return c.bus.ReadByte()
}

func (c *i2cConnection) ReadByteData(reg uint8) (val uint8, err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return 0, err
}
return c.bus.ReadByteData(reg)
}

func (c *i2cConnection) ReadWordData(reg uint8) (val uint16, err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return 0, err
}
return c.bus.ReadWordData(reg)
}

func (c *i2cConnection) ReadBlockData(b []byte) (n int, err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return 0, err
}
return c.bus.ReadBlockData(b)
}

func (c *i2cConnection) WriteByte(val uint8) (err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return err
}
return c.bus.WriteByte(val)
}

func (c *i2cConnection) WriteByteData(reg uint8, val uint8) (err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return err
}
return c.bus.WriteByteData(reg, val)
}

func (c *i2cConnection) WriteBlockData(b []byte) (err error) {
if err := c.bus.SetAddress(c.address); err != nil {
return err
}
return c.bus.WriteBlockData(b)
}

// I2cConnector is a replacement to the I2c interface, providing
// access to more than one I2C bus per adaptor, and a more
// fine grained interface to the I2C bus.
type I2cConnector interface {
I2cGetConnection(address int, bus int) (device I2cConnection, err error)
}
35 changes: 25 additions & 10 deletions platforms/chip/chip_adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

multierror "github.com/hashicorp/go-multierror"
"gobot.io/x/gobot"
"gobot.io/x/gobot/drivers/i2c"
"gobot.io/x/gobot/sysfs"
)

Expand All @@ -18,7 +19,7 @@ type Adaptor struct {
name string
digitalPins map[int]sysfs.DigitalPin
pinMap map[string]int
i2cDevice sysfs.I2cDevice
i2cBuses [3]sysfs.I2cDevice
}

var fixedPins = map[string]int{
Expand Down Expand Up @@ -103,9 +104,11 @@ func (c *Adaptor) Finalize() (err error) {
}
}
}
if c.i2cDevice != nil {
if e := c.i2cDevice.Close(); e != nil {
err = multierror.Append(err, e)
for _, bus := range c.i2cBuses {
if bus != nil {
if e := bus.Close(); e != nil {
err = multierror.Append(err, e)
}
}
}
return
Expand Down Expand Up @@ -175,31 +178,43 @@ func (c *Adaptor) DigitalWrite(pin string, val byte) (err error) {
// This assumes that the bus used is /dev/i2c-1, which corresponds to
// pins labeled TWI1-SDA and TW1-SCK (pins 9 and 11 on header 13).
func (c *Adaptor) I2cStart(address int) (err error) {
if c.i2cDevice == nil {
c.i2cDevice, err = sysfs.NewI2cDevice("/dev/i2c-1", address)
if c.i2cBuses[1] == nil {
c.i2cBuses[1], err = sysfs.NewI2cDevice("/dev/i2c-1", address)
}
return err
}

// I2cWrite writes data to i2c device
func (c *Adaptor) I2cWrite(address int, data []byte) (err error) {
if err = c.i2cDevice.SetAddress(address); err != nil {
if err = c.i2cBuses[1].SetAddress(address); err != nil {
return
}
_, err = c.i2cDevice.Write(data)
_, err = c.i2cBuses[1].Write(data)
return
}

// I2cRead returns value from i2c device using specified size
func (c *Adaptor) I2cRead(address int, size int) (data []byte, err error) {
if err = c.i2cDevice.SetAddress(address); err != nil {
if err = c.i2cBuses[1].SetAddress(address); err != nil {
return
}
data = make([]byte, size)
_, err = c.i2cDevice.Read(data)
_, err = c.i2cBuses[1].Read(data)
return
}

// I2cGetConnection returns a connection to a device on a specified bus.
// Valid bus number is [0..2] which corresponds to /dev/i2c-0 through /dev/i2c-2.
func (c *Adaptor) I2cGetConnection(address int, bus int) (connection i2c.I2cConnection, err error) {
if (bus < 0) || (bus > 2) {
return nil, fmt.Errorf("Bus number %d out of range", bus)
}
if c.i2cBuses[bus] == nil {
c.i2cBuses[bus], err = sysfs.NewI2cDevice(fmt.Sprintf("/dev/i2c-%d", bus))
}
return i2c.NewI2cConnection(c.i2cBuses[bus], address), err
}

func getXIOBase() (baseAddr int, err error) {
// Default to original base from 4.3 kernel
baseAddr = 408
Expand Down
2 changes: 0 additions & 2 deletions platforms/chip/chip_adaptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ func TestChipAdaptorI2c(t *testing.T) {
sysfs.SetFilesystem(fs)
sysfs.SetSyscall(&sysfs.MockSyscall{})
a.I2cStart(0xff)
a.i2cDevice = &NullReadWriteCloser{}

a.I2cWrite(0xff, []byte{0x00, 0x01})
data, _ := a.I2cRead(0xff, 2)
gobottest.Assert(t, data, []byte{0x00, 0x01})
Expand Down
Loading

0 comments on commit f41021c

Please sign in to comment.