Skip to content

Commit 1eb91ca

Browse files
committed
Add support for MCP3004, MCP3204 and MCP3208
The MCP3004 and MCP3008 have almost identical implementation, as well as the MCP3204 and MCP3208. Although differences between MCP3004/8 vs MCP3204/8 are small, it was hard to come up with a shared abstraction that wasn't very hard to read and understant. Therefore I wrote 2 implementations which lead to some duplicate code.
1 parent 3245ca6 commit 1eb91ca

File tree

4 files changed

+223
-14
lines changed

4 files changed

+223
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Go packages for pheripheral I/O.
44

5-
* [spi][spi]
5+
* [spi][spi]
66

77
## License
88

spi/adc/README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/AdvancedClimateSystems/io/spi/adc)
2+
13
# ADC
24

35
Package ADC implements a few Analog Digital Converters (ADC). Communication
@@ -20,7 +22,7 @@ func main() {
2022
conn, err := spi.Open(&spi.Devfs{
2123
Dev: "/dev/spidev32766.0",
2224
Mode: spi.Mode0,
23-
MaxSpeed: 5000000,
25+
MaxSpeed: 3600000,
2426
})
2527

2628
if err != nil {
@@ -42,7 +44,9 @@ func main() {
4244
fmt.Printf("read %f Volts from channel 3", v)
4345
}
4446
```
45-
4647
## Supported ADC's
4748

48-
* MCP3008
49+
* [MCP3004](http://www.microchip.com/wwwproducts/en/MCP3004)
50+
* [MCP3008](http://www.microchip.com/wwwproducts/en/MCP3008)
51+
* [MCP3204](http://www.microchip.com/wwwproducts/en/MCP3204)
52+
* [MCP3208](http://www.microchip.com/wwwproducts/en/MCP3208)

spi/adc/adc.go

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
// InputType defines how an ADC samples the input signal. A single-ended input
13-
// samples its input in the range from the ground (0V) to Vref, or the refence
13+
// samples its input in the range from the ground (0V) to Vref, that is the reference
1414
// input. A 10-bits ADC with a reference input of 5V has a precision of (5 -
1515
// 0) / 1024 = 0.0049V = 4.9mV on single-ended inputs.
1616
//
@@ -24,18 +24,45 @@ type InputType int
2424
const (
2525
// SingleEnded configures the inputs of an ADC as single-ended.
2626
SingleEnded InputType = 0
27+
2728
// PseudoDifferential configures the inputs of an ADC as pseudo-differential.
2829
PseudoDifferential InputType = 1
2930
)
3031

3132
// ADC is the interface that wraps a Read method.
3233
//
33-
// Read returns the voltage of a channel of the ADC.
34+
// Read queries the channel of an ADC and returns it's voltage.
3435
type ADC interface {
3536
Read(channel int) (float64, error)
3637
}
3738

39+
// MCP3004 is 10-bits ADC with 4 single-ended or 2 pseudo-differential inputs.
40+
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295C.pdf
41+
type MCP3004 struct {
42+
Conn *spi.Device
43+
44+
// Vref is the voltage on the reference input of the ADC.
45+
Vref float64
46+
47+
InputType InputType
48+
}
49+
50+
// Read returns the voltage of a channel.
51+
func (m MCP3004) Read(channel int) (float64, error) {
52+
if channel < 0 || channel > 4 {
53+
return 0, fmt.Errorf("channel %d is invalid, ADC has only 4 channels", channel)
54+
}
55+
56+
raw, err := read10(m.Conn, channel, m.InputType)
57+
if err != nil {
58+
return 0, err
59+
}
60+
61+
return (m.Vref / 1024) * float64(raw), nil
62+
}
63+
3864
// MCP3008 is 10-bits ADC with 8 single-ended or 4 pseudo-differential inputs.
65+
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295C.pdf
3966
type MCP3008 struct {
4067
Conn *spi.Device
4168

@@ -47,12 +74,26 @@ type MCP3008 struct {
4774

4875
// Read returns the voltage of a channel.
4976
func (m MCP3008) Read(channel int) (float64, error) {
77+
if channel < 0 || channel > 7 {
78+
return 0, fmt.Errorf("channel %d is invalid, ADC has only 8 channels", channel)
79+
}
80+
81+
raw, err := read10(m.Conn, channel, m.InputType)
82+
if err != nil {
83+
return 0, err
84+
}
85+
86+
return (m.Vref / 1024) * float64(raw), nil
87+
}
88+
89+
// read10 reads a 10 bits value from an channel of an ADC.
90+
func read10(conn *spi.Device, channel int, inputType InputType) (int, error) {
5091
var cmd int
5192

5293
// The first bit after the start bit will determine if the conversion
5394
// is done using single-ended or differential input mode. 0 means
5495
// differential, 1 means single-ended.
55-
if m.InputType == SingleEnded {
96+
if inputType == SingleEnded {
5697
cmd = 1
5798
}
5899
// The bit is then shifted 3 times and the number is incremented with
@@ -77,7 +118,7 @@ func (m MCP3008) Read(channel int) (float64, error) {
77118
// bytes we read 3 bytes.
78119
in := make([]byte, 3)
79120

80-
if err := m.Conn.Tx(out, in); err != nil {
121+
if err := conn.Tx(out, in); err != nil {
81122
return 0, fmt.Errorf("failed to read channel %d: %v", channel, err)
82123
}
83124

@@ -100,7 +141,113 @@ func (m MCP3008) Read(channel int) (float64, error) {
100141
// 00000010 10110111
101142
//
102143
// 00000010 10110111 is 696 in base10.
103-
output := float64(int(in[1]&3)<<8 + int(in[2]))
144+
return int(in[1]&3)<<8 + int(in[2]), nil
145+
}
146+
147+
// MCP3204 is 12-bits ADC with 4 single-ended or 2 pseudo-differential inputs.
148+
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21298e.pdf
149+
type MCP3204 struct {
150+
Conn *spi.Device
104151

105-
return (m.Vref / 1024) * output, nil
152+
// Vref is the voltage on the reference input of the ADC.
153+
Vref float64
154+
155+
InputType InputType
156+
}
157+
158+
// Read returns the voltage of a channel.
159+
func (m MCP3204) Read(channel int) (float64, error) {
160+
if channel < 0 || channel > 4 {
161+
return 0, fmt.Errorf("channel %d is invalid, ADC has only 4 channels", channel)
162+
}
163+
164+
raw, err := read12(m.Conn, channel, m.InputType)
165+
if err != nil {
166+
return 0, err
167+
}
168+
169+
return (m.Vref / 4096) * float64(raw), nil
170+
}
171+
172+
// MCP3208 is 12-bits ADC with 8 single-ended or 4 pseudo-differential inputs.
173+
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21298e.pdf
174+
type MCP3208 struct {
175+
Conn *spi.Device
176+
177+
// Vref is the voltage on the reference input of the ADC.
178+
Vref float64
179+
180+
InputType InputType
181+
}
182+
183+
// Read returns the voltage of a channel.
184+
func (m MCP3208) Read(channel int) (float64, error) {
185+
if channel < 0 || channel > 8 {
186+
return 0, fmt.Errorf("channel %d is invalid, ADC has only 8 channels", channel)
187+
}
188+
189+
raw, err := read12(m.Conn, channel, m.InputType)
190+
if err != nil {
191+
return 0, err
192+
}
193+
194+
return (m.Vref / 4096) * float64(raw), nil
195+
}
196+
197+
// read12 reads a 12 bits value from an channel of an ADC.
198+
func read12(conn *spi.Device, channel int, inputType InputType) (int, error) {
199+
// The start bit.
200+
cmd := 1
201+
cmd = cmd << 1
202+
203+
// The first bit after the start bit will determine if the conversion
204+
// is done using single-ended or differential input mode. 0 means
205+
// differential, 1 means single-ended.
206+
if inputType == SingleEnded {
207+
cmd = 1
208+
}
209+
// The bit is then shifted 3 times and the number is incremented with
210+
// a 3 bits channel.
211+
cmd = cmd << 3
212+
cmd += channel
213+
214+
// The result is shifted 6 times.
215+
//
216+
// x x x x x 1 1 1 1 1 x x x x x x
217+
// | | | | | ------- 3 bits for selecting channel
218+
// | |---------------- 1 bit defining single-ended or pseudo-differential input mode
219+
// |------------------ 1 start bit
220+
cmd = cmd << 6
221+
222+
// The data is is in the first 2 bytes, the third byte is an empty byte.
223+
out := []byte{byte(cmd >> 8), byte(cmd & 0xFF), 0}
224+
225+
// For every byte send the SPI master reads a byte. Because we send 3
226+
// bytes we read 3 bytes.
227+
in := make([]byte, 3)
228+
229+
if err := conn.Tx(out, in); err != nil {
230+
return 0, fmt.Errorf("failed to read channel %d: %v", channel, err)
231+
}
232+
233+
// The 12-bits measurement is at the end of the 3 byte response.
234+
//
235+
// 11111111 11101100 10110111
236+
// ^^^^ ^^^^^^^^
237+
// To get the base10 value of the channel the second byte is masked
238+
// with 15:
239+
//
240+
// 11101100
241+
// 00001111
242+
// -------- &
243+
// 00001100
244+
//
245+
// The byte is shifted 8 bits and the last byte is added:
246+
// 00001100 00000000
247+
// 10110111
248+
// -------- +
249+
// 00001100 10110111
250+
//
251+
// 00001100 10110111 is 3255 in base10.
252+
return int(in[1]&0xF)<<8 + int(in[2]), nil
106253
}

spi/adc/adc_test.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (c testConn) Tx(w, r []byte) error {
3030
}
3131
func (c testConn) Close() error { return nil }
3232

33-
func TestMCP3008(t *testing.T) {
33+
func TestMCP300x(t *testing.T) {
3434
var tests = []struct {
3535
resp []byte
3636
v float64
@@ -44,7 +44,7 @@ func TestMCP3008(t *testing.T) {
4444
for _, test := range tests {
4545
c := testConn{
4646
tx: func(w, r []byte) error {
47-
assert.Equal(t, []byte{1, 240, 0}, w)
47+
assert.Equal(t, []byte{1, 176, 0}, w)
4848

4949
r[1] = test.resp[0]
5050
r[2] = test.resp[1]
@@ -54,14 +54,69 @@ func TestMCP3008(t *testing.T) {
5454
}
5555

5656
con, _ := spi.Open(&testDriver{c})
57-
m := MCP3008{
57+
mcp3004 := MCP3004{
5858
Conn: con,
5959
Vref: 5.0,
6060
InputType: SingleEnded,
6161
}
6262

63-
v, _ := m.Read(7)
63+
v, _ := mcp3004.Read(3)
6464
assert.Equal(t, test.v, v)
65+
66+
mcp3008 := MCP3008{
67+
Conn: con,
68+
Vref: 5.0,
69+
InputType: SingleEnded,
70+
}
71+
72+
v, _ = mcp3008.Read(3)
73+
assert.Equal(t, test.v, v)
74+
}
75+
}
76+
77+
func TestMCP320x(t *testing.T) {
78+
var tests = []struct {
79+
resp []byte
80+
v float64
81+
}{
82+
{[]byte{0, 0}, 0},
83+
{[]byte{2, 0}, 0.625},
84+
{[]byte{6, 0}, 1.875},
85+
{[]byte{1, 13}, 0.328369140625},
86+
{[]byte{255, 255}, 4.998779296875},
87+
}
88+
89+
for _, test := range tests {
90+
c := testConn{
91+
tx: func(w, r []byte) error {
92+
assert.Equal(t, []byte{4, 192, 0}, w)
93+
94+
r[1] = test.resp[0]
95+
r[2] = test.resp[1]
96+
97+
return nil
98+
},
99+
}
100+
101+
con, _ := spi.Open(&testDriver{c})
102+
mcp3204 := MCP3204{
103+
Conn: con,
104+
Vref: 5.0,
105+
InputType: PseudoDifferential,
106+
}
107+
108+
v, _ := mcp3204.Read(3)
109+
assert.Equal(t, test.v, v)
110+
111+
mcp3208 := MCP3208{
112+
Conn: con,
113+
Vref: 5.0,
114+
InputType: PseudoDifferential,
115+
}
116+
117+
v, _ = mcp3208.Read(3)
118+
assert.Equal(t, test.v, v)
119+
65120
}
66121
}
67122

@@ -81,6 +136,9 @@ func ExampleMCP3008() {
81136
a := MCP3008{
82137
Conn: conn,
83138
Vref: 5.0,
139+
140+
// Optional, default value is SingleEnded.
141+
InputType: PseudoDifferential,
84142
}
85143

86144
// Read the voltage on channel 3.

0 commit comments

Comments
 (0)