diff --git a/Makefile b/Makefile index e0d02b6..50efd99 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,8 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go + @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go diff --git a/examples/mcp23017-multiple/main.go b/examples/mcp23017-multiple/main.go new file mode 100644 index 0000000..14884c8 --- /dev/null +++ b/examples/mcp23017-multiple/main.go @@ -0,0 +1,45 @@ +// This example demonstrates putting several mcp23017 devices together into +// a single virtual I/O array. +package main + +import ( + "machine" + + "tinygo.org/x/drivers/mcp23017" +) + +func main() { + err := machine.I2C0.Configure(machine.I2CConfig{ + Frequency: machine.TWI_FREQ_400KHZ, + }) + if err != nil { + panic(err) + } + // Assume the devices are at addresses 0x20, 0x21 + dev, err := mcp23017.NewI2CDevices(machine.I2C0, 0x20, 0x21) + if err != nil { + panic(err) + } + // Configure pin 0 for input and all the others for output. + if err := dev.SetModes([]mcp23017.PinMode{ + mcp23017.Input | mcp23017.Pullup, + mcp23017.Output, + }); err != nil { + panic(err) + } + input := dev.Pin(0) + // Make a mask that represents all the output pins. + // Note that this leverages the driver behaviour which replicates the highest bit in + // the last slice element (1 in this case) to all other pins + outputMask := mcp23017.PinSlice{^mcp23017.Pins(1 << 0)} // All except pin 0 + inputVal, err := input.Get() + if err != nil { + panic(err) + } + println("input value: ", inputVal) + // Set the values of all the output pins. + err = dev.SetPins(mcp23017.PinSlice{0b1011011_01101110, 0b11111101_11100110}, outputMask) + if err != nil { + panic(err) + } +} diff --git a/mcp23017/device.go b/mcp23017/device.go index 341a043..fb2cbad 100644 --- a/mcp23017/device.go +++ b/mcp23017/device.go @@ -154,6 +154,15 @@ func (d *Device) SetPins(pins, mask Pins) error { return nil } +// TogglePins inverts the values on all pins for +// which mask is high. +func (d *Device) TogglePins(mask Pins) error { + if mask == 0 { + return nil + } + return d.SetPins(^d.pins, mask) +} + // Pin returns a Pin representing the given pin number (from 0 to 15). // Pin numbers from 0 to 7 represent port A pins 0 to 7. // Pin numbers from 8 to 15 represent port B pins 0 to 7. @@ -295,6 +304,11 @@ func (p Pin) Low() error { return p.Set(false) } +// Toggle inverts the value output on the pin. +func (p Pin) Toggle() error { + return p.dev.TogglePins(p.mask) +} + // Get returns the current value of the given pin. func (p Pin) Get() (bool, error) { // TODO this reads 2 registers when we could read just one. @@ -357,6 +371,11 @@ func (p *Pins) Low(pin int) { *p &^= pinMask(pin) } +// Toggle inverts the value of the given pin. +func (p *Pins) Toggle(pin int) { + *p ^= pinMask(pin) +} + func pinMask(pin int) Pins { return 1 << pin } diff --git a/mcp23017/device_test.go b/mcp23017/device_test.go index 65f0057..9134e26 100644 --- a/mcp23017/device_test.go +++ b/mcp23017/device_test.go @@ -50,6 +50,25 @@ func TestSetPins(t *testing.T) { c.Assert(pins, qt.Equals, Pins(0b01010000_00011010)) } +func TestTogglePins(t *testing.T) { + c := qt.New(t) + bus := newBus(c) + fdev := bus.addDevice(0x20) + fdev.Registers[rGPIO] = 0b00001111 + fdev.Registers[rGPIO|portB] = 0b11110000 + dev, err := NewI2C(bus, 0x20) + c.Assert(err, qt.IsNil) + pins, err := dev.GetPins() + c.Assert(err, qt.IsNil) + c.Assert(pins, qt.Equals, Pins(0b11110000_00001111)) + + err = dev.TogglePins(0b10101010_01010101) + c.Assert(err, qt.IsNil) + pins, err = dev.GetPins() + c.Assert(err, qt.IsNil) + c.Assert(pins, qt.Equals, Pins(0b01011010_01011010)) +} + func TestSetGetModes(t *testing.T) { c := qt.New(t) bus := newBus(c) @@ -108,6 +127,24 @@ func TestPinSetGet(t *testing.T) { c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0)) } +func TestPinToggle(t *testing.T) { + c := qt.New(t) + bus := newBus(c) + fdev := bus.addDevice(0x20) + dev, err := NewI2C(bus, 0x20) + c.Assert(err, qt.IsNil) + pin := dev.Pin(1) + v, err := pin.Get() + c.Assert(err, qt.Equals, nil) + c.Assert(v, qt.Equals, false) + err = pin.Toggle() + c.Assert(err, qt.Equals, nil) + c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0b10)) + err = pin.Toggle() + c.Assert(err, qt.Equals, nil) + c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0)) +} + func TestPinMode(t *testing.T) { c := qt.New(t) bus := newBus(c) diff --git a/mcp23017/multidevice.go b/mcp23017/multidevice.go index da2c730..34db93e 100644 --- a/mcp23017/multidevice.go +++ b/mcp23017/multidevice.go @@ -117,6 +117,22 @@ func (devs Devices) SetPins(pins, mask PinSlice) error { return nil } +// TogglePins inverts the values on all pins for +// which mask is high. +func (devs Devices) TogglePins(mask PinSlice) error { + defaultMask := mask.extra() + for i, dev := range devs { + devMask := defaultMask + if i < len(mask) { + devMask = mask[i] + } + if err := dev.TogglePins(devMask); err != nil { + return err + } + } + return nil +} + // PinSlice represents an arbitrary nunber of pins, each element corresponding // to the pins for one device. The value of the highest numbered pin in the // slice is extended to all other pins beyond the end of the slice. @@ -154,6 +170,11 @@ func (pins PinSlice) Low(pin int) { pins[pin/PinCount].Low(pin % PinCount) } +// Toggle inverts the value of the given pin. +func (pins PinSlice) Toggle(pin int) { + pins[pin/PinCount].Toggle(pin % PinCount) +} + // Ensure checks that pins has enough space to store // at least length pins. If it does, it returns pins unchanged. // Otherwise, it returns pins with elements appended as needed, diff --git a/mcp23017/multidevice_test.go b/mcp23017/multidevice_test.go index ebe06b9..b36a3d2 100644 --- a/mcp23017/multidevice_test.go +++ b/mcp23017/multidevice_test.go @@ -102,6 +102,26 @@ func TestDevicesSetPinsMask(t *testing.T) { c.Assert(pins, qt.DeepEquals, PinSlice{0b01010011_10101101, 0b01010010_10101100}) } +func TestDevicesTogglePins(t *testing.T) { + c := qt.New(t) + bus := newBus(c) + bus.addDevice(0x20) + bus.addDevice(0x21) + devs, err := NewI2CDevices(bus, 0x20, 0x21) + c.Assert(err, qt.IsNil) + + mask := make(PinSlice, 2) + mask.High(0) + mask.High(16) + + err = devs.TogglePins(mask) + c.Assert(err, qt.IsNil) + pins := make(PinSlice, 2) + err = devs.GetPins(pins) + c.Assert(err, qt.IsNil) + c.Assert(pins, qt.DeepEquals, PinSlice{0b00000000_00000001, 0b00000000_00000001}) +} + func TestDevicesSetGetModes(t *testing.T) { c := qt.New(t) bus := newBus(c) @@ -163,4 +183,6 @@ func TestPinSlice(t *testing.T) { c.Assert(pins.Get(16), qt.Equals, false) pins.High(16) c.Assert(pins.Get(16), qt.Equals, true) + pins.Toggle(16) + c.Assert(pins.Get(16), qt.Equals, false) }