How do I read data from an ADC over SPI?

This is a re-post of a question I posed previously about using a 16-bit Analog to Digital converter with an Arduino Mega. This question is more directed at the use of the SPI protocol. I'm using a TLC4541 A to D converter (datasheet: http://www.ti.com/lit/ds/symlink/tlc4545.pdf) and the Mega2560.

As I understand it, the process should be as follows:

  1. Enable SPI
  2. Read the first byte
  3. Read the second byte
  4. Disable SPI
  5. Write the bytes to an integer
  6. Display that integer in the Serial Window

Have I got this right or am I missing something obvious? Here is the code I'm working with:

#include <SPI.h>
//Using a strain gauge attached to 16-bit ADC, connected to an 
//Arduino Mega2560.
/* SPI Pins used:
   MISO = 50
   SCK = 52
   SS = 53
   */
// FS tied high (Vdd)
// default SPI speed (4 MHz), or high speed (8 MHz) may be used.  

const int adcCS = 53; //Chip select output. Controls the slave device

int adcValue=0;//This is the value in the ADC output register
byte dummyByte;//Byte that does nothing but allows the conversion cycle to complete.
byte upperByte;//Most significant BIT
byte lowerByte;//Least significant BIT


void setup(){
  SPI.begin();//Enables the SPI interface 
  SPI.setDataMode(SPI_MODE1);//Per datasheet for the TLC4541 ADC chip, the SPI interface
                             //is programmed for CPOL=0, CPHA=1.
  pinMode(adcCS, OUTPUT);

    Serial.begin(9600);

  
//Reset cycle for the ADC. Initializes the chip on power up. Occurs only 1 time.  
digitalWrite (adcCS, LOW); 
dummyByte = SPI.transfer(0); // Initialization/Reset clocks
digitalWrite (adcCS,  HIGH);
  
}
  
  void loop(){
    // read out data from prior conversion, provide clocks for next conversion

digitalWrite (adcCS, LOW); //Enables SPI 
upperByte = SPI.transfer(0);  // read one byte of result back
lowerByte = SPI.transfer(0);  // read one byte of result back
dummyByte = SPI.transfer(0); // finish providing 24 clocks to complete conversion
digitalWrite (adcCS,  HIGH); //Disables SPI
adcValue = (upperByte<<8) + lowerByte; // Moves the upperByte to the left, then adds 
                                  //the lowerByte.  Combining the two equals the adcValue.
    

    
    Serial.println(adcValue, DEC);//Prints the ADC Value to Serial port. 
                                  //Converts from binary to decimal.
 
    delay(250);
}

Thanks for the help!

am I missing something obvious?

well not setting adcCS to be an output is obviously something you are not doing.

adcCS is set as an output.

sorry so it is.
Have you put a scope on it?

Just a multimeter. I'm getting .076V on pin 4 (Ain) and the same on pin 6 (SDO). I assume that means the circuit is working properly so I'm trying to figure out what I'm doing wrong with the code.

I'm getting .076V on pin 4 (Ain) and the same on pin 6 (SDO). I assume that means the circuit is working properly

No a multimeter can't tell you that, you have to see the pulses.

so what is your physical set up- full diagram? this is my assumptions :
i assume your powering your TI properly with GND, VDD same as arduino, you've got some Vref hooked to it, you seem to have Ain hooked to your strain gauge with some small voltage with a resistor somewhere in there...

you've got mega pin 52 hooked to your TI CLK

you need to have pin 53 set to an output cause its the MASTER i would do this as the first line of set up.
i believe after this point you should not be using pin 53 (adcCS),
if you want to manually trigger the CS on the TI chip go for it but don't mess with pin 53- at least not yet.

youve got pin 50 MISO hooked to SDO

youve got the right SPI mode1 for your TI piece.

try MOSI connected to CS and dont manually (adcCS=low/high) start it, I'm not sure how the SPI is set up. but that's what i would try.
also try the manual trigger but not with pin 53.

but i dont really know anything about SPI that's just me looking over the data sheets.

on another note:
Couldn't you just use an analog input on your arduino?
the Arduino has built in 10 bit ADC converter,
do you really need the extra 6 bits of accuracy?

dej

references
data sheet
arduino SPI
the SPI wiki isnt bad either.

Thanks for the help. I'll start plugging away at your suggestions. The 10bit ADC does not have a fine enough resolution. I'm measuring the change in mV out from a strain gauge, but with an increasing load of 100 lb increments, the mV out is changing by only .01mV. I'm trying to use the 16bit ADC because the resolution is about .07mV per division.

Why don't you amplifiy the signal to a realistic level for ADC. Higher resolution ADC is not the way to solve your problem.

Here's how I arrived at the conclusion that simply amplifying the voltage was not enough:

With the strain gauge at rest, I get 3.4mV out. This voltage is the input to an INA125 amplifier. The output of the amplifier is .076V. This is an amplification of 22.35x.

As the load on the strain gauge increases, the mV out signal increases:
0lbs -- 3.4mV
100lbs -- 3.5mV
200lbs -- 3.6mV...and so on.

With an amplification of 22.35x, I get the following amplified readings:

3.4mV * 22.35V = .07599V
3.5mV * 22.35V = .078225V
3.6mV * 22.35V = .08046V

The resolution of the 10Bit ADC on the Arduino, even with a 1.1V reference, is 1.7mV.

The change in voltage with each increment of +100 lbs is 2.2mV. So if my application is to weigh a piece of steel, as a safety measure, the scale needs to be accurate. There's no way around it. Even with proper amplification, I need a higher resolution. I want accuracy within 0.5 lbs for loads in excess of 2500lbs.

Even if my logic is wrong here, I would still like to know how to properly read data from an external ADC over SPI.

With all that said, does anyone know how to do it? I'm having trouble understanding it.

Thanks

All that rambling and I forgot to post my circuit:

Circuit Image.docx (155 KB)

first balance your gage circuit. 0mV at 0 lbm. now you can amplify the signal and get it to the proper level for inpu to ADC. your gage output changes 0.1mV/100 lb. at 2500 lbm output is 2.5mV. gain ov 200 give 5V at 2500lbm.

How do I balance the gage circuit?

let us see the circuit. It depends on if it is a quarter, half or full bridge and how many active gages. as far as SPI interface goes it shouldn't be hard. look at the baro pressure example on how to read from a device and the AD data sheet for register access.

I'm using a full wheatstone bridge SGT-3E/350FB41 from Omega engineering. Also using an INA125 instrumentation amplifier. I'll take another look at the baro example.

Thanks for the help.

Circuit Image.docx (155 KB)

Any chance of posting the circuit in a format that everyone can read?

Is the word document a problem or is my sloppy circuit the problem?

Is the word document a problem

Yes I am on a Mac and while I can see .doc files the .docx you posted just show up blank.

No problem. Here it is in a different format:

Schematic Image.doc (223 KB)

Thanks.
I can't see how in your code you are initialising the module. See page 12 of the data sheet. You should bit bang this in the setup function.

Each SPI transfer is only 8 bits so having the dummy bits line with a comment of 24 bits is wrong.
I also can't see how you get the 20 clock edges before the sample is ready.
The whole thing probably requires bit banging.