Fetching 32 bits of Data over SPI on Arduino Due

Hello all,
I am trying to write a sketch to fetch SPI data from an Adafruit MAX31855 Thermocouple breakout board on an Arduino Due.

I'm pretty sure of what to do with the data from the various MAX31855 libraries out there once I have the data into an unsigned long variable. So what I am trying to accomplish is driving the specific chip select pin low, fetch 32 consecutive bits of data into an unsigned long and display that in serial to prove its working. If i grab the thermocouple with my fingers that value will change and then I can work from there.

My code wont compile and I'm a novice to these things so I need some help. I do know all the SPI parameters from the 31855 datasheet and have set it up correctly. My coding methodology just needs refinement. I majored in Aerospace Engineering so I had some EE and coding experience but not to this depth.

/*************************************************** 
 * Custom MAX31855 Library using Arduino Due
/****************************************************/

#include <SPI.h>

int SlavePin1 = 4; //Thermocouple #1

void setup() {
  Serial.begin(9600);
  pinMode(SlavePin1, OUTPUT);
  digitalWrite(SlavePin4, HIGH); //Disable TC1
  
  //SPI Init.
  SPI.begin(SlavePin1);                // Setup SPI to use pin 4 as CS.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SlavePin1,1);        // Use SPI Mode 1 (CPOL = 0, CPHA = 1)
  SPI.setClockDivider(SlavePin1, 21);  // Set SPI Clock divider to 21.(4MHz)
}

unsigned long ReadThermocouple(int cs){
  unsigned long data = 0; 
  digitalWrite(cs,LOW); 
  for(int i = 0; i < 32; i++) {
    data = SPI.transfer(cs); 
  }
  digitalWrite(cs,HIGH); 
  return data; 
}

void loop() {
  unsigned long c1 = ReadThermocouple(SlavePin1);
  Serial.println(c1);
  delay(1000);
}

Ok. I don't have specific documentation but

  1. usually you have to send a command to the device before it sends back your data
  2. each transfer gets you 8 bits so you need 4, not 32.
  3. you need to combine your 4 transfers to make your 32 bit long, something like
    data=data<<8+spi.transfer(cs)

Normally correct but there is no MOSI for this device it reads out only. Quoting directly from the datasheet "Drive CS low and apply a clock signal at SCK to read the results at SO" The limit of the chip is 5 MHz so I set it at 4 as you can see by the code above. This is really the source of my confusion as to how to resolve this. There is no SPI.read() function like SPI.transfer()

So try driving cs low and do 4 transfers. Just print them individually and see if they make any sense.

And sorry for not reading well enough, what do you mean your code won't compile?

rcullan:
Normally correct but there is no MOSI for this device it reads out only. Quoting directly from the datasheet "Drive CS low and apply a clock signal at SCK to read the results at SO" The limit of the chip is 5 MHz so I set it at 4 as you can see by the code above. This is really the source of my confusion as to how to resolve this. There is no SPI.read() function like SPI.transfer()

Doing an SPI.transfer will apply a clock signal and read the results.

SPI.transfer works, even if the device doesn't support reading/writing.

For example, I used SPI.transfer to send to a 595 shift register, which itself does not return any data. Similarly you could use it to read from something that does not receive data.

Okay I am trying from the suggestions provided but am getting limited results.

Here is the code I am trying, but it is returning is 25 bits. I am sure I am making an idiot mistake here

The serial output is
"1000000010000000100000001"

/*************************************************** 
 * Custom MAX31855 Library using hardware based SPI.h
/****************************************************/

#include <SPI.h>

union FourByte{
    unsigned long bit32;
    unsigned int bit16[2];
    unsigned char bit8[4];
};

int SlavePin1 = 4; //Thermocouple #1
long unsigned data;

