Updated Code for MCP3208

I rewrote the code for the MCP3208 12-bit SPI ADC to use the SPI library. Using the SPI library lets you have multiple SPI devices connected to the same MISO, MOSI, and CLK pins with different CS pins. This means you could now use the MCP3208 for an SD logging or SPI RAM project. Also, because you can set the SPI clock to 8Mhz with the easy to use SPI library, you can sample at very high rates. I was getting ~62.344 ksps with the code I included below. When using the code make sure to only use channel 0-7, not channel 1-8 like the previous example in the arduino playground. If you wanted to change it to use channel 1-8 it wouldn't be that difficult. I tried to comment out as much as possible in the readADC(int channel) function but feel free to ask questions if any arise. Also, i used pin 9 for my CS pin, but you could use any of the digital pins not in use. If you take a look at page 15 of the MCP3208 datasheet the SPI stuff should make sense... (http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf)

#include <SPI.h>

void setup()
{
  Serial.begin(115200);
  pinMode(9,OUTPUT);
  digitalWrite(9,HIGH);
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV2);

  float time = micros();
  for(int i = 0;i<1000;i++)
  {
    readADC(0);
  }
  float time1 = micros();
  
  Serial.println((time1-time)/1000);
}

void loop()
{

}

uint16_t readADC(int channel)
{
  uint16_t output;
  //Channel must be from 0 to 7
  //Shift bits to match datasheet for MCP3208
  byte channelBits = channel | 0x00;
  channelBits = channelBits<<5;
  
  //Select ADC
  digitalWrite(9, LOW);
  //send start bit and bit to specify single or differential mode (single mode chosen here)
  SPI.transfer(B00000110);
  //Read in MSB
  byte msb = SPI.transfer(channelBits);
  //Read in LSB
  byte lsb = SPI.transfer(0x00);
  
  digitalWrite(9,HIGH);
  
  //Don't care about the first 4 bits of MSB
  msb = msb & B00001111;
  
  //Combine to make 12 bit value
  output = msb<<8 | lsb;

  return output;
}

what does this do: i think it does nothing...
byte channelBits = channel | 0x00;

Try this, removed some intermediate values. faster?

#define SPI_PIN 9

uint16_t readADC(int channel)
{
  digitalWrite(SPI_PIN, LOW);

  //send start bit and bit to specify single mode  -- should be a define tooo
  SPI.transfer(B00000110);

  // channel = 0..7
  // shift 5 places, see datasheet MCP3208
  // Don't care about the first 4 bits of MSB
  byte msb = SPI.transfer((channel & 0x07) << 5) & B00001111;
  byte lsb = SPI.transfer(0x00);
  
  digitalWrite(SPI_PIN, HIGH);

  // cast before shiting the byte
  return = ((uint16_t) msb) <<8 | lsb;
}

You're right, I was just trying to break it up as much as possible to help others understand. Actually I messed up on the command bits too. I just looked back at the datasheet a second ago...

2 bytes need to be written to the ADC before values will start coming out:

000001<S/D> XXXXXX

S/D represents single mode or differential mode ADC calculation: 1 for single, 0 for differential
D2,D1,D0 represent the channel select.

the command bytes should really be:

commandMSB = b00000110;
uint16_t commandBytes = (uint16_t) (commandMSB<<8|channel<<6);

that should result in the right bytes to write out. so then we would have:

SPI.transfer((commandBytes>>8) & 0xff);

msb = SPI.transfer((byte)commandBytes & 0xff) & b00001111;
lsb = SPI.transfer(0x00);

Using the SPI library lets you have multiple SPI devices connected to the same MISO, MOSI, and CLK pins with different CS pins.

There's nothing magical about the SPI library that allows this - that's the design of SPI hardware.

because you can set the SPI clock to 8Mhz with the easy to use SPI library, you can sample at very high rates.

... and get incorrect results, because you are operating this chip at 4x its rated speed. Check page 3 of the datasheet; max clock speed is 2MHz at 5V.

-j

@jdubulator1

Please post your 'final' code, is more clear than the snippets.

kg4wsv:
There's nothing magical about the SPI library that allows this - that's the design of SPI hardware.

I meant as compared to the code provided in the arduino playground. That code is manually setting the clock pin high and low and not taking advantage of the internal SPI clock. Manually setting the clock pin high and low does not allow for the use of other SPI devices. I missed the part about the 2MHz cap, I'll make sure to change that, thanks.

#include <SPI.h>

void setup()
{
  Serial.begin(115200);
  pinMode(9,OUTPUT);
  digitalWrite(9,HIGH);
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);

  float time = micros();
  for(int i = 0;i<1000;i++)
  {
    readADC(0);
  }
  float time1 = micros();
  
  Serial.println((time1-time)/1000);
}

void loop()
{

}

uint16_t readADC(int channel)
{
  uint16_t output;
  //Channel must be from 0 to 7
  //Shift bits to match datasheet for MCP3208
  byte commandMSB = b00000110;
  uint16_t commandBytes = (uint16_t) (commandMSB<<8|channel<<6);
  
  //Select ADC
  digitalWrite(9, LOW);
  //send start bit and bit to specify single or differential mode (single mode chosen here)
  SPI.transfer((commandBytes>>8) & 0xff);

  msb = SPI.transfer((byte)commandBytes & 0xff) & b00001111;
  lsb = SPI.transfer(0x00);
  
  digitalWrite(9,HIGH);
  

  // cast before shiting the byte
  return = ((uint16_t) msb) <<8 | lsb;
}

Great, I'm lucky I found this. I was thinking I might need to try and do this so I could change the
clock rate.

Thanks so much.

