forked from google/periph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pca9548.go
158 lines (138 loc) · 4.2 KB
/
pca9548.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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/periph/conn"
"periph.io/x/periph/conn/i2c"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/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{}