void setup() {
  Serial.begin(9600);
  pinMode(SlavePin1, OUTPUT);
  digitalWrite(SlavePin1, HIGH); //Disable SS
  SPI.begin(SlavePin1);                // Setup SPI to use pin 4 as CS.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SlavePin1,1);        // Use SPI Mode 1 (CPOL = 0, CPHA = 1)
  SPI.setClockDivider(SlavePin1, 21);  // Set SPI Clock divider to 21.(4MHz)
  
}

unsigned long transfer(unsigned long value){
  FourByte data = {value};
  for(byte i = 0; i < 4; i++){
    data.bit8[i] = SPI.transfer(SlavePin1, data.bit8[i]);
    delay(1);
  }
  return data.bit32;
}

void loop() {
  digitalWrite(SlavePin1, LOW);
  unsigned long someValue = 0xFFFFFFFEUL; //No MOSI on MAX31855, dosent matter what is sent.
  data = transfer(someValue);
  Serial.println(data, BIN);
  digitalWrite(SlavePin1, HIGH);
  delay(1000);
}

Oh one last thing.... the Datasheet for the MAX31855 states the following:

Drive CS low to output the first bit on the SO pin. A
complete serial-interface read of the cold-junction compensated thermocouple temperature requires 14 clock
cycles. Thirty-two clock cycles are required to read both
the thermocouple and reference junction temperatures

With my for loop only cycling 3 times, is this my issue? If so how can my code be modified to the same end result of bringing out all 32 binary bits in the serial interface. If thats working I can get to my final goal from there.

You send data. The return value is useless. You send data, the return value is that of the previously sent value.

You seem to expect the first response to be useful information.

You send a value defined in HEX and print the result in binary. Why? I think you could see a pattern if the sent value and the printed value used the same base.

Paul,
I edited a key note above.

My desire to print in Binary results in the data sheet. The 32 bits contains temperature data, fault data, etc all in a nice table. If I can see what 1 and 0's are on/off I can debug alot more information.

I tried it... changing my loop to the following didnt change the serial readout.

void loop() {
  digitalWrite(SlavePin1, LOW);
  unsigned long someValue = 0xFFFFFFFEUL;
  transfer(someValue);
  readback = transfer(0);
  data = transfer(someValue);
  Serial.println(data, BIN);
  digitalWrite(SlavePin1, HIGH);
  delay(1000);
}

My desire to print in Binary results in the data sheet. The 32 bits contains temperature data, fault data, etc all in a nice table. If I can see what 1 and 0's are on/off I can debug alot more information.

Then, the specific value you are sending is meaningless?

Bingo.... there is no MOSI for this part above I stated

rcullan:
Normally correct but there is no MOSI for this device it reads out only. Quoting directly from the datasheet "Drive CS low and apply a clock signal at SCK to read the results at SO" The limit of the chip is 5 MHz so I set it at 4 as you can see by the code above.

I don't see how the code above addresses the issue. Here is something closer to what you said it needed:

#include <SPI.h>

const byte SlavePin1 = 10;

void setup ()
  {
  Serial.begin (115200);
  }  // end of setup

void loop ()
  {
  union 
    {
    byte byteResult [4];  
    unsigned long longResult;
    } temperature;
  
  digitalWrite(SlavePin1, LOW);

  // read all 4 bytes
  for (byte i = 0; i < 4; i++)
    temperature.byteResult [i] = SPI.transfer (0);

  digitalWrite(SlavePin1, HIGH);
  
  Serial.println (temperature.longResult);
  delay(1000);
}

You said the device returns four bytes once CS is asserted. So, you do 4 x SPI.transfer. Since you don't have anything connected to MOSI then you may as well send zero as anything else. If the endian-ness is the other way around, read into the array in the other order.

Nick,
Thank you for the continued help, and the code snippet. I tried this (with some adding debugging serial printouts) and the for loop dosent seem to be looping.

Here is the serial output:
Starting Program
SPI Variables Established
Deselect MAX318555 Chip
Loop Started
MAX31855 Selected
For Loop #
0

