Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
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 hour681
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 • 52 followers
i'm an embedded system engineer
Contact

Comments

Please log in or sign up to comment.