The Embedded Things
Published

STM32 Integration with MAX31856 Thermocouple Converter

Learn how to seamlessly integrate the STM32 microcontroller with the MAX31856 Type K thermocouple-to-digital converter

IntermediateFull instructions provided1 hour320
STM32 Integration with MAX31856 Thermocouple Converter

Things used in this project

Software apps and online services

STMicroelectronics STM32CubeIDE
STMicroelectronics STM32CubeMX
Proteus 8

Story

Read more

Code

Max31856.h

C/C++
#ifndef MAX31856_H
#define MAX31856_H

#include "main.h"
#include <stdio.h>
#include <stdbool.h>
#include <math.h>

#define MAX31856_CR0_REG          0x00         ///< Config 0 register
#define MAX31856_CR0_AUTOCONVERT  0x80 ///< Config 0 Auto convert flag
#define MAX31856_CR0_1SHOT        0x40       ///< Config 0 one shot convert flag
#define MAX31856_CR0_OCFAULT1     0x20    ///< Config 0 open circuit fault 1 flag
#define MAX31856_CR0_OCFAULT0     0x10    ///< Config 0 open circuit fault 0 flag
#define MAX31856_CR0_CJ           0x08          ///< Config 0 cold junction disable flag
#define MAX31856_CR0_FAULT        0x04       ///< Config 0 fault mode flag
#define MAX31856_CR0_FAULTCLR     0x02    ///< Config 0 fault clear flag

#define MAX31856_CR1_REG          0x01  ///< Config 1 register
#define MAX31856_MASK_REG         0x02 ///< Fault Mask register
#define MAX31856_CJHF_REG         0x03 ///< Cold junction High temp fault register
#define MAX31856_CJLF_REG         0x04 ///< Cold junction Low temp fault register
#define MAX31856_LTHFTH_REG                                                    \
  0x05 ///< Linearized Temperature High Fault Threshold Register, MSB
#define MAX31856_LTHFTL_REG                                                    \
  0x06 ///< Linearized Temperature High Fault Threshold Register, LSB
#define MAX31856_LTLFTH_REG                                                    \
  0x07 ///< Linearized Temperature Low Fault Threshold Register, MSB
#define MAX31856_LTLFTL_REG                                                    \
  0x08 ///< Linearized Temperature Low Fault Threshold Register, LSB
#define MAX31856_CJTO_REG 0x09  ///< Cold-Junction Temperature Offset Register
#define MAX31856_CJTH_REG 0x0A  ///< Cold-Junction Temperature Register, MSB
#define MAX31856_CJTL_REG 0x0B  ///< Cold-Junction Temperature Register, LSB
#define MAX31856_LTCBH_REG 0x0C ///< Linearized TC Temperature, Byte 2
#define MAX31856_LTCBM_REG 0x0D ///< Linearized TC Temperature, Byte 1
#define MAX31856_LTCBL_REG 0x0E ///< Linearized TC Temperature, Byte 0
#define MAX31856_SR_REG 0x0F    ///< Fault Status Register

#define MAX31856_FAULT_CJRANGE                                                 \
  0x80 ///< Fault status Cold Junction Out-of-Range flag
#define MAX31856_FAULT_TCRANGE                                                 \
  0x40 ///< Fault status Thermocouple Out-of-Range flag
#define MAX31856_FAULT_CJHIGH                                                  \
  0x20 ///< Fault status Cold-Junction High Fault flag
#define MAX31856_FAULT_CJLOW 0x10 ///< Fault status Cold-Junction Low Fault flag
#define MAX31856_FAULT_TCHIGH                                                  \
  0x08 ///< Fault status Thermocouple Temperature High Fault flag
#define MAX31856_FAULT_TCLOW                                                   \
  0x04 ///< Fault status Thermocouple Temperature Low Fault flag
#define MAX31856_FAULT_OVUV                                                    \
  0x02 ///< Fault status Overvoltage or Undervoltage Input Fault flag
#define MAX31856_FAULT_OPEN                                                    \
  0x01 ///< Fault status Thermocouple Open-Circuit Fault flag


#define SPI_DELAY  0xff
extern bool initialized;

/** Noise filtering options enum. Use with setNoiseFilter() */
typedef enum {
  MAX31856_NOISE_FILTER_50HZ,
  MAX31856_NOISE_FILTER_60HZ
} max31856_noise_filter_t;

