MCP3XXX
An Arduino Library supporting the MCP3XXX series of ADC SPI chips, including MCP3002, MCP3004, MCP3008 and others.
MCP3XXX.h
1 //
2 // Copyright (c) 2018 Christopher Baker <https://christopherbaker.net>
3 //
4 // SPDX-License-Identifier: MIT
5 //
6 
7 
8 #pragma once
9 
10 
11 #include <Arduino.h>
12 #include <SPI.h>
13 
14 
21 template<uint8_t NumBits,
22  uint8_t NumChannels,
23  uint32_t MaxSPIClockSpeed,
24  uint8_t SPITransferLength = 3>
25 class MCP3XXX_
26 {
27 public:
28 
29  enum
30  {
33 
36 
38  NUM_BITS = NumBits,
39 
41  BIT_MASK = (1 << NUM_BITS) - 1,
42 
44  NUM_CHANNELS = NumChannels,
45 
47  MAX_SPI_CLOCK_SPEED = MaxSPIClockSpeed,
48 
50  SPI_TRANSFER_LEGNTH = SPITransferLength
51  };
52 
53 
56  {
57  }
58 
61  {
62  }
63 
71  void begin(uint8_t csPin = SS)
72  {
73  _useHardwareSPI = true;
74 
75  _csPin = csPin;
76  _mosiPin = MOSI;
77  _misoPin = MISO;
78  _sckPin = SCK;
79 
80  // Set up pin modes.
81  pinMode(_csPin, OUTPUT);
82 
83  // Begin software SPI.
84  // Initializes the SPI bus by setting SCK, MOSI, and SS to outputs,
85  // pulling SCK and MOSI low, and SS high.
86  digitalWrite(_csPin, HIGH); // Redundant.
87  SPI.begin();
88  }
89 
100  void begin(uint8_t csPin, uint8_t mosiPin, uint8_t misoPin, uint8_t sckPin)
101  {
102  _useHardwareSPI = false;
103 
104  _csPin = csPin;
105  _mosiPin = mosiPin;
106  _misoPin = misoPin;
107  _sckPin = sckPin;
108 
109  // Set up pin modes manually.
110  pinMode(_csPin, OUTPUT);
111  pinMode(_mosiPin, OUTPUT);
112  pinMode(_misoPin, INPUT);
113  pinMode(_sckPin, OUTPUT);
114 
115  // Begin software SPI. We initiate CS Pin HIGH to prepare it to go LOW
116  // on our first read.
117  digitalWrite(_csPin, HIGH);
118  }
119 
126  uint32_t analogRead(uint8_t channel) const
127  {
128  if (channel < NUM_CHANNELS)
129  return _read(channel, false);
131  }
132 
144  uint32_t analogReadDifferential(uint8_t inPositiveChannel) const
145  {
146  if (inPositiveChannel < NUM_CHANNELS)
147  return _read(inPositiveChannel, true);
149  }
150 
152  size_t numChannels() const
153  {
154  return NUM_CHANNELS;
155  }
156 
158  size_t numBits() const
159  {
160  return NUM_BITS;
161  }
162 
163 private:
164  MCP3XXX_(const MCP3XXX_&);
165  MCP3XXX_& operator = (const MCP3XXX_&);
166 
170  uint32_t _read(uint8_t channel, bool differential) const
171  {
172  // Data transfers are done using "8-bit segments" approach in data sheet.
173  // The sent data alignment resuls in correctly aligned return bytes after
174  // the SPI transfer.
175  uint8_t data[SPI_TRANSFER_LEGNTH];
176 
177  // Check for MCP3004
178  if (NUM_CHANNELS == 2)
179  {
180  if (NUM_BITS == 10)
181  {
182  // Start bit.
183  data[0] = 0b01000000;
184  // Differential bit.
185  data[0] |= (differential ? 0b00000000 : 0b00100000);
186  // Channel bit.
187  data[0] |= (channel == 0 ? 0b00000000 : 0b00010000);
188  // MSBF bit is set to 1. See section 5.1 of the data sheet.
189  data[0] |= 0b00001000;
190  // It doesn't matter what data[1] is set to.
191  }
192  else
193  {
195  }
196  }
197  else
198  {
199  if (NUM_BITS == 10)
200  {
201  // The start bit. We position it here to align our output data.
202  data[0] = 0b00000001;
203  // Set the differential / single bit and the channel bits.
204  data[1] = (differential ? 0b00000000 : 0b10000000) | (channel << 4);
205  // It doesn't matter what data[2] is set to.
206  }
207  else
208  {
210  }
211  }
212 
213  if (_useHardwareSPI)
214  {
215  // Here we replace the sent data with the received data.
216  SPI.beginTransaction(SPISettings(MAX_SPI_CLOCK_SPEED, MSBFIRST, SPI_MODE0));
217  digitalWrite(_csPin, LOW);
218  for (size_t i = 0; i < SPI_TRANSFER_LEGNTH; ++i)
219  {
220  data[i] = SPI.transfer(data[i]);
221  }
222  digitalWrite(_csPin, HIGH);
223  SPI.endTransaction();
224  }
225  else
226  {
227  // Slower, but can use any pin.
228  // We could save a few operations by skipping some digitalWrites(),
229  // using bitwise operators and doing direct port-manipulation.
230  // But this is used because it is "easier" to read.
231  digitalWrite(_csPin, LOW);
232  for (size_t i = 0; i < SPI_TRANSFER_LEGNTH; ++i)
233  {
234  for (size_t j = 8; j-- > 0;)
235  {
236  // Set MOSI data.
237  digitalWrite(_mosiPin, bitRead(data[i], j));
238  // Set Clock HIGH.
239  digitalWrite(_sckPin, HIGH);
240  // Read MISO data.
241  bitWrite(data[i], j, digitalRead(_misoPin));
242  // Set Clock LOW.
243  digitalWrite(_sckPin, LOW);
244  }
245  }
246  digitalWrite(_csPin, HIGH);
247  }
248 
249  // Here we take the second two bytes returned as our value.
250  // This value is already correctly aligned since we are using the 8-bit
251  // segments approach. The BIT_MASK is calculated based on the number out
252  // bits specified in the template parameters.
253  return ((data[SPI_TRANSFER_LEGNTH - 2] << 8) | data[SPI_TRANSFER_LEGNTH - 1]) & BIT_MASK;
254  }
255 
256 
258  bool _useHardwareSPI = true;
259 
261  uint8_t _csPin = SS;
262 
264  uint8_t _mosiPin = MOSI;
265 
267  uint8_t _misoPin = MISO;
268 
270  uint8_t _sckPin = SCK;
271 
272 };
273 
279 
285 
291 
292 // /// \brief A typedef for the MCP3202.
293 // /// Max clock frequency for 2.7V: 900000 Hz
294 // /// Max clock frequency for 5.0V: 1800000 Hz
295 // /// \sa http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf
296 // typedef MCP3XXX_<12, 2, 900000> MCP3202;
297 //
298 // /// \brief A typedef for the MCP3204.
299 // /// Max clock frequency for 2.7V: 1000000 Hz
300 // /// Max clock frequency for 5.0V: 2000000 Hz
301 // /// \sa http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf
302 // typedef MCP3XXX_<12, 4, 1000000> MCP3204;
303 //
304 // /// \brief A typedef for the MCP3208.
305 // /// Max clock frequency for 2.7V: 1000000 Hz
306 // /// Max clock frequency for 5.0V: 2000000 Hz
307 // /// \sa http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf
308 // typedef MCP3XXX_<12, 8, 1000000> MCP3208;
309 //
310 // /// \brief A typedef for the MCP3208.
311 // /// Max clock frequency for 2.7V: 1050000 Hz
312 // /// Max clock frequency for 5.0V: 2100000 Hz
313 // /// \sa http://ww1.microchip.com/downloads/en/DeviceDoc/21697e.pdf
314 // typedef MCP3XXX_<13, 8, 1050000> MCP3304;
size_t numChannels() const
Definition: MCP3XXX.h:152
void begin(uint8_t csPin=SS)
Set up the ADC using default hardware SPI pins.
Definition: MCP3XXX.h:71
MCP3XXX_()
Construct a default MCP3XXX_ device.
Definition: MCP3XXX.h:55
Number of input channels.
Definition: MCP3XXX.h:44
uint32_t analogReadDifferential(uint8_t inPositiveChannel) const
Read a differential analog value by specifying the IN+ channel.
Definition: MCP3XXX.h:144
void begin(uint8_t csPin, uint8_t mosiPin, uint8_t misoPin, uint8_t sckPin)
Set up the ADC using custom software SPI pins.
Definition: MCP3XXX.h:100
ADC error value.
Definition: MCP3XXX.h:32
Number of ADC bits.
Definition: MCP3XXX.h:38
uint32_t analogRead(uint8_t channel) const
Read the analog value.
Definition: MCP3XXX.h:126
ADC error value.
Definition: MCP3XXX.h:35
~MCP3XXX_()
Destroy the MCP3XXX_ device.
Definition: MCP3XXX.h:60
Maximum SPI communication speed rate in Hz.
Definition: MCP3XXX.h:47
The number of bytes transferred over SPI.
Definition: MCP3XXX.h:50
A bit mask based on the number of bits.
Definition: MCP3XXX.h:41
size_t numBits() const
Definition: MCP3XXX.h:158
A template class supporting MCP3XXX ADC SPI chips.
Definition: MCP3XXX.h:25