And the exact code I am using

#include <SPI.h>

int SlavePin1 = 4; //Thermocouple #1

void setup() {
  Serial.begin(9600);
  Serial.println("Starting Program");
  //SPI Setup
  SPI.begin(SlavePin1);                // Setup SPI to use pin 4 as CS.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SlavePin1,1);        // Use SPI Mode 1 (CPOL = 0, CPHA = 1)
  SPI.setClockDivider(SlavePin1, 21);  // Set SPI Clock divider to 21.(4MHz)
  Serial.println("SPI Variables Established");
  digitalWrite(SlavePin1, HIGH);   // Deselect MAX31855 chip
  Serial.println("Deselect MAX318555 Chip");
}

void loop() {
  Serial.println("Loop Started");
  union {
    byte byteResult [4];  
    unsigned long longResult;
  } temperature;
  
  digitalWrite(SlavePin1, LOW);
  Serial.println("MAX31855 Selected");

  // read all 4 bytes
  for (byte i = 0; i < 4; i++) {
    Serial.println("For Loop #");
    Serial.println(i);
    temperature.byteResult [i] = SPI.transfer (0);
    Serial.println("Data Transfered ");
    delay(1);
  }

  digitalWrite(SlavePin1, HIGH);
  Serial.println("Deselect MAX318555 Chip");
    Serial.println (temperature.longResult);
  delay(1000);
}

Also here is the Serial instructions from the data sheet (page 9)

I believe I have the serial interface established correctly in the listed parameters above. But I am not an expert.

Drive CS low and apply a clock signal at SCK to read the results at SO. Conversions are always being performed in the background. The fault and temperature data are only be updated when CS is high. Drive CS low to output the first bit on the SO pin. A complete serial-interface read of the cold-junction compensated thermocouple temperature requires 14 clock cycles. Thirty-two clock cycles are required to read both the thermocouple and reference junction temperatures (Table 2 and Table 3.) The first bit, D31, is the thermocouple temperature sign bit, and is presented to the SO pin within tDV of the falling edge of CS. Bits D[30:18] contain the converted temperature in the order of MSB to LSB, and are presented to the SO pin within tD0 of the falling edge of SCK. Bit D16 is normally low and goes high when the thermocouple input is open or shorted to GND or VCC. The reference junction temperature data begins with D15. CS can be taken high at any point while clocking out conversion data. If T+ and T- are unconnected, the thermocouple temperature sign bit (D31) is 0, and the remainder of the thermocouple temperature value (D[30:18]) is 1.

rcullan:
Nick,
Thank you for the continued help, and the code snippet. I tried this (with some adding debugging serial printouts) and the for loop dosent seem to be looping.

I don't see anything in your code which would stop the loop looping. However I have not actually used a Due. Perhaps I should move this thread to the Due section?

I think I will make a shorter post over there summarazing what I have learned thus far...

One interesting thing is I modified the code slightly to be used on a MEGA 2560 that I also have and the for loop is working but it just returns a 0 on the output everytime.

In any event I would ask one last question on this. How would the code be modified to read bit by bit from the thermocouple? (I dont even know if this is standard with the SPI library) For example, the 1st bit is the sign of the temperature reading, the next 13 are the temperature over the thermocouple. There is a reserved and fault bit in between, and then the next 12 bits are the chips internal data. The remaining 4 bits are fault detectors.

So really what I am trying to do on a transfer level is read the 1st bit as 0 or 1, negative or positive respectively. Read bits 2-14 into a variable for the thermocouple temperature. Then read bits 17-29 for the chip temperature. There are various examples out in the public domain of how to convert the binary data into digestible temperature data formats. But I cant read the data. Sorry if this is a naive question, but I am trying to learn as much as I can here

I wouldn't be attempting to read bit by bit. It adds up to 32 bits for a reason, and you are no doubt supposed to read all 32 and break them down in your program.