Pages: [1]   Go Down
Author Topic: Updated Code for MCP3208  (Read 6059 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 220
Posts: 13846
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Try this, removed some intermediate values. faster?

Code:
#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;
}
Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
SPI.transfer((commandBytes>>8) & 0xff);

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

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 220
Posts: 13846
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@jdubulator1

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

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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:
#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;
}
« Last Edit: February 22, 2011, 12:41:47 pm by jdubulator1 » Logged

SF Bay Area
Offline Offline
Full Member
***
Karma: 1
Posts: 182
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

 
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 220
Posts: 13846
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 1
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
//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)
« Last Edit: June 04, 2012, 02:59:35 pm by alhinde » Logged

Pages: [1]   Go Up
Jump to: