0

I am trying to read data from an AD7314 Temperature Sensor that is connected to an Arduino Uno and output that value onto 6 LEDs also connected to the Arduino Uno board. From the image below, the sensor is showing only an output of 0011111111111111 during the streams of 16 clock cycles even after the chip enable goes from low to high before conversion to initiate a new one (it seems that the conversion is stuck). The datasheet says:

A leading zero and 10 bits of data are transferred during a read operation. Read operations occur during streams of 16 clock pulses. Output data is updated on the rising edge of SCLK. The serial data is accessed in a number of bytes if 10 bits of data are being read. At the end of the read operation, the SDO line remains in the state of the last bit of data clocked out of the AD7314 until CE returns low

I am not sure where exactly I am going wrong during the read operation:

Note: for the Waveform image (Top to bottom):

  • waveform 1: Chip Enable

  • waveform 2: Serial Clock

  • waveform 3: MISO Line

  • waveform 4: MOSI Line

My code is below:

#include <SPI.h>

const int MOSI_PIN = 11;
const int MISO_PIN = 12;
const int SCK_PIN = 13;
const int Slave_PIN = A0;

// module data
uint16_t myValFromTempSensor = 0;

void setup () {
  SPI.begin ();
  SPI.beginTransaction(SPISettings(125000, MSBFIRST, SPI_MODE0));
  pinMode(5, OUTPUT); //LED 3
  pinMode(6, OUTPUT); //LED 4
  pinMode(7, OUTPUT); //LED 5
  pinMode(8, OUTPUT); //LED 6
  pinMode(9, OUTPUT); //LED 7
  pinMode(10, OUTPUT);//LED 8
  pinMode(MOSI_PIN, OUTPUT); // MOSI to SDI
  pinMode(MISO_PIN, INPUT); //MISO to SDO
  pinMode(SCK_PIN, OUTPUT); //CLOCK PIN to SCK
  pinMode(Slave_PIN,OUTPUT); //Slave pin

  digitalWrite(Slave_PIN, LOW);//Set slave low before communication
  for (int i = 5; i < 11; i++) {
    digitalWrite(i, LOW);
  }
  delay(100);
} // end of setup

void loop () {
  digitalWrite(Slave_PIN, HIGH);  //Set slave high to read data
  myValFromTempSensor = SPI.transfer(0x00); // Read Data from the device
  myValFromTempSensor |= (uint8_t)SPI.transfer(0x00) << 8;// Bitshift to read next 8 bits
  digitalWrite(Slave_PIN, LOW);
  if(( myValFromTempSensor>=0) && ( myValFromTempSensor<=196)) {
    //Range 0C to 60C is  0 to 196
    digitalWrite(5, bitRead(myValFromTempSensor,0));
    digitalWrite(6, bitRead(myValFromTempSensor,1));
    digitalWrite(7, bitRead(myValFromTempSensor,2));
    digitalWrite(8, bitRead( myValFromTempSensor,3));
    digitalWrite(9, bitRead( myValFromTempSensor,4));
    digitalWrite(10, bitRead( myValFromTempSensor,5));
  } else {
    digitalWrite(5, 0);
    digitalWrite(6, 0);
    digitalWrite(7, 0);
    digitalWrite(8, 0);
    digitalWrite(9, 0);
    digitalWrite(10, 0);
  }
}

SPI Waveform

  • from the waveforms, it looks to me like the sensor is outputting 0b0011111111111111 = 0x3fff. – Edgar Bonet Jan 3 '18 at 18:45
  • The first 0 is a leading zero according to the datasheet which does not add to the sensor reading – user7187418 Jan 3 '18 at 19:18
  • I know, I did read the datasheet. I am just saying that I disagree with your statement: “the sensor is showing only an output of 01000000000”. – Edgar Bonet Jan 3 '18 at 19:20
  • I apologize, i corrected it. – user7187418 Jan 3 '18 at 19:36
1

I see several issues here.

Sensor output

According to the waveforms shown by your logical analyzer, the sensor is outputting 0b0011111111111111. We have to discard the first (a padding zero) and the last five bits (copies of DB0). We then end up with the actual data bits being 0111111111. This has to be interpreted as a signed fixed-point number in s.7.2 format, which gives 127.75 °C: the maximum possible value the sensor can output, and well above its maximum operating temperature.

I cannot tell what is going wrong here. It looks like the sensor is either burning alive, or trying to tell you it's extremely unhappy.

SPI mode

You wrote:

SPISettings(125000, MSBFIRST, SPI_MODE0)

SPI mode 0 seems to be pretty common. In this mode, the data lines are updated on the falling edge of the clock, and they are read on the rising edge. However, according to your sensor's datasheet, it appears this sensor works the other way around: it uses SPI mode 1. Your setting should reflect this.

Decoding the data

You wrote:

myValFromTempSensor = SPI.transfer(0x00);
myValFromTempSensor |= (uint8_t)SPI.transfer(0x00) << 8;

Here you are assuming the least significant byte is transmitted first. This is not the case: you get a stream of 16 bits with the most significant first. Thus, in terms of bytes, it's also MSB first.

Then, 6 of these 16 bits have to be discarded in order to get the actual 10 data bits. Since the Arduino has no 10-bit data type, and the number is signed, it has to be sign-extended to 16-bits. All this can be done in C/C++ by:

  • left-shifting one bit to the left (discarding the zero padding bit)
  • casting to a signed 16-bit integer
  • right-shifting (which gcc implements using sign extension) six bits to the right

