MCP3208

The MCP3208 is a 12bit, 8 channel, Serial Peripheral Interface (SPI) Analogue to Digital Converter. It comes as an 16 pin DIP (also soic) pinout below, get the datasheet here. Also available as the 3201/2/4 with 1, 2 and 4 channels.

NOTE: code is for single ended operation, read the datasheet for differential operation.

D10 indicates arduino digital pin 10 etc.

Pinout:

  • 1-8 - chan 0-7 -> the 8 levels to be measured
  • 9 DGND -> GND
  • 10 CS chip select -> D10
  • 11 Din MOSI -> D11
  • 12 Dout MISO -> D12
  • 13 CLC clock -> D13
  • 14 AGN -> GND
  • 15 Vref -> reference voltage (that gives max adc reading)
  • 16 Vdd -> supply voltage, max 5.5V so Arduino 5V is fine
   ___ 
1 | u | 16 
2 |   | 15 
3 |   | 14 
4 |   | 13 
5 |   | 12 
6 |   | 11 
7 |   | 10 
8 |___| 9 

Code:

#define SELPIN 10 //Selection Pin 
#define DATAOUT 11//MOSI 
#define DATAIN  12//MISO 
#define SPICLOCK  13//Clock 
int readvalue; 

void setup(){ 
 //set pin modes 
 pinMode(SELPIN, OUTPUT); 
 pinMode(DATAOUT, OUTPUT); 
 pinMode(DATAIN, INPUT); 
 pinMode(SPICLOCK, OUTPUT); 
 //disable device to start with 
 digitalWrite(SELPIN,HIGH); 
 digitalWrite(DATAOUT,LOW); 
 digitalWrite(SPICLOCK,LOW); 

 Serial.begin(9600); 
} 

int read_adc(int channel){
  int adcvalue = 0;
  byte commandbits = B11000000; //command bits - start, mode, chn (3), dont care (3)

  //allow channel selection
  commandbits|=((channel-1)<<3);

  digitalWrite(SELPIN,LOW); //Select adc
  // setup bits to be written
  for (int i=7; i>=3; i--){
    digitalWrite(DATAOUT,commandbits&1<<i);
    //cycle clock
    digitalWrite(SPICLOCK,HIGH);
    digitalWrite(SPICLOCK,LOW);    
  }

  digitalWrite(SPICLOCK,HIGH);    //ignores 2 null bits
  digitalWrite(SPICLOCK,LOW);
  digitalWrite(SPICLOCK,HIGH);  
  digitalWrite(SPICLOCK,LOW);

  //read bits from adc
  for (int i=11; i>=0; i--){
    adcvalue+=digitalRead(DATAIN)<<i;
    //cycle clock
    digitalWrite(SPICLOCK,HIGH);
    digitalWrite(SPICLOCK,LOW);
  }
  digitalWrite(SELPIN, HIGH); //turn off device
  return adcvalue;
}


void loop() { 
 readvalue = read_adc(1); 
 Serial.println(readvalue,DEC); 
 readvalue = read_adc(2); 
 Serial.println(readvalue,DEC); 
 Serial.println(" "); 
 delay(250); 
} 

MCP3304

Here is a version that works with the MCP3304, which is an 8-channel, differential, 13-bit version of the above controller. The pinout is exactly the same.

This code uses the SPI library rather than bit-banging with digitalWrite() so is much more efficient. It can sustain about 32ksps with a 2MHz SPI clock on an Uno or Mega

Note that this code hard-codes the command bits:

  // command bits for MCP3304
  // 0000 = diff, ch0 = in+, ch1 = in-
  // 0010 = diff, ch2 = in+, ch3 = in-
  // 0100 = diff, ch4 = in+, ch5 = in-

(the read_adc function provides 3 differential input channels, I didn't bother to code up for the 4th differential channel)

Code:

// read the MCP3304 in quad differential mode, non-bit-banging version
//#include <Serial.h>
#include <SPI.h>

#include <HardwareSerial.h>

long ticks = 0;

// for Uno
//#define SELPIN 10    // chip-select
//#define DATAOUT 11   // MOSI 
//#define DATAIN 12    // MISO 
//#define SPICLOCK 13  // Clock 

// for Mega2560 / Max32
#define SELPIN 53    // chip-select
#define DATAOUT 51   // MOSI 
#define DATAIN 50    // MISO 
#define SPICLOCK 52  // Clock 

int readvalue; 


void setup(){ 
  //set pin modes 
  pinMode(SELPIN, OUTPUT); 

  // disable device to start with 
  digitalWrite(SELPIN, HIGH); 

  SPI.setClockDivider( SPI_CLOCK_DIV8 ); // slow the SPI bus down
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);    // SPI 0,0 as per MCP330x data sheet 
  SPI.begin();

  Serial.begin(115200); 
}


void loop() {
  long ticks = millis();
  int A = 0, B = 0;

  A = read_adc(1);
  B = read_adc(2);

  // do whatever you want with these readings

  long tcnv = millis() - ticks;

  delay(100 - tcnv);
}

// non-bit-banging version
// channel ranges from 1.. 3 (not zero!)
// maximum clock frequency is 2.1 MHz @ 5V
//
// this is SPI_CLOCK_DIV8
// at DIV64, 512 in 60ms (8.5 ksps)
// at DIV32, 512 in 35ms (14.6 ksps)
// at DIV16, 512 in 22ms (23.3 ksps)
// at DIV8, 512 in 16ms (32 ksps)

int read_adc(int channel){
  int adcvalue = 0;
  int b1 = 0, b2 = 0;
  int sign = 0;

  // command bits for MCP3304
  // 0000 = diff, ch0 = in+, ch1 = in-
  // 0010 = diff, ch2 = in+, ch3 = in-
  // 0100 = diff, ch4 = in+, ch5 = in-

  digitalWrite (SELPIN, LOW); // Select adc

  // first byte
  // first byte will always be B000010xx where xx are the D2 and D1 channel bits  
  byte commandbits = B00001000;
  commandbits |= (channel >> 1);         // high bit of channel

  SPI.transfer(commandbits);       // send out first byte of command bits

  // second byte; Bx0000000; leftmost bit is D0 channel bit
  commandbits = B00000000;
  commandbits |= (channel << 7);        // if D0 is set it will be the leftmost bit now
  b1 = SPI.transfer(commandbits);       // send out second byte of command bits

  // hi byte will have XX0SBBBB
  // set the top 3 don't care bits so it will format nicely
  b1 |= B11100000;
  //Serial.print(b1, BIN); Serial.print(" ");
  sign = b1 & B00010000;
  int hi = b1 & B00001111;

  // read low byte
  b2 = SPI.transfer(b2);              // don't care what we send
  //Serial.print(b2, BIN); Serial.print("\r\n");
  int lo = b2;
  digitalWrite(SELPIN, HIGH); // turn off device

  int reading = hi * 256 + lo;

  if (sign) {
    reading = (4096 - reading) * -1;
  }

  return (reading);
}

Share