i am using MCP3208 with LPC2148.
I always get the value as zero from all the eight channels. Kindly help.
my code

void spi1_open()
{
PINSEL1 |= 0x000000A8;
sseldf1_h();
SSPCR0 = 0x0B; // 12 Bit data transfer..
SSPCPSR = 30;
SSPIMSC = 0;
SSPCR1 = 0x02;

}

int spi1_read()
{

if(!(SSPSR & 0x04))
return -1;

return SSPDR;
}

unsigned char spi1_send_read_byte(unsigned int byte)
{
int rec_val;
while((SSPSR & 0x02) == 0){}
SSPDR = byte;

rec_val = spi1_read();
while(rec_val == -1)
rec_val = spi1_read();

return rec_val;
}

void select_3208()
{
//sseldf1_h();
IOCLR0|=0x00008000;
}

void deselect_3208()
{
//sseldf1_h();
IOSET0|=0x00008000;
}

unsigned int read_3208(unsigned int maddr)
{
unsigned int low,high=0;

unsigned int value;
uart0_printf("\nAddr :%x \n",maddr);
select_3208();
mdelay(20);
spi1_send_read_byte(maddr); // sends the address 0x060 for channel 1.

low=spi1_send_read_byte(0xFFFF);
mdelay(20);
deselect_3208();
uart0_printf("\nValue : %x \n",low);
return value;
}

Please modify your post, select the code and press the #button for proper tagging,
Rob

i am using MCP3208 with LPC2148.
I always get the value as zero from all the eight channels. Kindly help.
my code

void spi1_open()
{
   PINSEL1 |= 0x000000A8;
   sseldf1_h();
   SSPCR0 = 0x0B; // 12 Bit data transfer.. 
   SSPCPSR = 30;
   SSPIMSC = 0;
   SSPCR1 = 0x02;
   
}

int spi1_read()
{

if(!(SSPSR & 0x04))
      return -1;
   
   return SSPDR;
}

unsigned char spi1_send_read_byte(unsigned int byte)
{
    int rec_val;
   while((SSPSR & 0x02) == 0){}
   SSPDR = byte;
   
   rec_val = spi1_read();
   while(rec_val == -1)
      rec_val = spi1_read();
   
   return rec_val;
}

void select_3208()
{
  //sseldf1_h();
  IOCLR0|=0x00008000;
}

void deselect_3208()
{
 //sseldf1_h();
 IOSET0|=0x00008000;
}

unsigned int read_3208(unsigned int maddr)
{
 unsigned int low,high=0;

 unsigned int value;
             uart0_printf("\nAddr :%x \n",maddr);
 select_3208();
 mdelay(20);               
 spi1_send_read_byte(maddr); // sends the address 0x060 for channel 1. 

 low=spi1_send_read_byte(0xFFFF);
 mdelay(20);               
 deselect_3208();
            uart0_printf("\nValue : %x \n",low);
 return value;
}

Any initializations to be done?
I have just initialized the spi of the LPC2148 and started to read the adc value from the MCP3208.

I was using SPI1 of the LPC2148. When I used the SPI0 of LPC2148 my code started working.
Kindly note, with the same SPI1 routines used, I am able to communicate with other devices.

I've put the code from this topic and from arduino playground joined together into MCP3208 arduino library.

Download Library: AH_MCP320x.h (control of MCP3204, MCP3208 chips)
Link: description

//AH_MCP320x.h functions
    //free pin mode declaration
AH_MCP320x(int CS_pin, int DOUT_pin, int DIN_pin, int CLK_pin)
    //SPI mode declaration
 AH_MCP320x(int CS_pin)
    //read channel ADC value
int readCH(int CHANNEL)
    //read all channels
void readALL(int values[], int n)
    //read saved configuration of all channels
void getCONFIG(int config[],int n)
    //set config of measurement mode: single or differential
void setCONFIG(int CHANNEL, boolean SINGLE)
    //set all channels to diff. mode
void setCONFIG_allDiff(int n)
    //set all channels to single mode
void setCONFIG_allSingle(int n)
    //calculate the input voltage from ADC and VREF
float calcVOLT(float VREF, int ADCvalue)

Good day, I am a beginner and wants to know Is is better to use a MUX to get more analog inputs or a "MCP3208" and what are the advantages ,and can you show an example circuit using more than one "MCP3208" ?

Thanks an advance ...

mux only gets you 10-bit resolution at 100uS+ conversion time. You also have series resistance in the mux that may impact readings.
Part like MCP3208 gets you 12-bit readigs at much faster speed per reading.
It has SPI interface. All devices get connected to SPI, MOSI, and MISO in parallel. Each device than gets its own unique slave select signal.
Section 6 of the datasheet shows 3 SPI.transfers are needed.

digitalWrite (ss1Pin, LOW); // ss1Pin, ss2Pin, ss3Pin for 3 devices for example
byte0 = SPI.transfer(commandByte0); // byteo is read back on MISO while commandByte0 is sent out on MOSI
upperBits = SPI.transfer(commandByte1);  //  read back on MISO while commandByte1 is sent out on MOSI
lowerBits = SPI.transfer(0); // read back on MISO, device ignores the 0 going out
digitalWrite (ss1Pin, LOW);
// mask off 4 upper bits of upperBits and shift into the upper byte of dataCombined, bring in the lower 8 bits
int dataCombined = (int ((upperBits & 0b00001111) <<8) | lowerBits); // not 100% on the int there

commandByte0 is 0bxxxxx123, and commandByte1 is 0b45xxxxxx
where 123 and 45 are 11AAA where AAA is 000, 001, 010, 011, 100, 101, 110, 111 for 8 different channels, using single ended inputs per table 5-2. 10AAA for differential inputs.