/** Multiple types of thermocouples supported */
typedef enum {
  MAX31856_TCTYPE_B  = 0b0000,
  MAX31856_TCTYPE_E  = 0b0001,
  MAX31856_TCTYPE_J  = 0b0010,
  MAX31856_TCTYPE_K  = 0b0011,
  MAX31856_TCTYPE_N  = 0b0100,
  MAX31856_TCTYPE_R  = 0b0101,
  MAX31856_TCTYPE_S  = 0b0110,
  MAX31856_TCTYPE_T  = 0b0111,
  MAX31856_VMODE_G8  = 0b1000,
  MAX31856_VMODE_G32 = 0b1100,
} max31856_thermocoupletype_t;

/** Temperature conversion mode */
typedef enum {
  MAX31856_ONESHOT,
  MAX31856_ONESHOT_NOWAIT,
  MAX31856_CONTINUOUS
} max31856_conversion_mode_t;



bool begin(void);


uint8_t readRegister8(uint8_t addr);
uint16_t readRegister16(uint8_t addr);
uint32_t readRegister24(uint8_t addr);
void writeRegister8(uint8_t addr, uint8_t reg);

void setConversionMode(max31856_conversion_mode_t mode);
max31856_conversion_mode_t getConversionMode(void);

void setThermocoupleType(max31856_thermocoupletype_t type);
max31856_thermocoupletype_t getThermocoupleType(void);

uint8_t readFault(void);

void triggerOneShot(void);
bool conversionComplete(void);

float readCJTemperature(void);
float readThermocoupleTemperature(void);

void setTempFaultThreshholds(float flow, float fhigh);
void setColdJunctionFaultThreshholds(int8_t low, int8_t high);
void setNoiseFilter(max31856_noise_filter_t noiseFilter);

void writeRegister8(uint8_t addr, uint8_t reg);
void readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n);

uint8_t readRegister8(uint8_t addr);
uint16_t readRegister16(uint8_t addr);
uint32_t readRegister24(uint8_t addr);

#endif // MAX31856_H

Max31856.c

C/C++
#include "Max31856.h"

extern SPI_HandleTypeDef hspi1;
bool initialized = false;
max31856_conversion_mode_t conversionMode;



bool begin(void) {
  if (HAL_SPI_Init(&hspi1) == HAL_OK) {initialized = true;}
  else {initialized = false;}

  // assert on any fault
  writeRegister8(MAX31856_MASK_REG, 0x0);

  // enable open circuit fault detection
  writeRegister8(MAX31856_CR0_REG, MAX31856_CR0_OCFAULT0);

  // set cold junction temperature offset to zero
  writeRegister8(MAX31856_CJTO_REG, 0x0);

  // set Type K by default
  setThermocoupleType(MAX31856_TCTYPE_K);

  // set One-Shot conversion mode
  setConversionMode(MAX31856_ONESHOT);

  return initialized;
}

max31856_conversion_mode_t getConversionMode(void) {
  return conversionMode;
}

void setConversionMode(max31856_conversion_mode_t mode) {
  conversionMode = mode;
  uint8_t t = readRegister8(MAX31856_CR0_REG); // get current register value
  if (conversionMode == MAX31856_CONTINUOUS) {
    t |= MAX31856_CR0_AUTOCONVERT; // turn on automatic
    t &= ~MAX31856_CR0_1SHOT;      // turn off one-shot
  } else {
    t &= ~MAX31856_CR0_AUTOCONVERT; // turn off automatic
    t |= MAX31856_CR0_1SHOT;        // turn on one-shot
  }
  writeRegister8(MAX31856_CR0_REG, t); // write value back to register
}

void setThermocoupleType(max31856_thermocoupletype_t type) {
  uint8_t t = readRegister8(MAX31856_CR1_REG);
  t &= 0xF0; // mask off bottom 4 bits
  t |= (uint8_t)type & 0x0F;
  writeRegister8(MAX31856_CR1_REG, t);
}
max31856_thermocoupletype_t getThermocoupleType(void) {
  uint8_t t = readRegister8(MAX31856_CR1_REG);
  t &= 0x0F;

  return (max31856_thermocoupletype_t)(t);
}
uint8_t readFault(void) {
  return readRegister8(MAX31856_SR_REG);
}
void setColdJunctionFaultThreshholds(int8_t low,int8_t high) {
  writeRegister8(MAX31856_CJLF_REG, low);
  writeRegister8(MAX31856_CJHF_REG, high);
}


