MCP3002 example code for Raspberry Pi (ADC through SPI)

26 September, 2012

I’ve been tinkering with my Raspberry Pi for quite a few weeks now. It’s a great little thing. Like many, though, I’ve been a little frustrated with the lack of analogue inputs into the Pi. I was for a while using a barebones arduino to service serial comms from the Pi but this had it’s own limitations. (One day I’ll post the code I used for that on this blog!). Adafruit industries have been doing a great job of promoting the use of the Pi to hobbyists and showing examples of hardware control. One of these was to control the SPI controlled 8 channel ADC chip known as the MCP3008 from Microchip. Find it here!

In my naivety I thought that the control would be exactly the same for its 2 channel sibling, the MCP3002. I was wrong! It’s still an SPI chip and when I started to delve into the datasheets it was relatively straight forward to modify the code. I thought I’d post that here so others could make use of it. You’ll notice I haven’t changed the meat of the code and all credit goes to Adafruit for their hard work on which I build!

The code:

#!/usr/bin/env python

# just some bitbang code for testing the 2 channels

import RPi.GPIO as GPIO, time, os

DEBUG = 1
GPIO.setmode(GPIO.BCM)

# read SPI data from MCP3002 chip, 2 possible adc's (0 thru 1)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
    if ((adcnum > 1) or (adcnum < 0)):
        return -1
	if (adcnum == 0):
            commandout = 0x6
        else:
            commandout = 0x7
	GPIO.output(cspin, True)

	GPIO.output(clockpin, False)  # start clock low
	GPIO.output(cspin, False)     # bring CS low

	#commandout = 0x6  #start bit and 1, 0 to select single ended ch0
	commandout <<= 5    # we only need to send 3 bits here
	for i in range(3):
		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 /= 2       # first bit is 'null' so drop it
	return adcout

# change these as desired
SPICLK = 11
SPIMOSI = 9
SPIMISO = 10
SPICS = 18

# 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)
adcnum = 0

# Note that bitbanging SPI is incredibly slow on the Pi as its not
# a RTOS - reading the ADC takes about 30 ms (~30 samples per second)
# which is awful for a microcontroller but better-than-nothing for Linux

while True:
    print "------------"
    for adcnum in range(2):
        ret = readadc(adcnum, SPICLK, SPIMOSI, SPIMISO, SPICS)
        print adcnum, ": ",ret
    print "------------"
Advertisements

Wifly Working at Last!

25 September, 2012

After several attempts to get my Wifly-rn-xv working I’ve finally succeeded. The documentation seems good at first glance but when you get into it you realise information is missing. To be fair I got the initial connection working with a combination of the command datasheet and the following blog: http://www.tinkerfailure.com/2012/02/setting-up-the-wifly-rn-xv/

Thanks Tinker! Check out his other posts including using PHP to read the results. That’s exactly what I wanted to do. Take an analogue input voltage from a temperature sensor and record it using the web server. I struggled a lot though until I read the following posts on the sparkfun forum: https://forum.sparkfun.com/viewtopic.php?f=13&t=32719

It turns out there are some subtleties to all this, including an unseen resistor network before the reading is measured and least significant bits being stripped in the GET request. In the end, inspired by one of the posters I decided to use a multimeter and the measurement command and do my own linear regression. This actually worked out pretty well and the measurements are very accurate now.

So I had input voltages ranging from 0 to 1V in ~100mV increments and had the decimal value at the command line of 34,500 (0x08698 -> ~0x086A via HTTP GET) to 183,800 (0x2cf3e), supposedly in uVs. That gave me a slope and offset to play with. It turns out the readings were thankfully very linear.

I used the Sparkfun named pin ADC0, which is pin 20, but called SEN 2 at the wifly. At the command line, to read this input use the command: “show q 2”.

I hope this makes sense to someone else who is having a bit of a struggle!

One day I might try the UART data via HTTP option – but not until I can face it!