Offline
Newbie
Karma: 0
Posts: 19
|
 |
« on: February 21, 2011, 04:43:02 pm » |
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; }
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9407
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #1 on: February 21, 2011, 05:28:08 pm » |
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; }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 19
|
 |
« Reply #2 on: February 21, 2011, 11:29:39 pm » |
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: 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);
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Faraday Member
Karma: 6
Posts: 2504
|
 |
« Reply #3 on: February 22, 2011, 07:20:23 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9407
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #4 on: February 22, 2011, 07:49:08 am » |
@jdubulator1
Please post your 'final' code, is more clear than the snippets.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 19
|
 |
« Reply #5 on: February 22, 2011, 12:40:02 pm » |
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; }
|
|
|
|
« Last Edit: February 22, 2011, 12:41:47 pm by jdubulator1 »
|
Logged
|
|
|
|
|
SF Bay Area
Offline
Full Member
Karma: 0
Posts: 176
Arduino rocks
|
 |
« Reply #6 on: May 18, 2011, 01:44:50 am » |
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
Newbie
Karma: 0
Posts: 4
|
 |
« Reply #7 on: August 29, 2011, 05:46:43 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9407
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #8 on: August 29, 2011, 06:28:05 am » |
Please modify your post, select the code and press the #button for proper tagging, Rob
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 4
|
 |
« Reply #9 on: August 29, 2011, 07:57:04 am » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 4
|
 |
« Reply #10 on: August 29, 2011, 08:03:02 am » |
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
Newbie
Karma: 0
Posts: 4
|
 |
« Reply #11 on: August 30, 2011, 01:16:08 am » |
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
Newbie
Karma: 0
Posts: 6
|
 |
« Reply #12 on: May 03, 2012, 03:39:04 pm » |
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)
|
|
|
|
« Last Edit: June 04, 2012, 02:59:35 pm by alhinde »
|
Logged
|
|
|
|
|
|