uint8_t readRegister8(uint8_t addr) {
  uint8_t ret = 0;
  readRegisterN(addr, &ret, 1);

  return ret;
}
void setNoiseFilter(max31856_noise_filter_t noiseFilter) {
  uint8_t t = readRegister8(MAX31856_CR0_REG);
  if (noiseFilter == MAX31856_NOISE_FILTER_50HZ) {
    t |= 0x01;
  } else {
    t &= 0xfe;
  }
  writeRegister8(MAX31856_CR0_REG, t);
}

void setTempFaultThreshholds(float flow, float fhigh) {
  int16_t low, high;

  flow *= 16;
  low = flow;

  fhigh *= 16;
  high = fhigh;

  writeRegister8(MAX31856_LTHFTH_REG, high >> 8);
  writeRegister8(MAX31856_LTHFTL_REG, high);

  writeRegister8(MAX31856_LTLFTH_REG, low >> 8);
  writeRegister8(MAX31856_LTLFTL_REG, low);
}

void triggerOneShot(void) {

  if (conversionMode == MAX31856_CONTINUOUS)
    return;

  uint8_t t = readRegister8(MAX31856_CR0_REG); // get current register value
  t &= ~MAX31856_CR0_AUTOCONVERT;              // turn off autoconvert
  t |= MAX31856_CR0_1SHOT;                     // turn on one-shot
  writeRegister8(MAX31856_CR0_REG, t);         // write value back to register
                                       // conversion starts when CS goes high
}

bool conversionComplete(void) {

  if (conversionMode == MAX31856_CONTINUOUS)
    return true;
  return !(readRegister8(MAX31856_CR0_REG) & MAX31856_CR0_1SHOT);
}

float readCJTemperature(void) {

  return readRegister16(MAX31856_CJTH_REG) / 256.0;
}



float readThermocoupleTemperature(void) {

  // for one-shot, make it happen
  if (conversionMode == MAX31856_ONESHOT) {
    triggerOneShot();
    uint32_t start = HAL_GetTick();
    while (!conversionComplete()) {
      if (HAL_GetTick() - start > 250) //250
        return NAN;
      HAL_Delay(10);

    }
  }

  // read the thermocouple temperature registers (3 bytes)
  int32_t temp24 = readRegister24(MAX31856_LTCBH_REG);
  // and compute temperature
  if (temp24 & 0x800000) {
    temp24 |= 0xFF000000; // fix sign
  }

  temp24 >>= 5; // bottom 5 bits are unused

  return temp24 * 0.0078125;
}

uint16_t readRegister16(uint8_t addr) {
  uint8_t buffer[2] = {0, 0};
  readRegisterN(addr, buffer, 2);

  uint16_t ret = (uint16_t)buffer[0]; // Cast to uint16_t before left shift
  ret <<= 8;
  ret |= buffer[1];

  return ret;
}



uint32_t readRegister24(uint8_t addr) {
  uint8_t buffer[3] = {0, 0, 0};
  readRegisterN(addr, buffer, 3);

  uint32_t ret = (uint32_t) buffer[0];
  ret <<= 8;
  ret |= buffer[1];
  ret <<= 8;
  ret |= buffer[2];

  return ret;
}
void readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
  addr &= 0x7F; // MSB=0 for read, make sure top bit is not set

  HAL_GPIO_WritePin(GPIOA , SPI1_CS_Pin, GPIO_PIN_RESET);
  HAL_SPI_Transmit(&hspi1, &addr, 1, SPI_DELAY);
  HAL_SPI_Receive(&hspi1, buffer, n, SPI_DELAY);
  HAL_GPIO_WritePin(GPIOA , SPI1_CS_Pin, GPIO_PIN_SET);
}
void writeRegister8(uint8_t addr, uint8_t data) {
  addr |= 0x80; // MSB=1 for write, make sure top bit is set

  uint8_t buffer[2] = {addr, data};

  HAL_GPIO_WritePin(GPIOA, SPI1_CS_Pin, GPIO_PIN_RESET);
  HAL_SPI_Transmit(&hspi1, buffer, 2, SPI_DELAY);  // Corrected size to 2
  HAL_GPIO_WritePin(GPIOA, SPI1_CS_Pin, GPIO_PIN_SET);
}

Credits

The Embedded Things
29 projects • 45 followers
i'm an embedded system engineer

Comments