Or, in code:

digitalWrite(Slave_PIN, HIGH);
uint16_t raw_data = SPI.transfer16(0);
digitalWrite(Slave_PIN, LOW);
int temperature_reading = (int16_t)(raw_data << 1) >> 6;

Edit: Here are more details about the data representation, and about the conversion to 16-bits done by the expression (int16_t)(raw_data << 1) >> 6.

According to the datasheet, the sensor gives the temperature as a 10-bit signed number in two's complement notation. If you fail to acknowledge the fact that the result is signed, and you interpret the 10 bits as an unsigned number, then a temperature of −1 °C will be interpreted as +255 °C, which is obviously very wrong.

Since it's a signed number, increasing the bit width of the temperature to 16 bits has to be done using sign extension, i.e. the missing bit positions have to be filled with copies of the leftmost bit (the “sign bit”).

The datasheet also shows that the sensor sends a stream of 16-bits as follows:

0 db9 db8 db7 db6 db5 db4 db3 db2 db1 db0 db0 db0 db0 db0 db0

where the first bit is a padding zero, then come the ten data bits db9 – db0, and then five extra copies of the last data bit. These extra copies can be seen in figure 3 (serial interface digram), and can also be inferred from the sentence “the SDO line remains in the state of the last bit of data clocked out of the AD7314”.

The line

uint16_t raw_data = SPI.transfer16(0);

puts this bit pattern into the variable raw_data, which is a 16-bit unsigned integer. Then, the operation raw_data << 1 shifts this pattern one bit position to the left, discarding the leftmost zero and padding with another zero on the right, which gives:

db9 db8 db7 db6 db5 db4 db3 db2 db1 db0 db0 db0 db0 db0 db0 0

This is then cast to a signed number, which doesn't change the bit pattern, but only the interpretation of the leftmost bit, which is now a sign bit. Then, the number is shifted by 6 bit positions to the right. Since this is an arithmetic shift, which does sign extension, the leftmost bit (the “sign bit”) is replicated during the shift, which yields:

db9 db9 db9 db9 db9 db9 db9 db8 db7 db6 db5 db4 db3 db2 db1 db0

And this is the expected result: the same numeric value that was originally stored in the “temperature value register” of the sensor, only represented as a 16-bit number.

  • I am not sure where you are getting 16 bits from rather than 11 bits (including leading zero) if i understood correctly. The datasheet mentions 11 bits being transferred during streams of 16 clock cycles. – user7187418 Jan 4 '18 at 14:11
  • @user7187418: At the hardware level, the SPI port can only handle multiples of 8 bits, and it reads an input bit on every clock cycle. At the library level, the transfer16() method sends 16 clocks pulses and returns an unsigned 16-bit number. – Edgar Bonet Jan 4 '18 at 14:36
  • So at the line of code : int temperature_reading = (int16_t)(raw_data << 1) >> 6; Does this mean i receive the 10 bit signed data, if so , i have 2 questions: 1) If i understood correctly, the transfer16() returns an unsigned 16 bit number does this mean that the 10 bit temperature data in the code mentioned above is an unsigned 10 bit reading ? (i need to read between 0 C and 60 C ) 2) Should i output the temperature data on to the 6 leds by reading the only the first 6 bits by using the bitRead() function as i am currently doing or is there another way that i am currently unaware of ? – user7187418 Jan 4 '18 at 16:55
  • 1) You seem confused bout binary number representations. See my extended answer. 2) Only you can know how you want to display the temperature to the end user. I would output through the serial port, at least during the debugging stage. – Edgar Bonet Jan 4 '18 at 18:57
1

This is not an answer to your question but I cannot add it as a remark since it involves code.

You can greatly reduce your code by using for loops, e.g.:

   pinMode(5, OUTPUT); //LED 3
   pinMode(6, OUTPUT); //LED 4
   pinMode(7, OUTPUT); //LED 5
   pinMode(8, OUTPUT); //LED 6
   pinMode(9, OUTPUT); //LED 7
   pinMode(10, OUTPUT);//LED 8  

to

   int n;
   for (n = 5; n < 11; n++)
   {
      pinMode(n, OUTPUT);
   }

And

digitalWrite(5,bitRead( myValFromTempSensor,0));
digitalWrite(6,bitRead( myValFromTempSensor,1));
digitalWrite(7,bitRead( myValFromTempSensor,2));
digitalWrite(8,bitRead( myValFromTempSensor,3));
digitalWrite(9,bitRead( myValFromTempSensor,4));
digitalWrite(10,bitRead( myValFromTempSensor,5));

to

int n = 0;
for (n = 0; n < 6; n++)
{
    digitalWrite(n + 5,bitRead( myValFromTempSensor, n));
}

And

  digitalWrite(5,0);
  digitalWrite(6,0);
  digitalWrite(7,0);
  digitalWrite(8,0);
  digitalWrite(9,0);
  digitalWrite(10,0);

to

int n;
for (n = 5; n < 11; n++)
{
    digitalWrite(n, 0);
}

Instead of hardcoded values 5 and 11 you can use defines, e.g.

#define LED_MIN 5
#define LED_MAX 11

and change

for (n = 5; n < 11; n++)

to

for (n = LED_MIN; n < LED_MAX; n++)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.