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.