Raspberry Alpha Omega

Raspberry Pi from start to finish

Reading analogue data on a Raspberry Pi using MCP3002

Jun 23, 2013 - 5 minute read -

I got a bit disappointed with my little bus pirate yesterday. I'll investigate how to update it to fix the odd SPI behaviour another time, but for now, its on to trying to read the MCP3002 SPI ADC chip directly using the Raspberry Pi. However, this one I'm going to do a bit differently. My desk is filling up with breadboards and floating wires, and working out each time which wire to connect to which header pin is becoming annoying. So I have decided to wire up a simple plugin board with the MCP3002 connected to the correct SPI pins on a 26-way header. That way I can just plug it in when I want to use it, and unplug it if I want to use the rspberry Pi for something else.

I have a fairly diverse collection of prototype boards, some with strips, some with grouped pads and some with just a grid (a.k.a. "pegboard", "pad per hole", etc.) of tinned holes. For this little project I thought I'd have a go at using one of the "pad per hole" boards. They are very cheap, particularly when ordered from China, so I'm not too worried if I mess it up. One of my collection has a grid of 14 x 20 holes. This is almost exactly the right width for the 2x13 Raspberry Pi GPIO header and it looks like it will sit nicely on top of the Raspberry Pi. My plan is to add a socket for the MCP3002 (just in case I suddenly want to use it for something else) and wire the socket and the GPIO header together.

While thinking about how to lay out the board, I found that if I put the IC socket on the "header" side of the board, and arrange it in the right way, the MOSI, MISO and CLK pins line up with each other. That certainly makes wiring simpler, so that's the way I have decided to do it.

Of course, I can't resist adding a little bit of extra. The Raspberry Pi provides two SPI /CS lines (labelled CE0 and CE1). Although I only plan to use CE0 at the moment, I thought I might as well add a little pin-and-jumper switch to allow me to choose which one to use without re-soldering. Also, I guess it makes sense to add a header to connect whatever the ADC will be reading. This one will provide GND, Vcc and a signal input pin. Other than that, the circuit is very simple, mainly a matter of connecting the right Raspberry Pi headers to the right MCP3002 pins.

When it comes to putting it together, that's a different matter. I learned my soldering and circuit-building in the late 1970s and early 1980s when Veroboard was king and only the rich kids who read "elektor" magazine would dream of using a printed circuit board. I never encountered any of these pad-per-hole boards at that time, though, and I quickly realized that I was unsure how to use it! Luckily the internet is full of helpful people, and this video gave me enough confidence to give it a go.

After soldering and double-checking all the connections (by eye, and also with a multimeter to check for accidental shorts and bridges) I was ready to try it out. I plugged the MCP3002 chip into its socket, added the jumper to select CE0 and connected my resistor divider (still on the breadboard) to the analogue input connector. However, I hit a problem straight away. Although I had checked the heights of components on the board to make sure that they did not foul the Raspberry Pi main board, I had not allowed for the extra height of the connectors I was using to wire the analogue input. They were only a few mm too high, but it was enough to prevent the expansion board from sitting properly on the Raspberry Pi. As a quick and dirty fix I just plugged in another header which boosted the height by about 1cm. If this board seems useful, I'll probably replace the analogue input header with a right-angle one for the future.

The next step was to try out the software. To start with I just re-used some of the software bits I had built for driving a 7219 LED matrix from a Raspberry Pi and sent the intended 0x60 0x00 to the board, while monitoring the traffic on the logic analyser. This looked like plausible SPI traffic, so I changed one of the resistors and tried again. Reassuringly different!

Now that I was fairly comfortable that the hardware was working, I just needed to tweak the software to not only send the right SPI data, but also to retrieve and interpret the response:

Note that the SPI protocol "returns" the same number of bits as sent, and in this case the 10-bit ADC data is encoded in the last two bits of the first byte and the eight bits of the second byte. I chose to retrieve the data in MSB-first order, so the result is the sum of (the bottom two bits of the first byte shifted up by eight bits) + the second byte.

#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define CHANNEL 0

uint8_t buf[2];

unsigned int sample(int channel) {
	buf[0] = 0x60 + channel;
	buf[1] = 0x00;
	wiringPiSPIDataRW(CHANNEL, buf, 2);
	return buf[1] + (buf[0] & 0x03) << 8;
}

void main(int argc, char** argv) {
	if (wiringPiSPISetup(CHANNEL, 4000000) < 0) {
		fprintf (stderr, "SPI Setup failed: %s\n", strerror (errno));
		exit(errno);
	}

	printf("sample=%04x\n", sample(0));
}

That should do for now. Next time I'll take a look at what I can do to use this chip to read changing signals rather than a fixed voltage.

As usual, all code, including a makefile to build it, can be found on github.

ADC analogue breadboard C circuit hardware mcp3002 pegboard perfboard resistor soldering SPI


Comments

  1. Franz

    Hi did I a mistake or why i don't get the right reply back. I use a MCP2008 and my code looks like that:

    #define CHANNEL 0
    uint8_t buf[8];
    unsigned int sample(int channel) {
    	buf[0] = 0x60 + channel;
    	buf[1] = 0x00; buf[2] = 0x00;
    	buf[3] = 0x00; buf[4] = 0x00;
    	buf[5] = 0x00; buf[6] = 0x00;
    	buf[7] = 0x00; wiringPiSPIDataRW(CHANNEL, buf, 8);
    	return buf[1] + (buf[0] & 0x03) << 8;
    }
    void main(int argc, char** argv) {
    	if (wiringPiSPISetup(CHANNEL, 4000000) < 0) {
    		fprintf (stderr, "SPI Setup failed: %sn", strerror (errno));
    		exit(errno);
    	}
    	printf("sample=%04xn", sample(0));
    }
    

  2. Guy

    Hi all, please excuse me as this is a bit off topic, but google lead me to this thread. I need to create a switch that is closed with a delta in milivolts. However the voltage might range from 1.65x volts with only a couple of mv delta. I would love to use an arduino or your a raspberry pi as I can do it in software, but I can't interface to any external system. I found this thread based on the MCP3002. Again please excuse this reply as it is off topic, but you guys obviously understand A to D conversion and I need assistance. Thanks :)

Leave a Reply

Your email address will not be published.