Thermistor and Thermocouple Temperature Logger Part 2

temp_logger_thermistor_test

 

This week in the epic adventure I like to call Thermistor and Thermocouple Temperature Logger Part 2, I took a look at the thermistor portion of my circuit.

[Read more…]

Thermistor and Thermocouple Temperature Logger

max31855_spi

(Image: scope capture of three T type thermocouples being read with SPI and the MAX31855 ICs)

We’ve been developing a barbecue controller as part of an R&D project.  My portion of the project is interfacing the temperature sensors to the overall system.  I’ve decided to include both thermistors and thermocouple sensors, as well as battery backup and data logging functions.  In the end the project won’t need all of that but  I wanted to come up with something “stand-alone” I could develop.

Thus far I’ve designed the schematic and first revision circuit board.  I’ve been able to test the temperature sensors, but not much else.  I did want to share the MAX31855 interface, which I found pretty cool and a definite time saver.  The Maxim Integrated Circuits’ MAX31855 (datasheet) is a cold-junction compensated thermocouple interface.  It provides a 14-bit signed digital value that is temperature in 0.25 degree C increments with +/-2 degree C accuracy.  You buy a chip specific to the thermocouple you’re using.  In our case I placed 4 of these ICs on the board and selected “T” type thermocouples.  The device uses an SPI interface, although it only outputs data.  You can see a partial schematic below (click to enlarge).

temp_logger_max31855_schematic

 

For the microcontroller interface I used the PIC16F1789, a Microchip product.  This part has 12-bit analog to digital converters and operates between 2.5V-5.5V, which works well with the battery powered system I’ve envisioned,

Configuring the SPI interface is pretty straightforward, and here is the code I used to do this.

/******************************************************************************/
/* Configure_SPIPort:
   Configures registers used to send and receive using the SPI hardware       */
/******************************************************************************/
void Configure_SPIPort(void)
{
    SSPSTATbits.SMP = 0;  // Input data sampled at middle of data output time
    SSPSTATbits.CKE = 0;   // Transmit occurs on transition from Idle to active clock state

    SSPCONbits.CKP = 0; //  Idle state for clock is a low level
    SSPCONbits.SSPM = 2; //SPI Master mode, clock = FOSC/64
    SSPCONbits.SSPEN = 1; // Enables serial port and configures SCK, SDO, SDI and SS as the source of the serial port pins

    SSPCON2 = 0;
    SSPCON3 = 0;
}

I’m running the SPI at 500KHz, which is a little fast for some SPI ICs, but the MAX31855 is spec’ed at 5MHz max speed.  The actual communication with a MAX31855 is shown in the code below.  The code is repeated for each thermocouple (with different variables).…

/******************************************************************************/
/* Read_Thermocouples:
 Reads temperature signals from MAX31855 using the SPI interface.  
 /******************************************************************************/
void Read_Thermocouples(void)
{  
    _CS_TEMP1 = 0;  // select the first MAX31855
    SSPBUF = 0xAA;          // doesn't matter what we write but H'AA' easy to see on scope
    while(SSPSTATbits.BF == 0); // wait for 8 bits to clock in
    MyTemp.B[1] = SSPBUF; // save data
    SSPBUF = 0xF0; // doesn't matter what we write but H'F0' easy to see on scope differs / from H'AA'
    while(SSPSTATbits.BF == 0); // wait for 8 bits to clock in
    MyTemp.B[0] = SSPBUF; // save data
    NV.Ints.Thermocouple1 = MyTemp.W[0]; // use union to combine bytes in 16 bit value, stick in reg
    _CS_TEMP1 = 1;   // de-select the MAX31855

    INDICATOR.Bits.THERMO1 = 0; // clear my fault flag
    if (NV.Ints.Thermocouple1 & 0x1 == 1) // check to see if an fault flag was set by MAX31855
        INDICATOR.Bits.THERMO1 = 1; // if so, then set flag in my structure
    NV.Ints.Thermocouple1 = NV.Ints.Thermocouple1>>2; // shift temperature data two to the right
    NV.Ints.Thermocouple1 = NV.Ints.Thermocouple1 & 0x3fff; // make sure upper 2 bits are clear
    if ((NV.Ints.Thermocouple1 & 0x2000) != 0 ) // check sign bit
        NV.Ints.Thermocouple1 += 0xc000; // if sign bit is set then extend sign to all 16 bits
    if (NV.Ints.Thermocouple1 == 0x1fff) // check to see if thermocouple is attached
        INDICATOR.Bits.THERMO1 = 1; // if not then set flag

The code deserves some decryption.  Here are some descriptions of the variables used above.
_CS_TEMP1: A constant that refers to the chip select pin.
INDICATOR.Bits.THERMO1: A variable structure that maintains fault conditions and other flags in the firmware.
NV.Ints.Thermocouple1: A variable structure of integers that can be stored in EEPROM and accessed via a serial UART, this is where the temperature data is stored.
MyTemp: This variable can be a little confusing in this context. It is not related to temperature.  MyTemp is a variable union that I often use as a temporary register to quickly break apart or assemble other variables.  For example, I can read two bytes from the SPI port and place them in byte sized members of the union and then use an integer sized member of the union to copy the bytes as a single 16-bit value.

union LongUnion
    {
    long LW;
    unsigned long uLW;
    int W[2];
    unsigned int uW[2];
    char B[4];
    unsigned char uB[4];
    };
union LongUnion
MyTemp;

In my firmware I select the MAX31855 by asserting the chip select pin and then sending hexadecimal 0xAA and 0xF0.  The data I send doesn’t matter, but the PIC16F1789 needs to have the SSBUF register written to in order to begin clocking data out and reading data in.  The SSPSTATbits.BF variable is defined by Microchip in this part’s header file, and will be set when the SPI receive buffer is full.  After 16 bits of data are read the chip is de-selected.  The image at the head of this blog entry shows a scope capture of this process.

Once I’ve read 16 bits I need to convert the data to a temperature value.  Not all of the bits relate to temperature so I need to adjust the values I read somewhat.  Bit 0 is a fault flag (set if there is a fault) and bit 1 is reserved (read as 0).  Additionally, bit 15 is the sign bit (using two’s compliment format this bit is set if the value is negative).  In the firmware I check the fault flag (bit 0) and then shift the 16-bits I read from the MAX31855 two places to the right.  This leaves the sign flag in the 13th bit location.  I clear the upper two bits of the register which is kind of an old school operation.  In assembly code when you shift data the carry bit can be shifted into a register.  In C I’ve never seen a “1” get shifted in, but I’ve been doing this for so many years I just can’t stop.   I then check the sign bit and if it is set I set the two highest bits of the temperature data to maintain the two’s compliment formatting.

This diagram shows the process more visually.

max31855_data

There are some additional points related to the MAX31855 that deserve mention.

First, if the thermocouple is disconnected the datasheet states that the MAX31855 will respond with a cleared sign bit and all other temperature data bits set (0x1FFF in hexadecimal).  I didn’t see that, but also didn’t delve into why it didn’t happen.

There are an additional 16-bits of data that can be read from the MAX31855.  You would read this data by clocking in 16 more bits before setting the chip select pin.   This additional data includes the 12-bit internal temperature of the IC and more details on the nature of any fault that occurs.

Lastly, the MAX31855 calculates temperature when the chip is not selected for SPI communication.  It also requires a maximum of 100mS to take the measurements.  Therefore, the fastest you can sample a thermocouple using this IC is 10Hz.