Pages: [1]   Go Down
 Author Topic: 16-bit spi ADC acts like a 15 bits...  (Read 3370 times) 0 Members and 1 Guest are viewing this topic.
0
Offline
Newbie
Karma: 0
Posts: 3
Arduino rocks
 « on: June 04, 2010, 01:29:31 pm » Bigger Smaller Reset

Hi to you all,

First of all let me tell you im new here! So i hope you guys can help me.

My project:
I want to read  analog inputs from four different sensors and send them to a pc using a serial communication.

Hardware:
I recently bought a Arduino Duemilanove(Atmega328) for reading my 16 bits adc(ads8341) and sending data to a pc.

The problem
It seems like the adc is acting like a 15 bits adc. Let's go a little bit deeper:
Analog inputs range are 0-5v(vref is 5v). So with a 16-bits adc the 5 v is divided in 2^16( 65536) levels from 0-65535 right?
So if i put 5v on my analog inputs you should read 65535 out of my adc right?  However i get 32767 (like its is a 15-bits adc). So i tried some different input voltages (like 2v and 3.3) and all the conversions are correct only if i use 15-bits in my calculations.

the analoge input voltage  = lvl/(2^15-1)*5v

For my project i dont really need 16-bits , 14-bits is enough.  However i want to understand why it is working like a 15 bits adc and learn from the experiance.

I figure it is a software problem because the adc is sort of working.

Software
I based my mC software on this example from the arduino site:
http://www.arduino.cc/playground/Code/MCP3208

My code:
Code:
#define SELPIN 10 //Selection Pin
#define DATAOUT 11//MOSI
#define DATAIN  12//MISO
#define SPICLOCK  13//Clock

void setup()
{
//set pin modes
pinMode(SELPIN, OUTPUT);
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);
digitalWrite(SELPIN,HIGH);
digitalWrite(DATAOUT,LOW);
digitalWrite(SPICLOCK,LOW);

Serial.begin(9600);
}

{
int cnumber = 0;
byte commandbits =  B00000000;// read nothing....

switch(channel)//switch case to select channel
{
case 1:  {
commandbits = B10010100;// channel 0 selected  start bit(bit 7)-Channel select(bits 6,5,4)-dont care(bit 3)-sgl/diff(bit 2)-powerdown mode(bit 1,0)
}
break;
case 2:  {
commandbits = B11010100;// channel 1
}
break;
case 3:  {
commandbits = B10100100;// channel 2
}
break;
case 4:  {
commandbits = B11100100;// channel 3
}
break;
}

//we need 24 cycles for a full conversion
// 8 cycles for control byte and 16 for
// setup bits to be written
for (int i=7; i>=0; i--){
digitalWrite(DATAOUT,commandbits&1<<i);
//cycle clock
digitalWrite(SPICLOCK,HIGH); //8 times
digitalWrite(SPICLOCK,LOW);
}
for (int i=15; i>=0; i--){ //
//cycle clock
digitalWrite(SPICLOCK,HIGH);//16 times
digitalWrite(SPICLOCK,LOW);
}
//24 cycles total
digitalWrite(SELPIN, HIGH); //turn off device
}

void loop()
{
char inByte;
int ichannel;
if(inByte == 'r')//if ibyte is r
{
for(ichannel=1;ichannel <=4;ichannel++)
{ //loop to read all four channels and send it to serial
}
}
// wait a bit for the analog-to-digital converter
// to stabilize after the last reading:
delay(10);
}

I  send the char "r" to de atmega so that it returns the four analog values.

Can comeone please explain to me why it is acting like a 15-bits adc?

Thanks you,

Alex

 Logged

Left Coast, CA (USA)
Offline
Brattain Member
Karma: 361
Posts: 17263
Measurement changes behavior
 « Reply #1 on: June 04, 2010, 03:15:03 pm » Bigger Smaller Reset

Quote
Can comeone please explain to me why it is acting like a 15-bits adc?

