tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/mcp23017/device.go (about)

     1  // Package mcp23017 implements a driver for the MCP23017
     2  // I2C port expander chip. See https://www.microchip.com/wwwproducts/en/MCP23017
     3  // for details of the interface.
     4  //
     5  // It also provides a way of joining several such devices into one logical
     6  // device (see the Devices type).
     7  package mcp23017
     8  
     9  import (
    10  	"errors"
    11  
    12  	"tinygo.org/x/drivers"
    13  	"tinygo.org/x/drivers/internal/legacy"
    14  )
    15  
    16  const (
    17  	// hwAddressFixed holds the bits of the hardware address
    18  	// that are fixed by the chip. Bits 0-3 (those in hwAddressMask)
    19  	// are user-defined by the A0-A2 pins on the chip.
    20  	hwAddress = uint8(0b010_0000)
    21  	// hwAddressMask holds the bits that are significant in hwAddress.
    22  	hwAddressMask = uint8(0b111_1000)
    23  )
    24  
    25  type register uint8
    26  
    27  const (
    28  	// The following registers all refer to port A (except
    29  	// rIOCON with is port-agnostic).
    30  	// ORing them with portB makes them refer to port B.
    31  	rIODIR        = register(0x00) // I/O direction. 0=output; 1=input.
    32  	rIOPOL        = register(0x02) // Invert input values. 0=normal; 1=inverted.
    33  	rGPINTEN      = register(0x04)
    34  	rDEFVAL       = register(0x06)
    35  	rINTCON       = register(0x08)
    36  	rIOCON        = register(0x0A)
    37  	rGPPU         = register(0x0C) // Pull up; 0=no pull-up; 1=pull-up.
    38  	rINTF         = register(0x0E)
    39  	rINTCAP       = register(0x10)
    40  	rGPIO         = register(0x12) // GPIO pin values.
    41  	rOLAT         = register(0x14)
    42  	registerCount = 0x16
    43  
    44  	portB = register(0x1)
    45  )
    46  
    47  // PinCount is the number of GPIO pins available on the chip.
    48  const PinCount = 16
    49  
    50  // PinMode represents a possible I/O mode for a pin.
    51  // The zero value represents the default value
    52  // after the chip is reset (input).
    53  type PinMode uint8
    54  
    55  const (
    56  	// Input configures a pin as an input.
    57  	Input = PinMode(0)
    58  	// Output configures a pin as an output.
    59  	Output = PinMode(1)
    60  
    61  	// Direction is the bit mask of the pin mode representing
    62  	// the I/O direction.
    63  	Direction = PinMode(1)
    64  
    65  	// Pullup can be bitwise-or'd with Input
    66  	// to cause the pull-up resistor on the pin to
    67  	// be enabled.
    68  	Pullup = PinMode(2)
    69  
    70  	// Invert can be bitwise-or'd with Input to
    71  	// cause the pin value to reflect the inverted
    72  	// value on the pin.
    73  	Invert = PinMode(4)
    74  )
    75  
    76  // ErrInvalidHWAddress is returned when the hardware address
    77  // of the device is not valid (only some bits can be set by the
    78  // address pins).
    79  var ErrInvalidHWAddress = errors.New("invalid hardware address")
    80  
    81  // New returns a new MCP23017 device at the given I2C address
    82  // on the given bus.
    83  // It returns ErrInvalidHWAddress if the address isn't possible for the device.
    84  //
    85  // By default all pins are configured as inputs.
    86  func NewI2C(bus drivers.I2C, address uint8) (*Device, error) {
    87  	if address&hwAddressMask != hwAddress {
    88  		return nil, ErrInvalidHWAddress
    89  	}
    90  	d := &Device{
    91  		bus:  bus,
    92  		addr: address,
    93  	}
    94  	pins, err := d.GetPins()
    95  	if err != nil {
    96  		return nil, errors.New("cannot initialize mcp23017 device at " + hex(address) + ": " + err.Error())
    97  	}
    98  	d.pins = pins
    99  	return d, nil
   100  }
   101  
   102  func hex(x uint8) string {
   103  	digits := "0123456789abcdef"
   104  	return "0x" + digits[x>>4:x>>4+1] + digits[x&0xf:x&0xf+1]
   105  }
   106  
   107  // Device represents an MCP23017 device.
   108  type Device struct {
   109  	// TODO would it be good to have a mutex here so that independent goroutines
   110  	// could change pins without needing to do the locking themselves?
   111  
   112  	// bus holds the reference the I2C bus that the device lives on.
   113  	// It's an interface so that we can write tests for it.
   114  	bus  drivers.I2C
   115  	addr uint8
   116  	// pins caches the most recent pin values that have been set.
   117  	// This enables us to change individual pin values without
   118  	// doing a read followed by a write.
   119  	pins Pins
   120  }
   121  
   122  // GetPins reads all 16 pins from ports A and B.
   123  func (d *Device) GetPins() (Pins, error) {
   124  	return d.readRegisterAB(rGPIO)
   125  }
   126  
   127  // SetPins sets all the pins for which mask is high
   128  // to their respective values in pins.
   129  //
   130  // That is, it does the equivalent of:
   131  //
   132  //	for i := 0; i < PinCount; i++ {
   133  //		if mask.Get(i) {
   134  //			d.Pin(i).Set(pins.Get(i))
   135  //		}
   136  //	}
   137  func (d *Device) SetPins(pins, mask Pins) error {
   138  	if mask == 0 {
   139  		return nil
   140  	}
   141  	newPins := (d.pins &^ mask) | (pins & mask)
   142  	if newPins == d.pins {
   143  		return nil
   144  	}
   145  	err := d.writeRegisterAB(rGPIO, newPins)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	d.pins = newPins
   150  	return nil
   151  }
   152  
   153  // TogglePins inverts the values on all pins for
   154  // which mask is high.
   155  func (d *Device) TogglePins(mask Pins) error {
   156  	if mask == 0 {
   157  		return nil
   158  	}
   159  	return d.SetPins(^d.pins, mask)
   160  }
   161  
   162  // Pin returns a Pin representing the given pin number (from 0 to 15).
   163  // Pin numbers from 0 to 7 represent port A pins 0 to 7.
   164  // Pin numbers from 8 to 15 represent port B pins 0 to 7.
   165  func (d *Device) Pin(pin int) Pin {
   166  	if pin < 0 || pin >= PinCount {
   167  		panic("pin out of range")
   168  	}
   169  	var mask Pins
   170  	mask.High(pin)
   171  	return Pin{
   172  		dev:  d,
   173  		mask: mask,
   174  		pin:  uint8(pin),
   175  	}
   176  }
   177  
   178  // SetAllModes sets the mode of all the pins in a single operation.
   179  // If len(modes) is less than PinCount, all remaining pins
   180  // will be set fo modes[len(modes)-1], or PinMode(0) if
   181  // modes is empty.
   182  //
   183  // If len(modes) is greater than PinCount, the excess entries
   184  // will be ignored.
   185  func (d *Device) SetModes(modes []PinMode) error {
   186  	defaultMode := PinMode(0)
   187  	if len(modes) > 0 {
   188  		defaultMode = modes[len(modes)-1]
   189  	}
   190  	var dir, pullup, invert Pins
   191  	for i := 0; i < PinCount; i++ {
   192  		mode := defaultMode
   193  		if i < len(modes) {
   194  			mode = modes[i]
   195  		}
   196  		if mode&Direction == Input {
   197  			dir.High(i)
   198  		}
   199  		if mode&Pullup != 0 {
   200  			pullup.High(i)
   201  		}
   202  		if mode&Invert != 0 {
   203  			invert.High(i)
   204  		}
   205  	}
   206  	if err := d.writeRegisterAB(rIODIR, dir); err != nil {
   207  		return err
   208  	}
   209  	if err := d.writeRegisterAB(rGPPU, pullup); err != nil {
   210  		return err
   211  	}
   212  	if err := d.writeRegisterAB(rIOPOL, invert); err != nil {
   213  		return err
   214  	}
   215  	return nil
   216  }
   217  
   218  // GetModes reads the modes of all the pins into modes.
   219  // It's OK if len(modes) is not PinCount - excess entries
   220  // will be left unset.
   221  func (d *Device) GetModes(modes []PinMode) error {
   222  	dir, err := d.readRegisterAB(rIODIR)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	pullup, err := d.readRegisterAB(rGPPU)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	invert, err := d.readRegisterAB(rIOPOL)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	if len(modes) > PinCount {
   235  		modes = modes[:PinCount]
   236  	}
   237  	for i := range modes {
   238  		mode := Output
   239  		if dir.Get(i) {
   240  			mode = Input
   241  		}
   242  		if pullup.Get(i) {
   243  			mode |= Pullup
   244  		}
   245  		if invert.Get(i) {
   246  			mode |= Invert
   247  		}
   248  		modes[i] = mode
   249  	}
   250  	return nil
   251  }
   252  
   253  func (d *Device) writeRegisterAB(r register, val Pins) error {
   254  	// We rely on the auto-incrementing sequential write
   255  	// and the fact that registers alternate between A and B
   256  	// to write both ports in a single operation.
   257  	buf := [2]byte{uint8(val), uint8(val >> 8)}
   258  	return legacy.WriteRegister(d.bus, d.addr, uint8(r&^portB), buf[:])
   259  }
   260  
   261  func (d *Device) readRegisterAB(r register) (Pins, error) {
   262  	// We rely on the auto-incrementing sequential write
   263  	// and the fact that registers alternate between A and B
   264  	// to read both ports in a single operation.
   265  	var buf [2]byte
   266  	if err := legacy.ReadRegister(d.bus, d.addr, uint8(r), buf[:]); err != nil {
   267  		return Pins(0), err
   268  	}
   269  	return Pins(buf[0]) | (Pins(buf[1]) << 8), nil
   270  }
   271  
   272  // Pin represents a single GPIO pin on the device.
   273  type Pin struct {
   274  	// mask holds the mask of the pin.
   275  	mask Pins
   276  	// pin holds the actual pin number.
   277  	pin uint8
   278  	dev *Device
   279  }
   280  
   281  // Set sets the pin to the given value.
   282  func (p Pin) Set(value bool) error {
   283  	// TODO currently this always writes both registers when
   284  	// technically it only needs to write one. We could potentially
   285  	// optimize that.
   286  	if value {
   287  		return p.dev.SetPins(^Pins(0), p.mask)
   288  	} else {
   289  		return p.dev.SetPins(0, p.mask)
   290  	}
   291  }
   292  
   293  // High is short for p.Set(true).
   294  func (p Pin) High() error {
   295  	return p.Set(true)
   296  }
   297  
   298  // High is short for p.Set(false).
   299  func (p Pin) Low() error {
   300  	return p.Set(false)
   301  }
   302  
   303  // Toggle inverts the value output on the pin.
   304  func (p Pin) Toggle() error {
   305  	return p.dev.TogglePins(p.mask)
   306  }
   307  
   308  // Get returns the current value of the given pin.
   309  func (p Pin) Get() (bool, error) {
   310  	// TODO this reads 2 registers when we could read just one.
   311  	pins, err := p.dev.GetPins()
   312  	if err != nil {
   313  		return false, err
   314  	}
   315  	return pins&p.mask != 0, nil
   316  }
   317  
   318  // SetMode configures the pin to the given mode.
   319  func (p Pin) SetMode(mode PinMode) error {
   320  	// We could use a more efficient single-register
   321  	// read/write pattern but setting pin modes isn't an
   322  	// operation that's likely to need to be efficient, so
   323  	// use less code and use Get/SetModes directly.
   324  	modes := make([]PinMode, PinCount)
   325  	if err := p.dev.GetModes(modes); err != nil {
   326  		return err
   327  	}
   328  	modes[p.pin] = mode
   329  	return p.dev.SetModes(modes)
   330  }
   331  
   332  // GetMode returns the mode of the pin.
   333  func (p Pin) GetMode() (PinMode, error) {
   334  	modes := make([]PinMode, PinCount)
   335  	if err := p.dev.GetModes(modes); err != nil {
   336  		return 0, err
   337  	}
   338  	return modes[p.pin], nil
   339  }
   340  
   341  // Pins represents a bitmask of pin values.
   342  // Port A values are in bits 0-8 (numbered from least significant bit)
   343  // Port B values are in bits 9-15.
   344  type Pins uint16
   345  
   346  // Set sets the value for the given pin.
   347  func (p *Pins) Set(pin int, value bool) {
   348  	if value {
   349  		p.High(pin)
   350  	} else {
   351  		p.Low(pin)
   352  	}
   353  }
   354  
   355  // Get returns the value for the given pin.
   356  func (p Pins) Get(pin int) bool {
   357  	return (p & pinMask(pin)) != 0
   358  }
   359  
   360  // High is short for p.Set(pin, true).
   361  func (p *Pins) High(pin int) {
   362  	*p |= pinMask(pin)
   363  }
   364  
   365  // Low is short for p.Set(pin, false).
   366  func (p *Pins) Low(pin int) {
   367  	*p &^= pinMask(pin)
   368  }
   369  
   370  // Toggle inverts the value of the given pin.
   371  func (p *Pins) Toggle(pin int) {
   372  	*p ^= pinMask(pin)
   373  }
   374  
   375  func pinMask(pin int) Pins {
   376  	return 1 << pin
   377  }