mirror of https://github.com/periph/devices
mcp23xxx: support for Microchip MCP23 family of IO extenders (#433)
Initial code for MCP23xxx support.pull/1/head
parent
1f4a49516e
commit
dffddf2e7e
@ -0,0 +1,6 @@
|
||||
// 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 provides driver for the MCP23 family of IO extenders
|
||||
package mcp23xxx
|
||||
@ -0,0 +1,50 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/i2c/i2creg"
|
||||
"periph.io/x/periph/experimental/devices/mcp23xxx"
|
||||
"periph.io/x/periph/host"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
// Make sure periph is initialized.
|
||||
if _, err := host.Init(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Open default I²C bus.
|
||||
bus, err := i2creg.Open("")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open I²C: %v", err)
|
||||
}
|
||||
defer bus.Close()
|
||||
|
||||
// Create a new I2C IO extender
|
||||
extender, err := mcp23xxx.NewI2C(bus, mcp23xxx.MCP23017, 0x20)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for _, port := range extender.Pins {
|
||||
for _, pin := range port {
|
||||
err = pin.In(gpio.Float, gpio.NoEdge)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
level := pin.Read()
|
||||
fmt.Printf("%s\t%s\n", pin.Name(), level.String())
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,176 @@
|
||||
// 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 (
|
||||
"testing"
|
||||
|
||||
"periph.io/x/periph/conn/conntest"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/gpio/gpioreg"
|
||||
"periph.io/x/periph/conn/i2c/i2ctest"
|
||||
"periph.io/x/periph/conn/spi"
|
||||
"periph.io/x/periph/conn/spi/spitest"
|
||||
)
|
||||
|
||||
func TestMCP23017_out(t *testing.T) {
|
||||
const address uint16 = 0x20
|
||||
scenario := &i2ctest.Playback{
|
||||
Ops: []i2ctest.IO{
|
||||
// iodir is read on creation
|
||||
{Addr: address, W: []byte{0x00}, R: []byte{0xFF}},
|
||||
{Addr: address, W: []byte{0x01}, R: []byte{0xFF}},
|
||||
// iodira is set to output
|
||||
{Addr: address, W: []byte{0x00, 0xFE}, R: nil},
|
||||
// olata is read
|
||||
{Addr: address, W: []byte{0x14}, R: []byte{0x00}},
|
||||
// writing back unchanged value is omitted
|
||||
// writing high output
|
||||
{Addr: address, W: []byte{0x14, 0x01}, R: nil},
|
||||
},
|
||||
}
|
||||
|
||||
dev, err := NewI2C(scenario, MCP23017, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
pA0 := gpioreg.ByName("MCP23017_20_PORTA_0")
|
||||
pA0.Out(gpio.Low)
|
||||
pA0.Out(gpio.High)
|
||||
}
|
||||
|
||||
func TestMCP23S17_out(t *testing.T) {
|
||||
const address uint16 = 0x20
|
||||
scenario := &spitest.Playback{
|
||||
Playback: conntest.Playback{
|
||||
Ops: []conntest.IO{
|
||||
// iodira is read
|
||||
{W: []byte{0x41, 0x00}, R: []byte{0xFF}},
|
||||
{W: []byte{0x41, 0x01}, R: []byte{0xFF}},
|
||||
// iodira is set to output
|
||||
{W: []byte{0x40, 0x00, 0xFE}, R: nil},
|
||||
// olata is read
|
||||
{W: []byte{0x41, 0x14}, R: []byte{0x00}},
|
||||
// writing back unchanged value is omitted
|
||||
// writing high output
|
||||
{W: []byte{0x40, 0x14, 0x01}, R: nil},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := scenario.Connect(1, spi.Mode0, 8)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dev, err := NewSPI(conn, MCP23S17)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
pA0 := gpioreg.ByName("MCP23S17_PORTA_0")
|
||||
|
||||
pA0.Out(gpio.Low)
|
||||
pA0.Out(gpio.High)
|
||||
}
|
||||
|
||||
func TestMCP23017_in(t *testing.T) {
|
||||
const address uint16 = 0x20
|
||||
scenario := &i2ctest.Playback{
|
||||
Ops: []i2ctest.IO{
|
||||
// iodir is read on creation
|
||||
{Addr: address, W: []byte{0x00}, R: []byte{0xFF}},
|
||||
{Addr: address, W: []byte{0x01}, R: []byte{0xFF}},
|
||||
// not written, since it didn't change
|
||||
// gppua is read
|
||||
{Addr: address, W: []byte{0x0C}, R: []byte{0x00}},
|
||||
// not written, since it didn't change
|
||||
// gpio is read
|
||||
{Addr: address, W: []byte{0x12}, R: []byte{0x01}},
|
||||
},
|
||||
}
|
||||
|
||||
dev, err := NewI2C(scenario, MCP23017, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
pA0 := gpioreg.ByName("MCP23017_20_PORTA_0")
|
||||
|
||||
pA0.In(gpio.Float, gpio.NoEdge)
|
||||
l := pA0.Read()
|
||||
if l != gpio.High {
|
||||
t.Errorf("Input should be High")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCP23017_inInverted(t *testing.T) {
|
||||
const address uint16 = 0x20
|
||||
scenario := &i2ctest.Playback{
|
||||
Ops: []i2ctest.IO{
|
||||
// iodir is read on creation
|
||||
{Addr: address, W: []byte{0x00}, R: []byte{0xFF}},
|
||||
{Addr: address, W: []byte{0x01}, R: []byte{0xFF}},
|
||||
// not written, since it didn't change
|
||||
// gppua is read
|
||||
{Addr: address, W: []byte{0x0C}, R: []byte{0x00}},
|
||||
// not written, since it didn't change
|
||||
// polarity is set
|
||||
{Addr: address, W: []byte{0x02}, R: []byte{0x01}},
|
||||
// gpio is read
|
||||
{Addr: address, W: []byte{0x12}, R: []byte{0x01}},
|
||||
},
|
||||
}
|
||||
|
||||
dev, err := NewI2C(scenario, MCP23017, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
pA0 := gpioreg.ByName("MCP23017_20_PORTA_0").(Pin)
|
||||
|
||||
pA0.In(gpio.Float, gpio.NoEdge)
|
||||
pA0.SetPolarityInverted(true)
|
||||
l := pA0.Read()
|
||||
if l != gpio.High {
|
||||
t.Errorf("Input should be High")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMCP23017_inPullUp(t *testing.T) {
|
||||
const address uint16 = 0x20
|
||||
scenario := &i2ctest.Playback{
|
||||
Ops: []i2ctest.IO{
|
||||
// iodir is read on creation
|
||||
{Addr: address, W: []byte{0x00}, R: []byte{0xFF}},
|
||||
{Addr: address, W: []byte{0x01}, R: []byte{0xFF}},
|
||||
// not written, since it didn't change
|
||||
// gppua is read and written
|
||||
{Addr: address, W: []byte{0x0C}, R: []byte{0x00}},
|
||||
{Addr: address, W: []byte{0x0C, 0x01}, R: nil},
|
||||
// not written, since it didn't change
|
||||
// gpio is read
|
||||
{Addr: address, W: []byte{0x12}, R: []byte{0x01}},
|
||||
},
|
||||
}
|
||||
|
||||
dev, err := NewI2C(scenario, MCP23017, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dev.Close()
|
||||
|
||||
pA0 := gpioreg.ByName("MCP23017_20_PORTA_0")
|
||||
|
||||
pA0.In(gpio.PullUp, gpio.NoEdge)
|
||||
l := pA0.Read()
|
||||
if l != gpio.High {
|
||||
t.Errorf("Input should be High")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,220 @@
|
||||
// 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/periph/conn/gpio/gpioreg"
|
||||
"periph.io/x/periph/conn/i2c"
|
||||
"periph.io/x/periph/conn/spi"
|
||||
)
|
||||
|
||||
// Dev his a handle for a configured MCP23xxx device.
|
||||
type Dev struct {
|
||||
// Pins provide access to extender pins.
|
||||
Pins [][]Pin
|
||||
}
|
||||
|
||||
// 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. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23009
|
||||
MCP23009 Variant = "MCP23009"
|
||||
|
||||
// MCP23S09 8-bit SPI extender. 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 8-bit I2C extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23017
|
||||
MCP23017 Variant = "MCP23017"
|
||||
|
||||
// MCP23S17 8-bit SPI extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23S17
|
||||
MCP23S17 Variant = "MCP23S17"
|
||||
|
||||
// MCP23018 8-bit I2C extender. Datasheet: https://www.microchip.com/wwwproducts/en/MCP23018
|
||||
MCP23018 Variant = "MCP23018"
|
||||
|
||||
// MCP23S18 8-bit SPI extender. 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] {
|
||||
gpioreg.Register(pin)
|
||||
}
|
||||
}
|
||||
return &Dev{
|
||||
Pins: pins,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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(0x0B),
|
||||
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),
|
||||
}}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/physic"
|
||||
"periph.io/x/periph/conn/pin"
|
||||
)
|
||||
|
||||
// Pin extends gpio.PinIO interface with features supported by MCP23xxx devices.
|
||||
type Pin interface {
|
||||
gpio.PinIO
|
||||
// SetPolarityInverted if set to true, GPIO register bit reflects the same logic state of the input pin.
|
||||
SetPolarityInverted(p bool) error
|
||||
// IsPolarityInverted returns true if the value of the input pin reflects inverted logic state.
|
||||
IsPolarityInverted() (bool, error)
|
||||
}
|
||||
|
||||
type port struct {
|
||||
name string
|
||||
|
||||
// GPIO basic registers
|
||||
iodir registerCache
|
||||
gpio registerCache
|
||||
olat registerCache
|
||||
|
||||
// polarity setting
|
||||
ipol registerCache
|
||||
|
||||
// pull-up control register
|
||||
// Not present in all devices
|
||||
gppu registerCache
|
||||
supportPullup bool
|
||||
|
||||
// interrupt handling registers
|
||||
supportInterrupt bool
|
||||
gpinten registerCache
|
||||
intcon registerCache
|
||||
intf registerCache
|
||||
intcap registerCache
|
||||
}
|
||||
|
||||
type portpin struct {
|
||||
port *port
|
||||
pinbit uint8
|
||||
}
|
||||
|
||||
func (p *port) pins() []Pin {
|
||||
result := make([]Pin, 8)
|
||||
var i uint8
|
||||
for i = 0; i < 8; i++ {
|
||||
result[i] = &portpin{
|
||||
port: p,
|
||||
pinbit: i,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *portpin) String() string {
|
||||
return p.Name()
|
||||
}
|
||||
|
||||
func (p *portpin) Halt() error {
|
||||
// To halt all drive, set to high-impedance input
|
||||
return p.In(gpio.Float, gpio.NoEdge)
|
||||
}
|
||||
|
||||
func (p *portpin) Name() string {
|
||||
return p.port.name + "_" + strconv.Itoa(int(p.pinbit))
|
||||
}
|
||||
|
||||
func (p *portpin) Number() int {
|
||||
return int(p.pinbit)
|
||||
}
|
||||
|
||||
func (p *portpin) Function() string {
|
||||
return string(p.Func())
|
||||
}
|
||||
|
||||
func (p *portpin) In(pull gpio.Pull, edge gpio.Edge) error {
|
||||
// Set pin to input
|
||||
err := p.port.iodir.getAndSetBit(p.pinbit, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set pullup
|
||||
switch pull {
|
||||
case gpio.PullNoChange:
|
||||
// don't check, don't change
|
||||
case gpio.PullDown:
|
||||
// pull down is not supported by any device
|
||||
return errors.New("MCP23xxx: PullDown is not supported")
|
||||
case gpio.PullUp:
|
||||
if !p.port.supportPullup {
|
||||
return errors.New("MCP23xxx: PullUp is not supported by this device")
|
||||
}
|
||||
err = p.port.gppu.getAndSetBit(p.pinbit, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case gpio.Float:
|
||||
if p.port.supportPullup {
|
||||
err = p.port.gppu.getAndSetBit(p.pinbit, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// check edge detection
|
||||
// TODO interrupt support
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *portpin) Read() gpio.Level {
|
||||
v, _ := p.port.gpio.getBit(p.pinbit, false)
|
||||
if v {
|
||||
return gpio.High
|
||||
}
|
||||
return gpio.Low
|
||||
}
|
||||
|
||||
func (p *portpin) WaitForEdge(timeout time.Duration) bool {
|
||||
// TODO interrupt handling
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *portpin) Pull() gpio.Pull {
|
||||
if !p.port.supportPullup {
|
||||
return gpio.Float
|
||||
}
|
||||
v, err := p.port.gppu.getBit(p.pinbit, true)
|
||||
if err != nil {
|
||||
return gpio.PullNoChange
|
||||
}
|
||||
if v {
|
||||
return gpio.PullUp
|
||||
}
|
||||
return gpio.Float
|
||||
}
|
||||
|
||||
func (p *portpin) DefaultPull() gpio.Pull {
|
||||
return gpio.Float
|
||||
}
|
||||
|
||||
func (p *portpin) Out(l gpio.Level) error {
|
||||
err := p.port.iodir.getAndSetBit(p.pinbit, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.port.olat.getAndSetBit(p.pinbit, l == gpio.High, true)
|
||||
}
|
||||
|
||||
func (p *portpin) PWM(duty gpio.Duty, f physic.Frequency) error {
|
||||
return errors.New("MCP23xxx: PWM is not supported")
|
||||
}
|
||||
|
||||
func (p *portpin) Func() pin.Func {
|
||||
v, _ := p.port.iodir.getBit(p.pinbit, true)
|
||||
if v {
|
||||
return gpio.IN
|
||||
}
|
||||
return gpio.OUT
|
||||
}
|
||||
|
||||
func (p *portpin) SupportedFuncs() []pin.Func {
|
||||
return supportedFuncs[:]
|
||||
}
|
||||
|
||||
func (p *portpin) SetFunc(f pin.Func) error {
|
||||
var v bool
|
||||
switch f {
|
||||
case gpio.IN:
|
||||
v = true
|
||||
case gpio.OUT:
|
||||
v = false
|
||||
default:
|
||||
return errors.New("MCP23xxx: Function not supported: " + string(f))
|
||||
}
|
||||
return p.port.iodir.getAndSetBit(p.pinbit, v, true)
|
||||
}
|
||||
|
||||
func (p *portpin) SetPolarityInverted(pol bool) error {
|
||||
return p.port.ipol.getAndSetBit(p.pinbit, pol, true)
|
||||
}
|
||||
func (p *portpin) IsPolarityInverted() (bool, error) {
|
||||
return p.port.ipol.getBit(p.pinbit, true)
|
||||
}
|
||||
|
||||
var supportedFuncs = [...]pin.Func{gpio.IN, gpio.OUT}
|
||||
@ -0,0 +1,111 @@
|
||||
// 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 (
|
||||
"periph.io/x/periph/conn/i2c"
|
||||
"periph.io/x/periph/conn/spi"
|
||||
)
|
||||
|
||||
type registerAccess interface {
|
||||
define(address uint8) registerCache
|
||||
readRegister(address uint8) (uint8, error)
|
||||
writeRegister(address uint8, value uint8) error
|
||||
}
|
||||
|
||||
type i2cRegisterAccess struct {
|
||||
*i2c.Dev
|
||||
}
|
||||
|
||||
func (ra *i2cRegisterAccess) readRegister(address uint8) (uint8, error) {
|
||||
r := make([]byte, 1)
|
||||
err := ra.Tx([]byte{address}, r)
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (ra *i2cRegisterAccess) writeRegister(address uint8, value uint8) error {
|
||||
return ra.Tx([]byte{address, value}, nil)
|
||||
}
|
||||
|
||||
func (ra *i2cRegisterAccess) define(address uint8) registerCache {
|
||||
return newRegister(ra, address)
|
||||
}
|
||||
|
||||
type spiRegisterAccess struct {
|
||||
spi.Conn
|
||||
}
|
||||
|
||||
func (ra *spiRegisterAccess) readRegister(address uint8) (uint8, error) {
|
||||
r := make([]byte, 1)
|
||||
err := ra.Tx([]byte{0x41, address}, r)
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (ra *spiRegisterAccess) writeRegister(address uint8, value uint8) error {
|
||||
return ra.Tx([]byte{0x40, address, value}, nil)
|
||||
}
|
||||
|
||||
func (ra *spiRegisterAccess) define(address uint8) registerCache {
|
||||
return newRegister(ra, address)
|
||||
}
|
||||
|
||||
type registerCache struct {
|
||||
registerAccess
|
||||
address uint8
|
||||
got bool
|
||||
cache uint8
|
||||
}
|
||||
|
||||
func newRegister(ra registerAccess, address uint8) registerCache {
|
||||
return registerCache{
|
||||
registerAccess: ra,
|
||||
address: address,
|
||||
got: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *registerCache) readValue(cached bool) (uint8, error) {
|
||||
if cached && r.got {
|
||||
return r.cache, nil
|
||||
}
|
||||
v, err := r.readRegister(r.address)
|
||||
if err == nil {
|
||||
r.got = true
|
||||
r.cache = v
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (r *registerCache) writeValue(value uint8, cached bool) error {
|
||||
if cached && r.got && value == r.cache {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := r.writeRegister(r.address, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.got = true
|
||||
r.cache = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *registerCache) getAndSetBit(bit uint8, value bool, cached bool) error {
|
||||
v, err := r.readValue(cached)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value {
|
||||
v |= 1 << bit
|
||||
} else {
|
||||
v &= ^(1 << bit)
|
||||
}
|
||||
return r.writeValue(v, cached)
|
||||
}
|
||||
|
||||
func (r *registerCache) getBit(bit uint8, cached bool) (bool, error) {
|
||||
v, err := r.readValue(cached)
|
||||
return 0 != (v & (1 << bit)), err
|
||||
}
|
||||
Loading…
Reference in New Issue