Just a shot in the dark but why don't you try changing:

to

The sign bit of a standard int might be causing what you are seeing?

Lefty
 Logged

0
Offline
Newbie
Karma: 0
Posts: 3
Arduino rocks
 « Reply #2 on: June 05, 2010, 04:47:43 am » Bigger Smaller Reset

Quote
Just a shot in the dark but why don't you try changing:

to

The sign bit of a standard int might be causing what you are seeing?

Lefty

I tried changing it to unsigned int but it still gives me 15 bits....

 Logged

0
Offline
Newbie
Karma: 0
Posts: 1
Arduino rocks
 « Reply #3 on: June 17, 2010, 01:08:43 pm » Bigger Smaller Reset

Hi Alex,

I am also new here but allow me to take a crack at it.

The problem I see is that your code doesn't allow for a "sleep" cycle after the command byte is written to clear the busy period for the adc.  I recommend setting the SPICLOCK high then low once between writing the command byte and reading the data.  Alternatively you could write the code for reading the data in the following manner:

Code:

for (int i=15; i>=0; i--){
//cycle clock
digitalWrite(SPICLOCK,HIGH);//16 times
digitalWrite(SPICLOCK,LOW);

}

This will also allow you to take advantage of the fact that you should be able to write the first bit of the command byte during the same clock cycle as you read the last bit of your data.

Good luck,

Brian
 Logged

Manchester (England England)
Offline
Brattain Member
Karma: 604
Posts: 33448
Solder is electric glue
 « Reply #4 on: June 18, 2010, 08:31:59 am » Bigger Smaller Reset

Quote
I tried changing it to unsigned int but it still gives me 15 bits....

So now change it to a long int and see what you get.
You are never going to get over 32K with an int.
 Logged

0
Offline
Karma: 8
Posts: 2526
 « Reply #5 on: June 18, 2010, 08:40:41 am » Bigger Smaller Reset

er, yeah, Mike, an unsigned int is 0 - 65535. AFAICT, it should fit no problem.

signed vs. unsigned can screw you up, though.

-j
 Logged

Manchester (England England)
Offline
Brattain Member
Karma: 604
Posts: 33448
Solder is electric glue
 « Reply #6 on: June 18, 2010, 08:44:57 am » Bigger Smaller Reset

Yes the point is that if you are doing any sums with an unsigned int it screws you up and unless you deal with just bit patterns you are often better going to a long for all the variables you are using. I suspect the OP knows nothing of this.
 Logged

0
Offline
Karma: 8
Posts: 2526
 « Reply #7 on: June 18, 2010, 11:13:31 am » Bigger Smaller Reset

Quote
Yes the point is that if you are doing any sums with an unsigned int it screws you up and unless you deal with just bit patterns you are often better going to a long for all the variables you are using
Ah, I see your point now.

-j
 Logged

0
Offline
Newbie
Karma: 0
Posts: 8
Hello, World
 « Reply #8 on: July 29, 2010, 09:30:55 am » Bigger Smaller Reset

Hello all,

I'm having similar problems with an ADS8344 (the 8-channel version of the ADS8341).  I'm using unsigned long variables per Grumpy_Mike's suggestion, and have incorporated bmmcabe's posted code correction, as posted below:

Code:
// **********************************************
// Define the Arduino pins used for SPI interface
// **********************************************
#define SELPIN 10    // Selection pin
#define DATAOUT 11   // MOSI
#define DATAIN 12    // MISO
#define SPICLOCK 13  // Clock

// ************************************************************************************
// Declare (and initialize where necessary) global variables available to all functions
// ************************************************************************************

// *********************************************************
// Setup function - initializes the board and sets pin modes
// *********************************************************
void setup() {
// set pin modes
pinMode(SELPIN, OUTPUT);
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);

// disable device at the outset
digitalWrite(SELPIN, HIGH);
digitalWrite(DATAOUT, LOW);
digitalWrite(SPICLOCK, LOW);

