No response from ADC using UNO SPI

Hello,

I currently have an UNO connected via SPI to an ADC (ADC128S052) which is connected to a current transformer. My aim here is to have the Arduino Serial Terminal Print the values from the ADC which is connected to a current transformer. I have set up the following code on the UNO:

#include <SPI.h>

#define DATAOUT 11
#define DATAIN 12
#define SPICLOCK 13
#define SLAVESELECT 10

int value;

void setup ()
{
Serial.begin(9600);  
  
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV4);

pinMode (DATAOUT, OUTPUT);
pinMode (DATAIN, INPUT);
pinMode (SPICLOCK, OUTPUT);
pinMode (SLAVESELECT, OUTPUT);

digitalWrite(SLAVESELECT,HIGH); 
SPI.begin();
}

void loop ()
{
digitalWrite(SLAVESELECT, LOW);
SPI.transfer(value);
digitalWrite(SLAVESELECT, HIGH);
Serial.print(value);
Serial.print("\n");
}

Using a scope on the pins I can see that CLK is pulsing as the right frequency, 125KHz, as is the MOSI pin. However I am getting no output from the ADC to the MISO pin.

Any suggestions?

Thanks.

A couple of things come to mind.
I have a bad habit of reading the data sheet for any device that needs to be accessed by any MCU and I see from the data sheet for the ADC128S052 that the SCK has an absolute minimum frequency of 800KHz but a guaranteed minimum operating frequency of 3.2MHz ("AC Electrical Characteristics, 'FsclkMIN', bottom of page 5). Therefore your clock frequency of 125KHz probably won't cut it for the ADC. I suspect this may be why you are not seeing any signal on the 'DOUT' pin of the ADC.
Looking at the "Serial Interface" description (starting near the bottom of page 14), it would appear that you need to provide it with a 'command' value that will control the next conversion cycle.
Now, I have no experience with the SPI interface on the UNO so I have to assume that it is as per the documentation on the Arduino web site which shows that the values being transferred are bytes. I assume that is why you send first a zero sand the the 'value' to the device. However the values are sent MSB first and looking at the data sheet, the "control' bits all exist in the first 8 bits. This is fine as long as you always want to sample channel 0 but I'm guessing by the use of the 'value' variable that is sent and the 2nd 8-bits, that you want to use this to control the channel. IF so, then 2 things: 1) you need to declare 'value' somewhere (at least I can't see it and I'm surprised that the program compiled without this) and 2) it should be the first 8 bits to be sent, not the 2nd.
Also you have a floating point value called 'data' that I 'm guessing is supposed to receive the value back from the ADC. The 'transfer' function will return the 8-bit value that is received during the value exchange and so it will need to be saved as an integer as this will be to top 8 bits of the final value.. When the 2nd 'transfer' is made, you will need to also capture its return value and it will need to be used as the bottom 8 bits of the complete value from the ADC. According to the "ADC128S052 Transfer Function" section (bottom of page 15), the value sent by the ADC is a simple binary value which will be interpreted as a 16-bit integer with the range from 0 to 4095. If you want this as a floating point (presumably scaled to represent the full range of the signal you are measuring, then you will need to do something with the integer value yourself. At the moment, 'data' is never assigned a value.
Susan

Thanks for your reply and help. I've realised that the code I originally posted I was working with at the time so I accidentally posted that instead of the code I intended (not hugely different). I've edited the original post to what it should have been, and I've also changed the clock divider to 4 instead of 128, giving a CLK frequency of 4MHz.

With regards to the 'command' for the next conversion cycle, how would I go about implementing this?

I've removed the data variable as it was supposed to be the same as the value variable. With regards to this, does the SPI.transfer(value); store the information from the ADC or send the information from the arduino? I don't need to send any data out to the ADC I believe, just collect the output values.

Finally, I inserted the SPI.transfer(0); because I'd seen some examples where there needed to be an initial 'handshake' between the two devices. How does selecting 0 collect the 2nd 8-bits of data and also instead, how would I collect the first 8 bits?

Thanks.

Order here needs to change:

SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV4);

pinMode (DATAOUT, OUTPUT);
pinMode (DATAIN, INPUT);
pinMode (SPICLOCK, OUTPUT);
pinMode (SLAVESELECT, OUTPUT);

digitalWrite(SLAVESELECT,HIGH);
SPI.begin();

SPI.begin(); needs to occur before the other SPI settings.

Thanks!

Program_823:
With regards to the 'command' for the next conversion cycle, how would I go about implementing this?

I've removed the data variable as it was supposed to be the same as the value variable. With regards to this, does the SPI.transfer(value); store the information from the ADC or send the information from the arduino? I don't need to send any data out to the ADC I believe, just collect the output values.

Finally, I inserted the SPI.transfer(0); because I'd seen some examples where there needed to be an initial 'handshake' between the two devices. How does selecting 0 collect the 2nd 8-bits of data and also instead, how would I collect the first 8 bits?

In some ways these are all the same question and come down to understanding how SPI exchanges work.
The key thing to note in the above sentence is "exchange" - the SPI master sends a value to the slave at the same time that the slave sends a value back to the master. For an SPI exchange the master supplies the clock pulses as well.
Values must always be exchanged between the master and the slave. However that does not mean that either end needs to make use of any value is receives.
An SPI exchange is initiated when a value is written to the master. Therefore a value must always be sent from the master to the slave. In the case of the ADC, this is also interpreted as the command to tell the ADC what to do in the next measurement cycle. The ADC, as the slave, must be given time to make a new value available before the master triggers off a new exchange. Your ADC handles this by using the first few clock cycles of each exchange to perform the actual measurement and starts sending the measured value in the lower 12 bits.
If you look at the documentation for the SPI transfer function you will see that it says that it takes a byte value as a parameter and returns a byte value. The parameter value is what will be sent to the slave and the return value is what was received from the slave.
Things become a little harder when talking to the ADC because it expects 16-bit values in and out in a single exchange, whereas the SPI interface uses 8-bit values. Therefore you need to perform 2 'transfers' in sequence while the 'slave select' line is low ('low' selects the ADC slave).
The ADC expects values to be transferred MSB first so you need to do the following (pseudo-code)

  • drop the SS line
  • rxValue = transfer( (txValue >> 8) & 0xff) - send the top 8 bits and save the response
  • rxValue = (rxValue << 8) | transfer( txValue & 0xff) - send the bottom 8 bits, shift the previous response up 8 bits and add in the new response
  • raise the SS line
    As for what the 'command' value is, you need to look at the ADC data sheet and set it for whatever channel you want to read - I recall that the default command of all zeros equates to reading the channel 0.
    One other thing to remember if you are trying to read the value from multiple channels from the ADC is that the command tells the ADC which channel to read the next time.
    Susan