MCP3008 eight-channel ADC
Eight 10-bit analog inputs on SPI in a 16-pin DIP: how a Raspberry Pi reads a potentiometer or a light sensor.
The MCP3008 is Microchip's analog-to-digital converter that gpiozero and most Raspberry Pi tutorials use, because a Pi has no analog inputs of its own. It works on any board with an SPI bus. Aliases: MCP3004 (its four-channel sibling), 10-bit ADC, analog to digital converter.
Pins
Pin 1 is next to the notch, and the numbers run counter-clockwise seen from above: along the bottom row left to right, then back along the top.
| Pin | Name | What it does |
|---|---|---|
| 1 to 8 | CH0 to CH7 |
The eight inputs. |
| 9 | DGND |
Digital ground. |
| 10 | CS |
Chip select, active low: a conversion starts when it falls. |
| 11 | DIN |
Data in, from the board's MOSI. |
| 12 | DOUT |
Data out, to the board's MISO. High-Z while CS is high. |
| 13 | CLK |
The SPI clock. |
| 14 | AGND |
Analog ground: inputs and VREF are measured from here. |
| 15 | VREF |
The reference: the voltage that reads 1024. |
| 16 | VDD |
Supply, 2.7 to 5.5 V. |
On a Raspberry Pi, CLK goes to GPIO11 (pin 23), DOUT to GPIO9 (pin 21),
DIN to GPIO10 (pin 19) and CS to GPIO8 (pin 24, CE0), with VDD and
VREF on 3.3 V. Powering it from 3.3 V keeps DOUT at the Pi's own level.
A reading
The code is 1024 × Vin ÷ VREF, rounded down and held at 1023, so with
VREF on 3.3 V each count is 3.2 mV. A potentiometer across the rails, turned
three quarters of the way, reads 767.
In differential mode an input is measured against its pair (CH0 against CH1, CH2 against CH3, and so on), and a negative difference reads 0.
From Python
gpiozero's MCP3008 reads a channel through spidev:
from gpiozero import MCP3008
from time import sleep
pot = MCP3008(channel=0)
while True:
print(pot.value, pot.raw_value, pot.voltage)
sleep(0.5)
spidev does the same by hand: send 0x01, then 0x80 with the channel in
the next four bits, then a zero, and the answer is the low two bits of the
second byte and all of the third.
import spidev
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1350000
r = spi.xfer2([1, (8 + 0) << 4, 0])
print(((r[1] & 3) << 8) + r[2])
On a Pico with MicroPython, machine.SPI(0) with chip select on an ordinary
pin reads it the same way.
What the model gets right
The conversion as the datasheet draws it: the start bit, the single-ended or
differential bit and three channel bits clocked in on rising edges, the input
sampled until the falling edge of the fifth clock after the start bit, the
null bit, the ten bits most significant first on falling edges, then the
bits again least significant first while the clock runs on. DOUT lets go
when CS rises. SPI modes 0,0 and 1,1 both work.
What it does not
The sample-and-hold's 20 pF and its settling time, the clock-rate limits (3.6 MHz at 5 V, 1.35 MHz at 2.7 V: a faster clock reads right here and may not on a desk), and the converter's nonlinearity.
See it in action
The MQ-2 gas sensor on a Pi reads a 5 V sensor through a
10 k and 20 k divider on CH0.