// Setup communications rate
Serial.begin(9600);
}

// ****************************
// Loop function - main program
// ****************************
void loop() {
int iChannel = 1;
for(iChannel=1; iChannel <= 5; iChannel++) {
Loop to read all five channels and send it to serial
if (iChannel < 5) {
Serial.print(",");
}
}
Serial.println();
// Wait a bit for the analog-to-digital converter
// to stabilize after the last reading:
delay(10);
}

// *********************************************
// *********************************************
byte commandbits = B00000000;  // Read nothing....

switch(channel)  // Switch case to select channel
{
case 1:  {
commandbits = B10000100;  // Select channel 0
}
break;
case 2:  {
commandbits = B11000100;  // Select channel 1
}
break;
case 3:  {
commandbits = B10010100;  // Select channel 2
}
break;
case 4:  {
commandbits = B11010100;  // Select channel 3
}
break;
case 5:  {
commandbits = B10100100;  // Select channel 4
}
break;
}

// We need 24 cycles for a full conversion
// 8 cycles for control byte and 16 for
// setup bits to be written
for (int i=7; i>=0; i--){
digitalWrite(DATAOUT, commandbits&1<<i);
// Cycle the clock 8 times
digitalWrite(SPICLOCK, HIGH);
digitalWrite(SPICLOCK, LOW);
}
for (int i=15; i>=0; i--){ //
// Cycle the clock 16 times
digitalWrite(SPICLOCK, HIGH);
digitalWrite(SPICLOCK, LOW);

}
// 24 cycles total
digitalWrite(SELPIN, HIGH); // Turn off the device
}

Before I implemented bmccabe's code correction, sweeping 0-5V gave me a 15-bit (0-32k) reading.  Afterwards, something strange happened.  From 0V to 2.5V, I get the ADC to read from 0 to 32k; once I pass this half-full-scale point, the ADC 'rolls over' and starts readin 0 to 32k from 2.5V to 5V.

I think this could be due to the fact that I'm not reading enough bits per read.  The datasheet (which I can't link to yet as this is my first post on the forum) states that when using an external clock, we need to add one additional transfer to capture the LSB -- that is, 32 cycles instead of 24.  I'm going to investigate this a little further and will post back with my results, but if anyone has any suggestions I would really appreciate them!

Also, as a first-time poster: thanks to everyone in this thread so far for providing enough knowledge and code to get me started!
 Logged

Manchester (England England)
Offline
Brattain Member
Karma: 604
Posts: 33448
Solder is electric glue
 « Reply #9 on: July 29, 2010, 09:56:14 am » Bigger Smaller Reset

You are reading it in as an unsigned long but printing it out as if it were a signed value.
It looks like it is working as your results are what I would expect.
 Logged

0
Offline
Newbie
Karma: 0
Posts: 8
Hello, World
 « Reply #10 on: July 29, 2010, 11:00:15 am » Bigger Smaller Reset

Quote
You are reading it in as an unsigned long but printing it out as if it were a signed value.
It looks like it is working as your results are what I would expect.

Okay, I think I understand why -- Serial.print() is casting my unsigned long value to ASCII, am I on the right path here?

Not quite sure how to fix this.  If I change my code to

Code:

then I still have the same problem -- should I split up the value by byte and then send those bytes sequentially?
 Logged

Manchester (England England)
Offline
Brattain Member
Karma: 604
Posts: 33448
Solder is electric glue
 « Reply #11 on: July 29, 2010, 01:08:39 pm » Bigger Smaller Reset

Try defining the variables as simply long and not unsigned long. Long is supposed to give you 32 bit storage so that should be enough.
 Logged

0
Offline
Newbie
Karma: 0
Posts: 8
Hello, World
 « Reply #12 on: July 30, 2010, 08:59:25 am » Bigger Smaller Reset

I'm getting kind of frustrated with this but I suspect my troubles have less to do with my code and more to do with my circuit.

