mirror of https://github.com/periph/devices
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.
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
// Copyright 2018 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 pca9548
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"periph.io/x/conn"
|
|
"periph.io/x/conn/i2c"
|
|
"periph.io/x/conn/i2c/i2creg"
|
|
"periph.io/x/conn/physic"
|
|
)
|
|
|
|
// DefaultOpts is the recommended default options.
|
|
var DefaultOpts = Opts{Addr: 0x70}
|
|
|
|
// Opts is the pca9548 configuration.
|
|
type Opts struct {
|
|
// Addr is the pca9548 I²C Address. Valid addresses for the NXP pca9548 are
|
|
// 0x70 to 0x77. The address is set by pulling A0~A2 low or high. Please
|
|
// refer to the datasheet.
|
|
Addr int
|
|
}
|
|
|
|
// Dev is handle to a pca9548 I²C Multiplexer.
|
|
type Dev struct {
|
|
// Immutable.
|
|
c i2c.Bus
|
|
address uint16
|
|
name string
|
|
numPorts uint8
|
|
|
|
// Mutable.
|
|
mu sync.Mutex
|
|
activePort uint8
|
|
}
|
|
|
|
// New creates a new handle to a pca9548 I²C multiplexer.
|
|
func New(bus i2c.Bus, opts *Opts) (*Dev, error) {
|
|
if opts.Addr < 0x70 || opts.Addr > 0x77 {
|
|
return nil, errors.New("address outside valid range of 0x70-0x77")
|
|
}
|
|
d := &Dev{
|
|
c: bus,
|
|
activePort: 0xFF,
|
|
address: uint16(opts.Addr),
|
|
numPorts: 8,
|
|
name: "pca9548-" + strconv.FormatUint(uint64(opts.Addr), 16),
|
|
}
|
|
r := make([]byte, 1)
|
|
err := bus.Tx(uint16(opts.Addr), nil, r)
|
|
if err != nil {
|
|
return nil, errors.New("could not establish communicate with multiplexer: " + err.Error())
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// RegisterPorts registers multiplexer ports with the host. These ports can
|
|
// then be used as any other i2c.Bus. Busses will be named "alias0", "alias1"
|
|
// etc. If using more than one multiplexer note that the alias must be unique.
|
|
// Returns slice of ports names registered and error.
|
|
func (d *Dev) RegisterPorts(alias string) ([]string, error) {
|
|
var portNames []string
|
|
for i := uint8(0); i < d.numPorts; i++ {
|
|
portStr := strconv.Itoa(int(i))
|
|
addrStr := strconv.FormatUint(uint64(d.address), 16)
|
|
portName := d.c.String() + "-pca9548-" + addrStr + "-" + portStr
|
|
opener := newOpener(d, i, alias+portStr, portName)
|
|
if err := i2creg.Register(portName, []string{alias + portStr}, -1, opener); err != nil {
|
|
return portNames, err
|
|
}
|
|
portNames = append(portNames, portName)
|
|
}
|
|
return portNames, nil
|
|
}
|
|
|
|
// Halt does nothing.
|
|
func (d *Dev) Halt() error {
|
|
return nil
|
|
}
|
|
|
|
// String returns the bus base name for multiplexer ports.
|
|
func (d *Dev) String() string {
|
|
return d.name
|
|
}
|
|
|
|
// tx wraps the master bus tx, maintains which port that each bus is registered
|
|
// on so that communication from the master is always on the right port.
|
|
func (d *Dev) tx(port uint8, address uint16, w, r []byte) error {
|
|
if address == d.address {
|
|
return errors.New("device address conflicts with multiplexer address")
|
|
}
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
// Change active port if needed.
|
|
if port != d.activePort {
|
|
if err := d.c.Tx(d.address, []byte{1 << port}, nil); err != nil {
|
|
return errors.New("failed to change active port on multiplexer: " + err.Error())
|
|
}
|
|
d.activePort = port
|
|
}
|
|
return d.c.Tx(address, w, r)
|
|
}
|
|
|
|
// newOpener is a helper for creating an opener func.
|
|
func newOpener(d *Dev, portNumber uint8, alias string, name string) i2creg.Opener {
|
|
return func() (i2c.BusCloser, error) {
|
|
return &port{
|
|
name: name + "(" + alias + ")",
|
|
mux: d,
|
|
number: portNumber,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// port is a i2c.BusCloser.
|
|
type port struct {
|
|
// Immutable.
|
|
name string
|
|
number uint8
|
|
|
|
// Mutable.
|
|
mu sync.Mutex
|
|
mux *Dev
|
|
}
|
|
|
|
// String gets the port number of the bus on the multiplexer.
|
|
func (p *port) String() string { return "Port:" + p.name }
|
|
|
|
// SetSpeed is no implemented as the port slaves the master port clock.
|
|
func (p *port) SetSpeed(f physic.Frequency) error {
|
|
return errors.New("SetSpeed is not impelmented on a port by port basis")
|
|
}
|
|
|
|
// Tx does a transaction on the multiplexer port it is register to.
|
|
func (p *port) Tx(addr uint16, w, r []byte) error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
if p.mux == nil {
|
|
return errors.New(p.String() + " has been closed")
|
|
}
|
|
return p.mux.tx(p.number, addr, w, r)
|
|
}
|
|
|
|
// Close closes a port.
|
|
func (p *port) Close() error {
|
|
p.mu.Lock()
|
|
p.mux = nil
|
|
p.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
var _ conn.Resource = &Dev{}
|
|
var _ i2c.Bus = &port{}
|