Skip to content

Commit ce5e443

Browse files
rogpeppedeadprogram
authored andcommitted
mcp23017: implement pin toggling
Also add an example for using multiple devices.
1 parent edf9ba9 commit ce5e443

File tree

6 files changed

+146
-0
lines changed

6 files changed

+146
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ smoke-test:
8585
@md5sum ./build/test.hex
8686
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017/main.go
8787
@md5sum ./build/test.hex
88+
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp23017-multiple/main.go
89+
@md5sum ./build/test.hex
8890
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
8991
@md5sum ./build/test.hex
9092
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go

examples/mcp23017-multiple/main.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// This example demonstrates putting several mcp23017 devices together into
2+
// a single virtual I/O array.
3+
package main
4+
5+
import (
6+
"machine"
7+
8+
"tinygo.org/x/drivers/mcp23017"
9+
)
10+
11+
func main() {
12+
err := machine.I2C0.Configure(machine.I2CConfig{
13+
Frequency: machine.TWI_FREQ_400KHZ,
14+
})
15+
if err != nil {
16+
panic(err)
17+
}
18+
// Assume the devices are at addresses 0x20, 0x21
19+
dev, err := mcp23017.NewI2CDevices(machine.I2C0, 0x20, 0x21)
20+
if err != nil {
21+
panic(err)
22+
}
23+
// Configure pin 0 for input and all the others for output.
24+
if err := dev.SetModes([]mcp23017.PinMode{
25+
mcp23017.Input | mcp23017.Pullup,
26+
mcp23017.Output,
27+
}); err != nil {
28+
panic(err)
29+
}
30+
input := dev.Pin(0)
31+
// Make a mask that represents all the output pins.
32+
// Note that this leverages the driver behaviour which replicates the highest bit in
33+
// the last slice element (1 in this case) to all other pins
34+
outputMask := mcp23017.PinSlice{^mcp23017.Pins(1 << 0)} // All except pin 0
35+
inputVal, err := input.Get()
36+
if err != nil {
37+
panic(err)
38+
}
39+
println("input value: ", inputVal)
40+
// Set the values of all the output pins.
41+
err = dev.SetPins(mcp23017.PinSlice{0b1011011_01101110, 0b11111101_11100110}, outputMask)
42+
if err != nil {
43+
panic(err)
44+
}
45+
}

mcp23017/device.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,15 @@ func (d *Device) SetPins(pins, mask Pins) error {
154154
return nil
155155
}
156156

157+
// TogglePins inverts the values on all pins for
158+
// which mask is high.
159+
func (d *Device) TogglePins(mask Pins) error {
160+
if mask == 0 {
161+
return nil
162+
}
163+
return d.SetPins(^d.pins, mask)
164+
}
165+
157166
// Pin returns a Pin representing the given pin number (from 0 to 15).
158167
// Pin numbers from 0 to 7 represent port A pins 0 to 7.
159168
// Pin numbers from 8 to 15 represent port B pins 0 to 7.
@@ -295,6 +304,11 @@ func (p Pin) Low() error {
295304
return p.Set(false)
296305
}
297306

307+
// Toggle inverts the value output on the pin.
308+
func (p Pin) Toggle() error {
309+
return p.dev.TogglePins(p.mask)
310+
}
311+
298312
// Get returns the current value of the given pin.
299313
func (p Pin) Get() (bool, error) {
300314
// TODO this reads 2 registers when we could read just one.
@@ -357,6 +371,11 @@ func (p *Pins) Low(pin int) {
357371
*p &^= pinMask(pin)
358372
}
359373

374+
// Toggle inverts the value of the given pin.
375+
func (p *Pins) Toggle(pin int) {
376+
*p ^= pinMask(pin)
377+
}
378+
360379
func pinMask(pin int) Pins {
361380
return 1 << pin
362381
}

mcp23017/device_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ func TestSetPins(t *testing.T) {
5050
c.Assert(pins, qt.Equals, Pins(0b01010000_00011010))
5151
}
5252

53+
func TestTogglePins(t *testing.T) {
54+
c := qt.New(t)
55+
bus := newBus(c)
56+
fdev := bus.addDevice(0x20)
57+
fdev.Registers[rGPIO] = 0b00001111
58+
fdev.Registers[rGPIO|portB] = 0b11110000
59+
dev, err := NewI2C(bus, 0x20)
60+
c.Assert(err, qt.IsNil)
61+
pins, err := dev.GetPins()
62+
c.Assert(err, qt.IsNil)
63+
c.Assert(pins, qt.Equals, Pins(0b11110000_00001111))
64+
65+
err = dev.TogglePins(0b10101010_01010101)
66+
c.Assert(err, qt.IsNil)
67+
pins, err = dev.GetPins()
68+
c.Assert(err, qt.IsNil)
69+
c.Assert(pins, qt.Equals, Pins(0b01011010_01011010))
70+
}
71+
5372
func TestSetGetModes(t *testing.T) {
5473
c := qt.New(t)
5574
bus := newBus(c)
@@ -108,6 +127,24 @@ func TestPinSetGet(t *testing.T) {
108127
c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0))
109128
}
110129

