You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devices/mcp23xxx/mcp23xxx.go

238 lines
6.1 KiB
Go

// Copyright 2020 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package mcp23xxx
import (
"fmt"
"strconv"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/spi"
)
// Dev is a handle for a configured MCP23xxx device.
type Dev struct {
// For all variants, Pins exposes a two dimensional slice of pins. For a
// MCP23X08/X09, [][]Pins would have one row with 8 pins, while the 16
// pin variants would have two rows with 8 pins each.
Pins [][]Pin
edgePin *gpio.PinIn
variant Variant
}
// Variant is the type denoting a specific variant of the family.
type Variant string
const (
// MCP23008 8-bit I2C extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23008
MCP23008 Variant = "MCP23008"
// MCP23S08 8-bit SPI extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23S08
MCP23S08 Variant = "MCP23S08"
// MCP23009 8-bit I2C extender w/ Open-Drain Output. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23009
MCP23009 Variant = "MCP23009"
// MCP23S09 8-bit SPI extender w/ Open-Drain Output. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23S09
MCP23S09 Variant = "MCP23S09"
// MCP23016 16-bit I2C extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23016
MCP23016 Variant = "MCP23016"
// MCP23017 16-bit I2C extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23017
MCP23017 Variant = "MCP23017"
// MCP23S17 16-bit SPI extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23S17
MCP23S17 Variant = "MCP23S17"
// MCP23018 16-bit I2C extender w/ Open-Drain Output. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23018
MCP23018 Variant = "MCP23018"
// MCP23S18 16-bit SPI extender w/ Open-Drain Output. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23S18
MCP23S18 Variant = "MCP23S18"
)
// NewI2C initializes an IO extender through I2C connection.
func NewI2C(b i2c.Bus, variant Variant, addr uint16) (*Dev, error) {
if addr&0xFFF8 != 0x20 {
return nil, fmt.Errorf("%s: Supported address range is 0x20 - 0x27", variant)
}
devicename := string(variant) + "_" + strconv.FormatInt(int64(addr), 16)
ra := &i2cRegisterAccess{
Dev: &i2c.Dev{Bus: b, Addr: addr},
}
return makeDev(ra, variant, devicename)
}
// NewSPI initializes an IO extender through I2C connection.
func NewSPI(b spi.Conn, variant Variant) (*Dev, error) {
devicename := string(variant)
ra := &spiRegisterAccess{
Conn: b,
}
return makeDev(ra, variant, devicename)
}
// Close removes any registration to the device.
func (d *Dev) Close() error {
for _, port := range d.Pins {
for _, pin := range port {
err := gpioreg.Unregister(pin.Name())
if err != nil {
return err
}
}
}
return nil
}
func makeDev(ra registerAccess, variant Variant, devicename string) (*Dev, error) {
var ports []port
switch variant {
case MCP23008, MCP23009, MCP23S08, MCP23S09:
ports = mcp23x089port(devicename, ra)
case MCP23016:
ports = mcp23x16ports(devicename, ra)
case MCP23017, MCP23S17, MCP23018, MCP23S18:
ports = mcp23x178ports(devicename, ra)
default:
return nil, fmt.Errorf("%s: Unsupported variant", devicename)
}
pins := make([][]Pin, len(ports))
for i := range ports {
// pre-cache iodir
_, err := ports[i].iodir.readValue(false)
if err != nil {
return nil, err
}
pins[i] = ports[i].pins()
for _, pin := range pins[i] {
// Ignore registration failure.
_ = gpioreg.Register(pin)
}
}
return &Dev{
Pins: pins,
variant: variant,
}, nil
}
// SetEdgePin supplies a configured GPIO pin
func (dev *Dev) SetEdgePin(pin *gpio.PinIn) {
dev.edgePin = pin
}
func (dev *Dev) String() string {
return string(dev.variant)
}
func mcp23x178ports(devicename string, ra registerAccess) []port {
return []port{{
name: devicename + "_PORTA",
// GPIO basic registers
iodir: ra.define(0x00),
gpio: ra.define(0x12),
olat: ra.define(0x14),
// polarity setting
ipol: ra.define(0x02),
// pull-up control register
gppu: ra.define(0x0C),
supportPullup: true,
// interrupt handling registers
gpinten: ra.define(0x04),
intcon: ra.define(0x08),
intf: ra.define(0x0E),
intcap: ra.define(0x10),
supportInterrupt: true,
}, {
name: devicename + "_PORTB",
// GPIO basic registers
iodir: ra.define(0x01),
gpio: ra.define(0x13),
olat: ra.define(0x15),
// polarity setting
ipol: ra.define(0x03),
supportPullup: true,
// pull-up control register
gppu: ra.define(0x0D),
// interrupt handling registers
gpinten: ra.define(0x05),
intcon: ra.define(0x09),
intf: ra.define(0x0F),
intcap: ra.define(0x11),
supportInterrupt: true,
}}
}
func mcp23x089port(devicename string, ra registerAccess) []port {
return []port{{
name: devicename,
// GPIO basic registers
iodir: ra.define(0x00),
gpio: ra.define(0x09),
olat: ra.define(0x0A),
// polarity setting
ipol: ra.define(0x01),
// pull-up control register
gppu: ra.define(0x06),
supportPullup: true,
// interrupt handling registers
gpinten: ra.define(0x02),
intcon: ra.define(0x04),
intf: ra.define(0x07),
intcap: ra.define(0x08),
supportInterrupt: true,
}}
}
func mcp23x16ports(devicename string, ra registerAccess) []port {
return []port{{
name: devicename + "_PORT0",
// GPIO basic registers
iodir: ra.define(0x06),
gpio: ra.define(0x00),
olat: ra.define(0x02),
// polarity setting
ipol: ra.define(0x04),
// pull-up control register
supportPullup: false,
// interrupt handling registers
supportInterrupt: false,
intcap: ra.define(0x08),
}, {
name: devicename + "_PORT1",
// GPIO basic registers
iodir: ra.define(0x07),
gpio: ra.define(0x01),
olat: ra.define(0x03),
// polarity setting
ipol: ra.define(0x05),
// pull-up control register
supportPullup: false,
// interrupt handling registers
supportInterrupt: false,
intcap: ra.define(0x09),
}}
}