-
-
Save ladyada/3151375 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
# Written by Limor "Ladyada" Fried for Adafruit Industries, (c) 2015 | |
# This code is released into the public domain | |
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) |
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
Why aren't you using the SPI hardware? raspi-config
can enable it, so it's not hard. It's easy to use from C, and I HATE C. Bit banging SPI doesn't teach anyone how to use SPI, IMHO.
Has anyone ported the code to use the SPI interface?
Thank,
Ralf
Hey guys!
take a look at my library that uses SpiDev to communicate with the chip.
Reviving this because I think the issue ChickenProp raised in 2012 still remains. Reading 12 bits in that loop is indeed 1 too many, and a right shift or, equivalently, division by 2 is misleading, as is the comment that this is a "null" bit. As someone learning about SPI, this mysterious shift confused me for 2 solid days. The empty bit is already on the bus after the command, as ChickenProp states, so you only need to read out 11 bits: 1 null and 10 valid ADC bits. Sure, you can read the additional bit and shift it out after, but what's the point?
You have to follow these instructions or else the account blocks the script.
"> If the tips above didn't help, visit https://www.google.com/accounts/DisplayUnlockCaptcha and follow the steps on the page."
Can you make the print
statements compatible with Python 2.7/3.x?
When I run this I frequently get half-integer values, e.g. 1023.5. I think this part is incorrect:
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 inrange(11)
when setting upadcout
.