RSS YouTube LinkedIn Twitter Facebook


MicroChip MCP3204/3208 ADC

by bsmithyman528

  • Status: Under development
  • Downloads: 152
  • Licence: BSD
  • Last updated: 13/Sep/2010 at 04:41 PM

Version 1

Size: 7.18kb

View older versions

Project Overview

Re-posting of a package I put together last year on XMOSlinkers.

The MCP3208 is a 12-bit SAR ADC from MicroChip. The 3208 has 8 input pins, multiplexed to a 100 ksps ADC. The MCP3204 uses the same instructions, but has 4 input pins.

This is a short library to allow running the ADC over SPI; I've implemented in-thread functions, but also a separate thread that periodically sends samples from any/all multiplexed pins through a channel.


Verified to run on

Images and diagrams

Rate this project

Average: 4 (2 votes)
Your rating: None
Login or register to vote

Comments / Updates


So with help from Jon Ferguson from XMOS I now have demo_adc() working. I have placed the code on gitorious where you can easily see the changes:

The crucial change to make it work is "some time with SS high" before the main demo loop on line 50 of mcp3208.xc.

Hi kasbah,

I think I understand what you mean. What if you connect the sampling lines (channels 1-8) of the ADC to ground or Vref? That's what I was referring to: if you force a high or low on the analog input, do you get any data out?

On mine I have a 4.7k pullup to 5V on the chip select line, and everything else is connected directly to the XMOS pins on the digital end. I have the digital and analog grounds connected together, and the 5V power line and Vref connected by a jumper. You can see on my board that there are 8 pins with both male and female headers attached, and then two more where the female headers are on the opposite side. The 8 are the input lines, and the other two are ground and Vref.

Also don't forget you can breadboard it (perhaps you already are). I find a 16-connector ribbon cable very handy (I put a breadboard-compatible dip connector on one end, so it breaks out all 16 lines from one of the XMOS banks).

By high I mean a value of 8192. When I connect Dout (miso) to ground I get 0 as expected.

Hi Kasparb,

So yeah, I've got a capacitor from Vref to Agnd to keep things stable, and the 4.7 k pullup. I'm actually running the ADC at 5V to support higher sample rates. I'm not sure I'd use mine as an example of the ideal layout, but it does seem to work. Note there's also a jumper that optionally connects the Vref line to the 5V line, which is included in the photos. I find that it works pretty well, in my experience, but I haven't played with it very much lately, so I may be mis-remembering how I got it to work. I'll try to take a look shortly.

To clarify, what do you mean by values that are too high? If you connect a data line to ground / vref do you get the full-scale values on the ADC output?

So, I managed to get the mcp3208 to work with an NXP mbed board. The board has dedicted SPI pins and functions. I still can't get it to work with the XC-1. Same problem: always high values.

Ok so for now I got my hands on a 3208 and am just trying to make it work with that before I modify anything. I am having some trouble though. I am running main with just demo_adc() and I always get a value of 8192 back.

I am running the chip of 3v3 and using 3v3 as reference. I have tried a few different bypass capacitor configurations what configuration would you recommend? From your flickr it seems like you have one cap going from Vdd to Vref and one from Vref to Agnd. Is this correct and what values are they? What is a sensible value for the pullup on CS? It seemed like you were using 4.7k is that right?

Hi kasbah,

I haven't looked at it much since I wrote it last year; I'll write out some thoughts as I read through it:

  • The init_adc() function just sets up the ports - the slave select/chip select line is active low.
  • When you call read_adc() you pass the channel number (out of 8) which is cast as an 8-bit integer. In hindsight, it might be faster to pass it as a 32-bit integer and cast inside the function, because of the way the masking instructions work.
  • The preamble is made up of the binary constant 0b01000000 and the ADC channel integer. The channel is shifted two bits left (i.e. it occupies the x characters in 0b010xxx00). This whole thing is reversed, but it's actually a 32-bit integer (because that's what the bitrev instruction works on), so it's then shifted by 24 bits back to the lowest 8.
  • The mosi line is driven with each bit in the preamble. The sclk time is set to zero and timestamped (this is based on the port counter, not one of the timers). Time is incremented by the SPI_PERIOD, and the clock is driven high, another SPI_PERIOD, and then low again. After each iteration of the loop, the sync() makes sure that the program and the port are synchronized.
  • The next block is 13 bits long. This works basically the same way, except now we're reading from the port. We read when the clock is high (hence the sync() call) and the input) and then finish clocking to the next cycle.
  • The conversation is finished by setting the select line to high again (recall it's active low).
  • We clocked the data from the ADC into value; the rest of the bits were explicitly set to zero at the beginning. Since the bits were clocked in at the high end, we bit reverse the value and cast it as an unsigned short int (16-bit integer). Again, this cast could probably be removed to speed things up a bit, since the channel buffer is 32 bits anyway.

So the conversation is 20 bits (i.e. 7 + 13), and the first 7 contain all of the information needed for configuration. Note that there are zeros in the sequence that correspond to setting up the differential operation of the ADC, which I didn't implement. The remaining 13 bits include the 12 data bits and a dummy bit that's clocked out first to give the ADC time to settle.

It looks to me like the whole message on the 3208 is _17 bits_ and 16 bits on the 3202. The only difference seems to be the Din line:

3202:[ Start ]__[SGL/DIFF]__[ODD/SIGN]__[ MSBF ]
3208:[ Start ]__[SGL/DIFF]__[ D0 ]__[ D1 ]__[ D2 ]

I am trying to figure out why your preamble is 7-bits. Sorry but I am quite new to all of this.

Kasbah asked a good question: will this work for the MCP3202?
Short answer is it will need some minor modifications, but nothing too strenuous. From the datasheets, it looks like the MCP3202 uses a 16-bit long message, and the 3204/3208 uses an 18-bit long message . Basically they're the same internally, except that the multiplexer on the 3208 needs additional control bits to handle the extra channels (and they have to come before the 12 conversion bits). The whole conversation happens in 18 clocks on the 3204/3208 and in 16 clocks on the 3202 (page 20 and 17 of their respective datasheets).
I'd be happy to figure it out, and/or put together a bigger project if people are interested in grouping some of the ADC/DAC codes out there.
Edited: Misread one of the datasheets; I corrected an error.

This is the original version, but I haven't really made any changes. It should work on pretty much any XMOS board, with pin reassignment.

oogely boogely