package mcp23017 import ( "fmt" "testing" qt "github.com/frankban/quicktest" "tinygo.org/x/drivers/tester" ) func TestGetPins(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 0x20) fdev.Registers[rGPIO] = 0b10101100 fdev.Registers[rGPIO|portB] = 0b01010011 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(0b01010011_10101100)) } func TestSetPins(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 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.SetPins(0b01100000_00110000, 0b10101010_01010101) c.Assert(err, qt.IsNil) pins, err = dev.GetPins() c.Assert(err, qt.IsNil) c.Assert(pins, qt.Equals, Pins(0b01110000_0001_1010)) // The logic uses the cached value of the pins rather than // reading it from the registers each time. fdev.Registers[rGPIO] = 0 fdev.Registers[rGPIO|portB] = 0 err = dev.SetPins(0b01000000_00110000, 0b01100000_00000000) c.Assert(err, qt.IsNil) pins, err = dev.GetPins() c.Assert(err, qt.IsNil) c.Assert(pins, qt.Equals, Pins(0b01010000_00011010)) } func TestTogglePins(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 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 := tester.NewI2CBus(c) fdev := newDevice(bus, 0x20) dev, err := NewI2C(bus, 0x20) c.Assert(err, qt.IsNil) // Calling SetModes with less items in than there are // pins should use the last item for all the unspecified ones. err = dev.SetModes([]PinMode{Input | Invert, Output}) c.Assert(err, qt.IsNil) c.Assert(fdev.Registers[rIODIR], qt.Equals, uint8(0b00000001)) c.Assert(fdev.Registers[rIOPOL], qt.Equals, uint8(0b00000001)) c.Assert(fdev.Registers[rGPPU], qt.Equals, uint8(0)) modes := make([]PinMode, 17) err = dev.GetModes(modes) c.Assert(err, qt.IsNil) c.Assert(modes[0], qt.Equals, Input|Invert) for i, m := range modes[1:16] { c.Assert(m, qt.Equals, Output, qt.Commentf("index %d", i)) } c.Assert(modes[16], qt.Equals, PinMode(0)) // Using an empty slice should reset all the modes to the initial state. err = dev.SetModes(nil) c.Assert(err, qt.IsNil) c.Assert(fdev.Registers[rIODIR], qt.Equals, uint8(0b11111111)) c.Assert(fdev.Registers[rIOPOL], qt.Equals, uint8(0)) c.Assert(fdev.Registers[rGPPU], qt.Equals, uint8(0)) } func TestPinSetGet(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 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.Set(true) c.Assert(err, qt.Equals, nil) c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0b10)) v, err = pin.Get() c.Assert(err, qt.Equals, nil) c.Assert(v, qt.Equals, true) err = pin.Set(false) c.Assert(err, qt.Equals, nil) c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0)) err = pin.High() c.Assert(err, qt.Equals, nil) c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0b10)) err = pin.Low() c.Assert(err, qt.Equals, nil) c.Assert(fdev.Registers[rGPIO], qt.Equals, uint8(0)) } func TestPinToggle(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 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 := tester.NewI2CBus(c) fdev := newDevice(bus, 0x20) dev, err := NewI2C(bus, 0x20) c.Assert(err, qt.IsNil) pin := dev.Pin(1) mode, err := pin.GetMode() c.Assert(err, qt.IsNil) c.Assert(mode, qt.Equals, PinMode(0)) c.Assert(mode&Direction, qt.Equals, Input) err = pin.SetMode(Input | Pullup | Invert) c.Assert(err, qt.IsNil) c.Assert(fdev.Registers[rIODIR], qt.Equals, uint8(0b11111111)) c.Assert(fdev.Registers[rIOPOL], qt.Equals, uint8(0b10)) c.Assert(fdev.Registers[rGPPU], qt.Equals, uint8(0b10)) mode, err = pin.GetMode() c.Assert(err, qt.IsNil) c.Assert(mode, qt.Equals, Input|Pullup|Invert) // Set another pin to output. err = dev.Pin(2).SetMode(Output) c.Assert(err, qt.IsNil) c.Assert(fdev.Registers[rIODIR], qt.Equals, uint8(0b11111011)) c.Assert(fdev.Registers[rIOPOL], qt.Equals, uint8(0b10)) c.Assert(fdev.Registers[rGPPU], qt.Equals, uint8(0b10)) // Check that changing a pin in port B works too. err = dev.Pin(8).SetMode(Output) c.Assert(err, qt.IsNil) c.Assert(fdev.Registers[rIODIR], qt.Equals, uint8(0b11111011)) c.Assert(fdev.Registers[rIODIR|portB], qt.Equals, uint8(0b11111110)) c.Assert(fdev.Registers[rIOPOL], qt.Equals, uint8(0b10)) c.Assert(fdev.Registers[rIOPOL|portB], qt.Equals, uint8(0)) c.Assert(fdev.Registers[rGPPU], qt.Equals, uint8(0b10)) c.Assert(fdev.Registers[rGPPU|portB], qt.Equals, uint8(0)) } func TestPins(t *testing.T) { c := qt.New(t) var p Pins p.Set(1, true) c.Assert(p, qt.Equals, Pins(0b10)) c.Assert(p.Get(1), qt.Equals, true) c.Assert(p.Get(0), qt.Equals, false) c.Assert(p.Get(16), qt.Equals, false) p.High(2) c.Assert(p, qt.Equals, Pins(0b110)) p.Low(1) c.Assert(p, qt.Equals, Pins(0b100)) } func TestInitWithError(t *testing.T) { c := qt.New(t) bus := tester.NewI2CBus(c) fdev := newDevice(bus, 0x20) fdev.Err = fmt.Errorf("some error") dev, err := NewI2C(bus, 0x20) c.Assert(err, qt.ErrorMatches, `cannot initialize mcp23017 device at 0x20: some error`) c.Assert(dev, qt.IsNil) } func newDevice(bus *tester.I2CBus, addr uint8) *tester.I2CDevice8 { fdev := bus.NewDevice(addr) // IODIRA and IODIRB are all ones by default. fdev.Registers[rIODIR] = 0xff fdev.Registers[rIODIR|portB] = 0xff return fdev }