The Embedded Things
Published

STM32 & MAX31865: SPI Temperature Sensing

Explore the seamless integration of STM32 microcontrollers with the MAX31865 RTD-to-digital converter through SPI communication

IntermediateFull instructions provided1 hour442
STM32 & MAX31865: SPI Temperature Sensing

Things used in this project

Software apps and online services

STMicroelectronics STM32CubeIDE
STMicroelectronics STM32CubeMX
proteus 8

Story

Read more

Code

MAX31865.h

C/C++
/*
* MAX31865.h
*
*  Created on: Feb 19, 2024
*      Author: Physiomedica_dev
*/

#ifndef INC_MAX31865_H_
#define INC_MAX31865_H_

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

#define MAX31865_CONFIG_REG 		0x00
#define MAX31865_CONFIG_BIAS 		0x80
#define MAX31865_CONFIG_MODEAUTO	0x40
#define MAX31865_CONFIG_MODEOFF 	0x00
#define MAX31865_CONFIG_1SHOT 		0x20
#define MAX31865_CONFIG_3WIRE 		0x10
#define MAX31865_CONFIG_24WIRE 		0x00
#define MAX31865_CONFIG_FAULTSTAT 	0x02
#define MAX31865_CONFIG_FILT50HZ 	0x01
#define MAX31865_CONFIG_FILT60HZ 	0x00

#define MAX31865_RTDMSB_REG 0x01
#define MAX31865_RTDLSB_REG 0x02
#define MAX31865_HFAULTMSB_REG 0x03
#define MAX31865_HFAULTLSB_REG 0x04
#define MAX31865_LFAULTMSB_REG 0x05
#define MAX31865_LFAULTLSB_REG 0x06
#define MAX31865_FAULTSTAT_REG 0x07

#define MAX31865_FAULT_HIGHTHRESH 0x80
#define MAX31865_FAULT_LOWTHRESH  0x40
#define MAX31865_FAULT_REFINLOW   0x20
#define MAX31865_FAULT_REFINHIGH  0x10
#define MAX31865_FAULT_RTDINLOW   0x08
#define MAX31865_FAULT_OVUV 	  0x04

#define RTD_A 3.9083e-3
#define RTD_B -5.775e-7

#define SPI_DELAY 0xFF
extern bool initialized;


typedef enum max31865_numwires {
MAX31865_2WIRE = 0,
MAX31865_3WIRE = 1,
MAX31865_4WIRE = 0
} max31865_numwires_t;

typedef enum {
MAX31865_FAULT_NONE = 0,
MAX31865_FAULT_AUTO,
MAX31865_FAULT_MANUAL_RUN,
MAX31865_FAULT_MANUAL_FINISH
} max31865_fault_cycle_t;


bool begin(max31865_numwires_t wires);
uint8_t readFault(max31865_fault_cycle_t fault_cycle);

void clearFault(void);
uint16_t readRTD();

void setThresholds(uint16_t lower, uint16_t upper);
uint16_t getLowerThreshold(void);
uint16_t getUpperThreshold(void);

void setWires(max31865_numwires_t wires);
void autoConvert(bool b);
void enable50Hz(bool b);
void enableBias(bool b);
float temperature(float RTDnominal, float refResistor);
float calculateTemperature(uint16_t RTDraw, float RTDnominal,
					 float refResistor);
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);



#endif /* INC_MAX31865_H_ */

MAX31865.c

C/C++
/*
 * MAX31865.c
 *
 *  Created on: Feb 19, 2024
 *      Author: Physiomedica_dev
 */

#include "MAX31865.h"

extern SPI_HandleTypeDef hspi1;
bool initialized = false;

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

  setWires(wires);
  enableBias(false);
  autoConvert(false);
  setThresholds(0, 0xFFFF);
  clearFault();

  // print("config: ");
  // println(readRegister8(MAX31865_CONFIG_REG), HEX);
  return initialized;
}

uint8_t readFault(max31865_fault_cycle_t fault_cycle) {
  if (fault_cycle) {
    uint8_t cfg_reg = readRegister8(MAX31865_CONFIG_REG);
    cfg_reg &= 0x11; // mask out wire and filter bits
    switch (fault_cycle) {
    case MAX31865_FAULT_AUTO:
      writeRegister8(MAX31865_CONFIG_REG, (cfg_reg | 0b10000100));
      HAL_Delay(1);
      break;
    case MAX31865_FAULT_MANUAL_RUN:
      writeRegister8(MAX31865_CONFIG_REG, (cfg_reg | 0b10001000));
      return 0;
    case MAX31865_FAULT_MANUAL_FINISH:
      writeRegister8(MAX31865_CONFIG_REG, (cfg_reg | 0b10001100));
      return 0;
    case MAX31865_FAULT_NONE:
    default:
      break;
    }
  }
  return readRegister8(MAX31865_FAULTSTAT_REG);
}

