New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« on: March 29, 2008, 04:55:27 am » |
I've been working with gyros on the Arduino, but 10 bits of ADC wasn't really enough, so I added a 12 bit SPI ADC. I used the Microchip MCP3202 a 2 channel 12 bit SPI ADC (also available as 3201, 3204 and 3208 with 1,4, and 8 channels), they're cheap and easily available from Farnell ( http://www.farnell.com/). Because it is 12 bit and the built in SPI only likes 8 bit bytes, I manually manipulated the pins, it seems quite fast despite using mainly high-level commands. The code should work with the other 320* chips, but I think that the 04 and 08 have 1 more setup bit, but that would take about 30 sec to change. If anyone has ideas to make it less processor intensive they're most welcome. Code below. Alec #define SELPIN 10 //Selection Pin #define DATAOUT 11//MOSI #define DATAIN 12//MISO #define SPICLOCK 13//Clock int readvalue; int commandbits[] = {1,1,0,0}; //command bits (null,mode,channel,MSB/LSB)
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 tempbit = 0; int adcvalue = 0; //allow channel selection if(channel==1){ commandbits[2]=0; } else { commandbits[2]=1; } digitalWrite(SELPIN,LOW); //Select adc
// setup bits to be writen for (int i=0; i<=3; i++){ if(commandbits==1){ digitalWrite(DATAOUT,HIGH); } else { digitalWrite(DATAOUT,LOW); } //cycle clock digitalWrite(SPICLOCK,HIGH); digitalWrite(SPICLOCK,LOW); }
//read bits from adc for (int i=0; i<=12; i++){ tempbit=digitalRead(DATAIN); //ignores 1st null bit (always 0) adcvalue+=tempbit*(1<<(12-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); }
|
|
|
|
|
Logged
|
-- Alec
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #1 on: March 29, 2008, 05:57:44 am » |
Just found out that sparkfun also have the MCP3202
|
|
|
|
« Last Edit: March 29, 2008, 05:58:22 am by halabut »
|
Logged
|
-- Alec
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #2 on: May 10, 2008, 03:11:46 am » |
Ok, just thought i'd put in the latest, much neater code (also much more processor and code efficient). Note this is for the MCP3208, the 8 channel version. #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(ADCSEL,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(ADCSEL, HIGH); //turn off device return adcvalue; }
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); }
|
|
|
|
« Last Edit: May 10, 2008, 03:12:12 am by halabut »
|
Logged
|
-- Alec
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 10
Arduino rocks my world!
|
 |
« Reply #3 on: May 13, 2008, 12:23:59 am » |
Hi halabut, any chance you've got a wiring diagram or piccy's of your hookup as i'd like to use the ADC to increase my analogue inputs but am unsure how to wire up the MCP3208.
Any help much appreciated. Regards
|
|
|
|
|
Logged
|
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #4 on: May 13, 2008, 04:02:58 am » |
The MCP3208 comes as an 16 pin DIP (also soic) pinout below, get the datasheet here http://ww1.microchip.com/downloads/en/DeviceDoc/21298D.pdf With a little reading you should be able to come up with a similar solution to mine. NOTE: code is for single ended operation, read the datasheet for differential operation. D10 indicates arduino digital pin 10 etc. Pinout1-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
|
|
|
|
« Last Edit: May 13, 2008, 04:03:58 am by halabut »
|
Logged
|
-- Alec
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 10
Arduino rocks my world!
|
 |
« Reply #5 on: May 13, 2008, 05:36:09 pm » |
Thanks halabut, the ADC is perfect for the hardware side of things. now i just got to work out how/if MaxMSP can essentially identify each analog input into the ADC seperately as they will all be (hopefully) triggering independent sounds in Max.
Thanks Again
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 10
Arduino rocks my world!
|
 |