130+
func TestPinToggle(t *testing.T) {
131+
c := qt.New(t)
132+
bus := newBus(c)
133+
fdev := bus.addDevice(0x20)
134+
dev, err := NewI2C(bus, 0x20)
135+
c.Assert(err, qt.IsNil)
136+
pin := dev.Pin(1)
137+
v, err := pin.Get()
138+
c.Assert(err, qt.Equals, nil)
139+
c.Assert(v, qt.Equals, false)
140+
err = pin.Toggle()
141+
c.Assert(err, qt.Equals, nil)
142+
c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0b10))
143+
err = pin.Toggle()
144+
c.Assert(err, qt.Equals, nil)
145+
c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0))
146+
}
147+
111148
func TestPinMode(t *testing.T) {
112149
c := qt.New(t)
113150
bus := newBus(c)

mcp23017/multidevice.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,22 @@ func (devs Devices) SetPins(pins, mask PinSlice) error {
117117
return nil
118118
}
119119

120+
// TogglePins inverts the values on all pins for
121+
// which mask is high.
122+
func (devs Devices) TogglePins(mask PinSlice) error {
123+
defaultMask := mask.extra()
124+
for i, dev := range devs {
125+
devMask := defaultMask
126+
if i < len(mask) {
127+
devMask = mask[i]
128+
}
129+
if err := dev.TogglePins(devMask); err != nil {
130+
return err
131+
}
132+
}
133+
return nil
134+
}
135+
120136
// PinSlice represents an arbitrary nunber of pins, each element corresponding
121137
// to the pins for one device. The value of the highest numbered pin in the
122138
// slice is extended to all other pins beyond the end of the slice.
@@ -154,6 +170,11 @@ func (pins PinSlice) Low(pin int) {
154170
pins[pin/PinCount].Low(pin % PinCount)
155171
}
156172

173+
// Toggle inverts the value of the given pin.
174+
func (pins PinSlice) Toggle(pin int) {
175+
pins[pin/PinCount].Toggle(pin % PinCount)
176+
}
177+
157178
// Ensure checks that pins has enough space to store
158179
// at least length pins. If it does, it returns pins unchanged.
159180
// Otherwise, it returns pins with elements appended as needed,

mcp23017/multidevice_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@ func TestDevicesSetPinsMask(t *testing.T) {
102102
c.Assert(pins, qt.DeepEquals, PinSlice{0b01010011_10101101, 0b01010010_10101100})
103103
}
104104

105+
func TestDevicesTogglePins(t *testing.T) {
106+
c := qt.New(t)
107+
bus := newBus(c)
108+
bus.addDevice(0x20)
109+
bus.addDevice(0x21)
110+
devs, err := NewI2CDevices(bus, 0x20, 0x21)
111+
c.Assert(err, qt.IsNil)
112+
113+
mask := make(PinSlice, 2)
114+
mask.High(0)
115+
mask.High(16)
116+
117+
err = devs.TogglePins(mask)
118+
c.Assert(err, qt.IsNil)
119+
pins := make(PinSlice, 2)
120+
err = devs.GetPins(pins)
121+
c.Assert(err, qt.IsNil)
122+
c.Assert(pins, qt.DeepEquals, PinSlice{0b00000000_00000001, 0b00000000_00000001})
123+
}
124+
105125
func TestDevicesSetGetModes(t *testing.T) {
106126
c := qt.New(t)
107127
bus := newBus(c)
@@ -163,4 +183,6 @@ func TestPinSlice(t *testing.T) {
163183
c.Assert(pins.Get(16), qt.Equals, false)
164184
pins.High(16)
165185
c.Assert(pins.Get(16), qt.Equals, true)
186+
pins.Toggle(16)
187+
c.Assert(pins.Get(16), qt.Equals, false)
166188
}

0 commit comments

Comments
 (0)