public
Last active

Raspbery Pi Analog Input with MCP3008

  • Download Gist
adafruit_mcp3008.py
Python
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
#!/usr/bin/env python
import time
import os
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
DEBUG = 1
 
# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
if ((adcnum > 7) or (adcnum < 0)):
return -1
GPIO.output(cspin, True)
 
GPIO.output(clockpin, False) # start clock low
GPIO.output(cspin, False) # bring CS low
 
commandout = adcnum
commandout |= 0x18 # start bit + single-ended bit
commandout <<= 3 # we only need to send 5 bits here
for i in range(5):
if (commandout & 0x80):
GPIO.output(mosipin, True)
else:
GPIO.output(mosipin, False)
commandout <<= 1
GPIO.output(clockpin, True)
GPIO.output(clockpin, False)
 
adcout = 0
# read in one empty bit, one null bit and 10 ADC bits
for i in range(12):
GPIO.output(clockpin, True)
GPIO.output(clockpin, False)
adcout <<= 1
if (GPIO.input(misopin)):
adcout |= 0x1
 
GPIO.output(cspin, True)
adcout >>= 1 # first bit is 'null' so drop it
return adcout
 
# change these as desired - they're the pins connected from the
# SPI port on the ADC to the Cobbler
SPICLK = 18
SPIMISO = 23
SPIMOSI = 24
SPICS = 25
 
# set up the SPI interface pins
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)
 
# 10k trim pot connected to adc #0
potentiometer_adc = 0;
 
last_read = 0 # this keeps track of the last potentiometer value
tolerance = 5 # to keep from being jittery we'll only change
# volume when the pot has moved more than 5 'counts'
 
while True:
# we'll assume that the pot didn't move
trim_pot_changed = False
 
# read the analog pin
trim_pot = readadc(potentiometer_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
# how much has it changed since the last read?
pot_adjust = abs(trim_pot - last_read)
 
if DEBUG:
print "trim_pot:", trim_pot
print "pot_adjust:", pot_adjust
print "last_read", last_read
 
if ( pot_adjust > tolerance ):
trim_pot_changed = True
 
if DEBUG:
print "trim_pot_changed", trim_pot_changed
 
if ( trim_pot_changed ):
set_volume = trim_pot / 10.24 # convert 10bit adc0 (0-1024) trim pot read into 0-100 volume level
set_volume = round(set_volume) # round out decimal value
set_volume = int(set_volume) # cast volume as integer
 
print 'Volume = {volume}%' .format(volume = set_volume)
set_vol_cmd = 'sudo amixer cset numid=1 -- {volume}% > /dev/null' .format(volume = set_volume)
os.system(set_vol_cmd) # set volume
 
if DEBUG:
print "set_volume", set_volume
print "tri_pot_changed", set_volume
 
# save the potentiometer reading for the next loop
last_read = trim_pot
 
# hang out and do nothing for a half second
time.sleep(0.5)

When I run this I frequently get half-integer values, e.g. 1023.5. I think this part is incorrect:

        adcout = 0
        # read in one empty bit, one null bit and 10 ADC bits
        for i in range(12):
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)
                adcout <<= 1
                if (GPIO.input(misopin)):
                        adcout |= 0x1

We already cycled the clock after sending the command, so the empty bit (allowed to be anything) is on MISO. First time through the loop, we cycle the clock again, so first time we actually read MISO it's guaranteed to be null. Then we read another 11 bits and store them in adcout, which is one too many. When you read too many bits, the 3008 starts sending them in reverse order; it only sends the LSB twice, so the final bit we read is going to be the same as the bit two before that, which is the second-least significant bit of the actual value. (This is what the datasheet says should happen, and I put in a debug print to confirm it.)

We later divide by two, but this apparently assumes the final bit read would be 0, which isn't guaranteed. Replacing the /= 2 with >>= 1 would fix it, but it's cleaner to remove that line completely and only loop in range(11) when setting up adcout.

I'm assuming that

adcout >>= 1 # first bit is 'null' so drop it

is a erroneously commented correction for the mistake mentioned by ChickenProp above

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.