« Reply #6 on: May 16, 2008, 08:10:30 pm » |
HI halabut, so now I have my MCP3208 all wired up and have entered the code but am getting this error message in the digitalwrite section of the code:
error:'ADCSEL' was not declared in this scope In function 'int read_adc(int)': error: 'ADCSEL' was not declared in this scope AT global scope:
any ideas on what i could be doing wrong?
thanks again frances
|
|
|
|
|
Logged
|
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #7 on: May 16, 2008, 08:24:28 pm » |
oops, I pulled that code from a project sketch, and looks like i renamed a constant without changing the rest of the code. Replace ADCSEL with SELPIN and it will work 
|
|
|
|
|
Logged
|
-- Alec
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 10
Arduino rocks my world!
|
 |
« Reply #8 on: May 17, 2008, 07:16:24 pm » |
thanks again halabut.. that got it working.. now i've just got to get it talking to Max/msp.
cheers
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Jr. Member
Karma: 0
Posts: 50
Arduino rocks
|
 |
« Reply #9 on: May 17, 2008, 11:27:59 pm » |
Anyone know of a good 12bit ADC that has +VREF as well as -VREF?
thanks, Phil
|
|
|
|
|
Logged
|
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #10 on: May 17, 2008, 11:44:17 pm » |
If I understand right, you want to measure between a lower and upper voltage. On the MCP3208 you can set analogue and digital ground separately, so you should be able to set AGND to your lower ref voltage and VREF to your upper ref voltage. Read the datasheet for more info. You could also try differential operation, useful for some of those sensors with small voltage swings and large offsets, I'm using it on some freescale pressure sensors which have a 2.5V offset and a 20mV swing (0.02V!) and get fairly stable results.
|
|
|
|
|
Logged
|
-- Alec
|
|
|
|
0
Offline
Jr. Member
Karma: 0
Posts: 50
Arduino rocks
|
 |
« Reply #11 on: May 19, 2008, 01:51:25 am » |
Thank you halabut, I'll have to check that out. I'm using a pressure sensor from Allsensors. It maxes out at about 4.88V and its minimum is a "don't care" in my project.
I got some freescale pressure sensors sample that I have yet to play with.
Phil
|
|
|
|
|
Logged
|
|
|
|
|
Australia
Offline
Jr. Member
Karma: 0
Posts: 94
Arduino rocks
|
 |
« Reply #12 on: May 20, 2008, 06:51:50 pm » |
If I understand right, you want to measure between a lower and upper voltage. On the MCP3208 you can set analogue and digital ground separately, so you should be able to set AGND to your lower ref voltage and VREF to your upper ref voltage. Read the datasheet for more info. Also if you are trying to measure +/- values, try to get the zero-point in the centre of the ADC range (by suitable selection of VREF & AGND). Then you can convert the ADC value from offset binary to two's complement signed fairly quickly with the following: // Convert offset binary (zero value in centre of range) to two's complement signed // val = read ADC val // bits = resolution of ADC // int offsetBinary2TwosComp( int val, char bits ) { val ^= 1 << (bits - 1); // Toggle the high bit if (val & (1 << (bits - 1))) val |= (0xFFFF << bits); // Sign extend return val; } Conversion involves inverting the MSB then sign-extending if the result is negative.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Jr. Member
Karma: 0
Posts: 50
Arduino rocks
|
 |
« Reply #13 on: June 15, 2008, 07:21:46 pm » |
Hello again Halabut,
I just got a few of these ADC's, tried to run the code, but I'm getting a strange error:
error: expected constructor, destructor, or type conversion before '(' token
It highlights this portion of the code:
digitalWrite(SELPIN, HIGH); //turn off device
Any ideas?
thanks, Phil
|
|
|
|
|
Logged
|
|
|
|
|
New Zealand
Offline
Jr. Member
Karma: 0
Posts: 69
arduino: far too much like electrical engineering.
|
 |
« Reply #14 on: June 15, 2008, 11:44:10 pm » |
Try this, I just spotted another error in the 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); }
|
|
|
|
|
Logged
|
-- Alec
|
|
|
|
|