Go Down

Topic: Updated Code for MCP3208 (Read 6466 times) previous topic - next topic

jdubulator1

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)

Code: [Select]
#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;
}



robtillaart

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

Try this, removed some intermediate values. faster?

Code: [Select]

#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;
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

jdubulator1

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><D2>  <D1><D0>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:

Code: [Select]
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:

Code: [Select]
SPI.transfer((commandBytes>>8) & 0xff);

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

kg4wsv

Quote
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.

Quote
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

robtillaart

@jdubulator1

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

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

jdubulator1

#5
Feb 22, 2011, 06:40 pm Last Edit: Feb 22, 2011, 06:41 pm by jdubulator1 Reason: 1


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.

Code: [Select]
#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;
}

oscarcar

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.

praks1510

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;
}


robtillaart


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

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

praks1510

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

Code: [Select]
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;
}

praks1510

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

praks1510

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.

alhinde

#12
May 03, 2012, 10:39 pm Last Edit: Jun 04, 2012, 09:59 pm by alhinde Reason: 1
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

Code: [Select]

//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)


Go Up