void clearFault(void) {
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  t &= ~0x2C;
  t |= MAX31865_CONFIG_FAULTSTAT;
  writeRegister8(MAX31865_CONFIG_REG, t);
}

void enableBias(bool b) {
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  if (b) {
    t |= MAX31865_CONFIG_BIAS; // enable bias
  } else {
    t &= ~MAX31865_CONFIG_BIAS; // disable bias
  }
  writeRegister8(MAX31865_CONFIG_REG, t);
}

void autoConvert(bool b) {
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  if (b) {
    t |= MAX31865_CONFIG_MODEAUTO; // enable autoconvert
  } else {
    t &= ~MAX31865_CONFIG_MODEAUTO; // disable autoconvert
  }
  writeRegister8(MAX31865_CONFIG_REG, t);
}

void enable50Hz(bool b) {
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  if (b) {
    t |= MAX31865_CONFIG_FILT50HZ;
  } else {
    t &= ~MAX31865_CONFIG_FILT50HZ;
  }
  writeRegister8(MAX31865_CONFIG_REG, t);
}

void setThresholds(uint16_t lower, uint16_t upper) {
  writeRegister8(MAX31865_LFAULTLSB_REG, lower & 0xFF);
  writeRegister8(MAX31865_LFAULTMSB_REG, lower >> 8);
  writeRegister8(MAX31865_HFAULTLSB_REG, upper & 0xFF);
  writeRegister8(MAX31865_HFAULTMSB_REG, upper >> 8);
}

uint16_t getLowerThreshold(void) {
  return readRegister16(MAX31865_LFAULTMSB_REG);
}

uint16_t getUpperThreshold(void) {
  return readRegister16(MAX31865_HFAULTMSB_REG);
}

void setWires(max31865_numwires_t wires) {
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  if (wires == MAX31865_3WIRE) {
    t |= MAX31865_CONFIG_3WIRE;
  } else {
    // 2 or 4 wire
    t &= ~MAX31865_CONFIG_3WIRE;
  }
  writeRegister8(MAX31865_CONFIG_REG, t);
}

float temperature(float RTDnominal, float refResistor) {
  return calculateTemperature(readRTD(), RTDnominal, refResistor);
}

float calculateTemperature(uint16_t RTDraw, float RTDnominal,float refResistor) {
  float Z1, Z2, Z3, Z4, Rt, temp;

  Rt = RTDraw;
  Rt /= 32768;
  Rt *= refResistor;

  // Serial.print("\nResistance: "); Serial.println(Rt, 8);

  Z1 = -RTD_A;
  Z2 = RTD_A * RTD_A - (4 * RTD_B);
  Z3 = (4 * RTD_B) / RTDnominal;
  Z4 = 2 * RTD_B;

  temp = Z2 + (Z3 * Rt);
  temp = (sqrt(temp) + Z1) / Z4;

  if (temp >= 0)
    return temp;

  // ugh.
  Rt /= RTDnominal;
  Rt *= 100; // normalize to 100 ohm

  float rpoly = Rt;

  temp = -242.02;
  temp += 2.2228 * rpoly;
  rpoly *= Rt; // square
  temp += 2.5859e-3 * rpoly;
  rpoly *= Rt; // ^3
  temp -= 4.8260e-6 * rpoly;
  rpoly *= Rt; // ^4
  temp -= 2.8183e-8 * rpoly;
  rpoly *= Rt; // ^5
  temp += 1.5243e-10 * rpoly;

  return temp;
}

uint16_t readRTD(void) {
  clearFault();
  enableBias(true);
  HAL_Delay(10);
  uint8_t t = readRegister8(MAX31865_CONFIG_REG);
  t |= MAX31865_CONFIG_1SHOT;
  writeRegister8(MAX31865_CONFIG_REG, t);
  HAL_Delay(65);

  uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);

  enableBias(false); // Disable bias current again to reduce selfheating.

  // remove fault
  rtd >>= 1;

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

  return ret;
}
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(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
  HAL_SPI_Transmit(&hspi1, &addr, 1, SPI_DELAY);
  HAL_SPI_Receive(&hspi1, buffer, n, SPI_DELAY);
  HAL_GPIO_WritePin(CS_GPIO_Port, 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(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
  HAL_SPI_Transmit(&hspi1, buffer, 2, SPI_DELAY);  // Corrected size to 2
  HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}

Credits

The Embedded Things

The Embedded Things

29 projects • 43 followers
i'm an embedded system engineer

Comments