Basically, I'm just trying to measure the voltage level at one of the ADS8344's inputs.  I'm using the Arduino's +5V and GND pins to power the ADC, as well as provide VRef; I've got a 10K pot as the variable voltage source to the ADC's input.

Here's a graph of expected vs. actual results:

I can't see any reason why my code (same as above, only using long instead of unsigned long variables) would produce these results.  I'm going to try using a better (i.e., more stable) power supply option like a MAX6350 and see if this changes anything.
 Logged

0
Offline
Newbie
Karma: 0
Posts: 8
Hello, World
 « Reply #13 on: July 30, 2010, 11:46:58 am » Bigger Smaller Reset

GOT IT!

The problem was in the OP's choice of clock.  Setting PD1 = PD0 = 1 in the control bits selects the external clock (i.e., the Arduino's); I also added a fourth read byte per the "External Clock Mode, 32 Clock Cycles per Conversion" information in the datasheet (mostly zeroes).  Otherwise, the LSB of the read bytes comes in at the same time as the MSB of the control byte and I think the code just wasn't handling this properly.

However, signed/unsigned is still just not working as expected.  So, I'm leaving the variable as signed, and using logic in the code to correct negative to positive.

Code:
// **********************************************
// Define the Arduino pins used for SPI interface
// **********************************************
#define SELPIN 10    // Selection pin
#define DATAOUT 11   // MOSI
#define DATAIN 12    // MISO
#define SPICLOCK 13  // Clock

// ************************************************************************************
// Declare (and initialize where necessary) global variables available to all functions
// ************************************************************************************

// *********************************************************
// Setup function - initializes the board and sets pin modes
// *********************************************************
void setup() {
// set pin modes
pinMode(SELPIN, OUTPUT);
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);

// disable device at the outset
digitalWrite(SELPIN, HIGH);
digitalWrite(DATAOUT, LOW);
digitalWrite(SPICLOCK, LOW);

// Setup communications rate
Serial.begin(9600);
analogReference(DEFAULT);
}

// ****************************
// Loop function - main program
// ****************************
void loop() {
int iChannel = 1;

for(iChannel=1; iChannel <= 5; iChannel++) {
// Loop to read all five channels and send it to serial
if (iChannel < 5) {
Serial.print(",");
}
}
Serial.println();
// Wait a bit for the analog-to-digital converter
// to stabilize after the last reading:
delay(10);
}

// *********************************************
// *********************************************
byte commandbits = B00000000;  // Read nothing....

switch(channel)  // Switch case to select channel
{
case 1:  {
commandbits = B10000111;  // Select channel 0 (LPS-1 Uout)
}
break;
case 2:  {
commandbits = B11000111;  // Select channel 1 (LPS-2 Uout)
}
break;
case 3:  {
commandbits = B10010111;  // Select channel 2 (LPS-2 Kout)
}
break;
case 4:  {
commandbits = B11010111;  // Select channel 3 (External)
}
break;
case 5:  {
commandbits = B10100111;  // Select channel 4 (2.500V Reference)
}
break;
}

// We need 24 cycles for a full conversion
// 8 cycles for control byte and 16 for
// setup bits to be written
for (int i=7; i>=0; i--) {
digitalWrite(DATAOUT,commandbits&1<<i);
// Cycle the clock 8 times
digitalWrite(SPICLOCK,HIGH);
digitalWrite(SPICLOCK,LOW);
}
for (int i=16; i>=0; i--) {

// Cycle the clock 17 times
digitalWrite(SPICLOCK,HIGH);
digitalWrite(SPICLOCK,LOW);
}

// Shift in 7 zeros to complete conversion cycle
for (int i=6; i>=0; i--) {

// Cycle the clock 7 times
digitalWrite(SPICLOCK,HIGH);
digitalWrite(SPICLOCK,LOW);
}

// 32 cycles total
digitalWrite(SELPIN, HIGH); //turn off device