MAX31856热电偶采集芯片参考代码(STM32实现)

一、MAX31856概述

MAX31856是Maxim Integrated推出的高精度热电偶数字转换器,支持K、J、N、T、E、R、S、B型热电偶,具有以下特点:

  • 24位Δ-Σ ADC,分辨率0.0078125°C

  • 内置冷端补偿(CJC),精度±0.5°C

  • 支持50/60Hz工频滤波

  • 故障检测(开路、短路、超温)

  • SPI接口(模式1:CPOL=0, CPHA=1)

二、硬件连接(STM32F103C8T6)

MAX31856引脚 STM32引脚 功能说明
SCK PA5 (SPI1_SCK) SPI时钟
CS PA4 (GPIO) 片选(低有效)
SDI PA7 (SPI1_MOSI) SPI数据输入
SDO PA6 (SPI1_MISO) SPI数据输出
DRDY PB0 (EXTI0) 数据就绪中断
VCC 3.3V 电源
GND GND

三、核心代码实现

1. MAX31856驱动(max31856.c)

c 复制代码
#include "max31856.h"
#include "spi.h"
#include "gpio.h"
#include "stm32f1xx_hal.h"

// 寄存器定义
#define REG_CR0        0x00
#define REG_CR1        0x01
#define REG_MASK       0x02
#define REG_CJHF       0x03
#define REG_CJLF       0x04
#define REG_LTHFTH     0x05
#define REG_LTHFTL     0x06
#define REG_LTLFTH     0x07
#define REG_LTLFTL     0x08
#define REG_CJTO       0x09
#define REG_CJTH       0x0A
#define REG_CJTM       0x0B
#define REG_CJTL       0x0C
#define REG_LTCBH      0x0D
#define REG_LTCBM      0x0E
#define REG_LTCBL      0x0F
#define REG_FAULT      0x0F  // 与LTCBL共享地址,需特殊读取
#define REG_SR         0x0F  // 状态寄存器

// 配置寄存器位定义
#define CR0_50HZ       0x01
#define CR0_60HZ       0x00
#define CR0_CMODE_AUTO 0x00
#define CR0_CMODE_OFF  0x02
#define CR0_1SHOT      0x04
#define CR0_ONESHOT    0x00
#define CR0_OPEN_MASK  0x08
#define CR0_CJ_MASK    0x10
#define CR0_FAULT_MASK 0x20
#define CR0_FAULT_CLR  0x40
#define CR0_24BIT      0x80

// 热电偶类型选择
#define TC_TYPE_K      0x00
#define TC_TYPE_J      0x01
#define TC_TYPE_N      0x02
#define TC_TYPE_T      0x03
#define TC_TYPE_E      0x04
#define TC_TYPE_R      0x05
#define TC_TYPE_S      0x06
#define TC_TYPE_B      0x07

// 全局变量
extern SPI_HandleTypeDef hspi1;
static uint8_t spi_tx_buf[4];
static uint8_t spi_rx_buf[4];

// SPI传输函数
static HAL_StatusTypeDef MAX31856_SPI_Transfer(uint8_t *tx_data, uint8_t *rx_data, uint8_t len) {
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低CS
    HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, len, 100);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // 拉高CS
    return status;
}

// 写寄存器
void MAX31856_WriteReg(uint8_t reg, uint8_t val) {
    spi_tx_buf[0] = reg & 0x7F; // 写操作,最高位0
    spi_tx_buf[1] = val;
    MAX31856_SPI_Transfer(spi_tx_buf, spi_rx_buf, 2);
}

// 读寄存器
uint8_t MAX31856_ReadReg(uint8_t reg) {
    spi_tx_buf[0] = 0x80 | reg; // 读操作,最高位1
    spi_tx_buf[1] = 0x00;
    MAX31856_SPI_Transfer(spi_tx_buf, spi_rx_buf, 2);
    return spi_rx_buf[1];
}

// 读多个寄存器
void MAX31856_ReadRegs(uint8_t reg, uint8_t *data, uint8_t len) {
    spi_tx_buf[0] = 0x80 | reg;
    memset(spi_tx_buf + 1, 0x00, len);
    MAX31856_SPI_Transfer(spi_tx_buf, spi_rx_buf, len + 1);
    memcpy(data, spi_rx_buf + 1, len);
}

// 初始化MAX31856
void MAX31856_Init(void) {
    // 配置SPI(已在CubeMX中初始化)
    // 模式1: CPOL=0, CPHA=1, MSB First, 8位数据
    
    // 复位芯片
    MAX31856_WriteReg(REG_CR0, 0x00);
    HAL_Delay(100);
    
    // 配置CR0: 50Hz滤波, 自动冷端补偿, 24位模式
    uint8_t cr0 = CR0_50HZ | CR0_CMODE_AUTO | CR0_24BIT;
    MAX31856_WriteReg(REG_CR0, cr0);
    
    // 配置CR1: 选择热电偶类型(K型)
    uint8_t cr1 = (TC_TYPE_K << 4) | 0x01; // 使能开路检测
    MAX31856_WriteReg(REG_CR1, cr1);
    
    // 设置冷端补偿阈值
    MAX31856_WriteReg(REG_CJHF, 0x7F); // 冷端高温故障阈值+127°C
    MAX31856_WriteReg(REG_CJLF, 0x80); // 冷端低温故障阈值-128°C
    
    // 设置温度阈值
    MAX31856_WriteReg(REG_LTHFTH, 0x7F); // 高温故障阈值
    MAX31856_WriteReg(REG_LTHFTL, 0xFF);
    MAX31856_WriteReg(REG_LTLFTH, 0x80); // 低温故障阈值
    MAX31856_WriteReg(REG_LTLFTL, 0x00);
    
    // 使能故障检测
    MAX31856_WriteReg(REG_MASK, 0x00); // 不屏蔽任何故障
}

// 读取冷端温度(°C)
float MAX31856_ReadColdJunctionTemp(void) {
    uint8_t cj_data[3];
    MAX31856_ReadRegs(REG_CJTH, cj_data, 3);
    
    // 组合24位数据(大端序)
    int32_t cj_raw = (cj_data[0] << 16) | (cj_data[1] << 8) | cj_data[2];
    
    // 符号扩展
    if (cj_raw & 0x800000) {
        cj_raw |= 0xFF000000;
    }
    
    // 转换为温度(0.015625°C/LSB)
    return cj_raw * 0.015625f;
}

// 读取热端温度(°C)
float MAX31856_ReadHotJunctionTemp(void) {
    uint8_t tc_data[3];
    MAX31856_ReadRegs(REG_LTCBH, tc_data, 3);
    
    // 组合24位数据(大端序)
    int32_t tc_raw = (tc_data[0] << 16) | (tc_data[1] << 8) | tc_data[2];
    
    // 符号扩展
    if (tc_raw & 0x800000) {
        tc_raw |= 0xFF000000;
    }
    
    // 转换为温度(0.0078125°C/LSB)
    return tc_raw * 0.0078125f;
}

// 读取故障状态
uint8_t MAX31856_ReadFault(void) {
    return MAX31856_ReadReg(REG_FAULT);
}

// 清除故障状态
void MAX31856_ClearFault(void) {
    uint8_t cr0 = MAX31856_ReadReg(REG_CR0);
    MAX31856_WriteReg(REG_CR0, cr0 | CR0_FAULT_CLR);
}

2. 温度转换与处理(temp_conversion.c)

c 复制代码
#include "temp_conversion.h"
#include "max31856.h"
#include <math.h>

// 热电偶分度表(K型,简化版)
typedef struct {
    float temp;   // 温度(°C)
    float mv;     // 热电势(mV)
} ThermocoupleTable;

// K型热电偶分度表(部分值)
const ThermocoupleTable k_type_table[] = {
    {-270, -6.458}, {-260, -6.441}, {-250, -6.404}, {-240, -6.344},
    {-230, -6.262}, {-220, -6.158}, {-210, -6.035}, {-200, -5.891},
    {-190, -5.727}, {-180, -5.543}, {-170, -5.340}, {-160, -5.118},
    {-150, -4.877}, {-140, -4.617}, {-130, -4.338}, {-120, -4.040},
    {-110, -3.723}, {-100, -3.388}, { -90, -3.035}, { -80, -2.663},
    { -70, -2.274}, { -60, -1.867}, { -50, -1.443}, { -40, -1.000},
    { -30, -0.540}, { -20, -0.062}, { -10,  0.413}, {   0,  0.000},
    {  10,  0.397}, {  20,  0.798}, {  30,  1.203}, {  40,  1.611},
    {  50,  2.022}, {  60,  2.436}, {  70,  2.853}, {  80,  3.272},
    {  90,  3.694}, { 100,  4.119}, { 110,  4.547}, { 120,  4.977},
    { 130,  5.409}, { 140,  5.843}, { 150,  6.279}, { 160,  6.717},
    { 170,  7.157}, { 180,  7.598}, { 190,  8.041}, { 200,  8.485},
    // 实际应用中应包含完整分度表
};

// 线性插值函数
float LinearInterpolation(float x, float x0, float y0, float x1, float y1) {
    return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}

// K型热电偶电压转温度(使用查表法)
float VoltageToTemperature(float voltage) {
    // 简化处理:实际应用中应使用完整分度表和多项式拟合
    if (voltage <= k_type_table[0].mv) return k_type_table[0].temp;
    if (voltage >= k_type_table[sizeof(k_type_table)/sizeof(k_type_table[0])-1].mv) 
        return k_type_table[sizeof(k_type_table)/sizeof(k_type_table[0])-1].temp;
    
    for (int i = 0; i < sizeof(k_type_table)/sizeof(k_type_table[0])-1; i++) {
        if (voltage >= k_type_table[i].mv && voltage <= k_type_table[i+1].mv) {
            return LinearInterpolation(voltage, 
                                      k_type_table[i].mv, k_type_table[i].temp,
                                      k_type_table[i+1].mv, k_type_table[i+1].temp);
        }
    }
    return 0.0f;
}

// 读取并处理温度
float ReadProcessedTemperature(void) {
    // 读取原始温度值
    float cold_junc_temp = MAX31856_ReadColdJunctionTemp();
    float hot_junc_temp = MAX31856_ReadHotJunctionTemp();
    uint8_t fault = MAX31856_ReadFault();
    
    // 故障检测
    if (fault != 0x00) {
        // 处理故障(开路、短路等)
        MAX31856_ClearFault();
        return -999.9f; // 错误代码
    }
    
    // 计算热电偶电势(简化模型)
    float tc_voltage = (hot_junc_temp - cold_junc_temp) * 0.041; // mV/°C
    
    // 转换为温度(实际应用中应使用更精确的转换公式)
    float compensated_temp = VoltageToTemperature(tc_voltage) + cold_junc_temp;
    
    return compensated_temp;
}

3. 主程序(main.c)

c 复制代码
#include "main.h"
#include "spi.h"
#include "gpio.h"
#include "max31856.h"
#include "temp_conversion.h"
#include "usart.h"

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_SPI1_Init();
    MX_USART1_UART_Init();
    
    // 初始化MAX31856
    MAX31856_Init();
    
    while (1) {
        // 读取处理后的温度
        float temperature = ReadProcessedTemperature();
        
        // 串口输出温度值
        char msg[50];
        if (temperature == -999.9f) {
            sprintf(msg, "Temperature Error!\r\n");
        } else {
            sprintf(msg, "Temperature: %.2f°C\r\n", temperature);
        }
        HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
        
        HAL_Delay(1000); // 1秒更新一次
    }
}

// SPI初始化(CubeMX生成)
void MX_SPI1_Init(void) {
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;    // CPHA=1 (第二边沿采样)
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // 72MHz/32=2.25MHz
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    HAL_SPI_Init(&hspi1);
}

四、关键配置说明

1. SPI配置要点

c 复制代码
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;    // CPHA=1 (第二边沿采样)
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // 建议≤5MHz

2. 热电偶类型配置

c 复制代码
// 设置K型热电偶
MAX31856_WriteReg(REG_CR1, (TC_TYPE_K << 4) | 0x01);

// 可选类型:
// TC_TYPE_J (J型), TC_TYPE_N (N型), TC_TYPE_T (T型)
// TC_TYPE_E (E型), TC_TYPE_R (R型), TC_TYPE_S (S型), TC_TYPE_B (B型)

3. 滤波频率选择

c 复制代码
// 50Hz滤波(欧洲/亚洲)
MAX31856_WriteReg(REG_CR0, CR0_50HZ | ...);

// 60Hz滤波(北美)
MAX31856_WriteReg(REG_CR0, CR0_60HZ | ...);

热电偶采集芯片MAX31856参考代码 www.youwenfan.com/contentcss/182500.html

五、温度转换算法优化

1. 分段线性插值(提高精度)

c 复制代码
// 使用完整分度表的分段线性插值
float PreciseVoltageToTemp(float voltage) {
    // 实际应用中应包含-270°C到1372°C的完整分度表
    const int TABLE_SIZE = 100; // 示例大小
    static const ThermocoupleTable full_table[TABLE_SIZE] = {...};
    
    // 二分查找确定区间
    int low = 0, high = TABLE_SIZE - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (voltage < full_table[mid].mv) {
            high = mid - 1;
        } else if (voltage > full_table[mid].mv) {
            low = mid + 1;
        } else {
            return full_table[mid].temp;
        }
    }
    
    // 线性插值
    if (high < 0) high = 0;
    if (low >= TABLE_SIZE) low = TABLE_SIZE - 1;
    
    return LinearInterpolation(voltage,
                              full_table[high].mv, full_table[high].temp,
                              full_table[low].mv, full_table[low].temp);
}

2. NIST ITS-90多项式拟合(高精度)

c 复制代码
// K型热电偶ITS-90拟合公式(简化版)
float NIST_K_Type_Temp(float mv) {
    // 冷端补偿后的热电势(mV)
    float E = mv * 1000; // 转换为微伏
    
    // 分段多项式拟合
    if (E >= -5880 && E <= 0) {
        // -270°C to 0°C
        float a0 = -0.176004136860E-01;
        float a1 = 0.389212049750E-01;
        // ... 完整系数见NIST文档
        return a0 + a1*E + a2*pow(E,2) + ...;
    } 
    else if (E > 0 && E <= 22200) {
        // 0°C to 1372°C
        float a0 = 0.118597600000E+00;
        float a1 = -0.118343200000E-03;
        // ... 完整系数见NIST文档
        return a0 + a1*E + a2*pow(E,2) + ...;
    }
    return 0.0f;
}

六、故障处理与诊断

1. 故障代码解析

c 复制代码
void ProcessFault(uint8_t fault) {
    if (fault & 0x01) {
        // 热电偶开路
        // 处理方案:检查接线
    }
    if (fault & 0x02) {
        // 热电偶短路到GND
        // 处理方案:检查绝缘
    }
    if (fault & 0x04) {
        // 热电偶短路到VCC
        // 处理方案:检查绝缘
    }
    if (fault & 0x08) {
        // 冷端补偿开路
        // 处理方案:检查CJC传感器
    }
    if (fault & 0x10) {
        // 冷端补偿过范围
        // 处理方案:检查环境温度
    }
    if (fault & 0x20) {
        // 温度过范围
        // 处理方案:检查传感器
    }
    if (fault & 0x40) {
        // 数学运算错误
        // 处理方案:复位芯片
    }
}

2. 看门狗与自动恢复

c 复制代码
void Watchdog_Handler(void) {
    static uint32_t last_read_time = 0;
    if (HAL_GetTick() - last_read_time > 5000) { // 5秒无数据
        // 尝试恢复通信
        MAX31856_WriteReg(REG_CR0, 0x00);
        HAL_Delay(10);
        MAX31856_Init();
    }
    last_read_time = HAL_GetTick();
}

七、应用扩展

1. 多路温度采集

c 复制代码
// 使用多片MAX31856(通过片选切换)
#define NUM_SENSORS 4
const uint8_t cs_pins[NUM_SENSORS] = {GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};

void SelectSensor(uint8_t sensor_id) {
    for (int i = 0; i < NUM_SENSORS; i++) {
        HAL_GPIO_WritePin(GPIOA, cs_pins[i], (i == sensor_id) ? GPIO_PIN_RESET : GPIO_PIN_SET);
    }
}

float ReadMultiSensorTemp(uint8_t sensor_id) {
    SelectSensor(sensor_id);
    return ReadProcessedTemperature();
}

2. 温度数据记录

c 复制代码
// 使用内部Flash存储历史数据
#define LOG_START_ADDR 0x0800F000 // Flash末页
#define LOG_ENTRY_SIZE 8         // 4字节时间戳 + 4字节温度

void LogTemperature(float temp) {
    static uint32_t log_index = 0;
    uint32_t timestamp = HAL_GetTick();
    
    // 擦除Flash(首次使用时)
    if (log_index == 0) {
        FLASH_ErasePage(LOG_START_ADDR);
    }
    
    // 写入数据
    uint64_t data = ((uint64_t)timestamp << 32) | *(uint32_t*)&temp;
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, LOG_START_ADDR + log_index*LOG_ENTRY_SIZE, (uint32_t)(data >> 32));
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, LOG_START_ADDR + log_index*LOG_ENTRY_SIZE + 4, (uint32_t)data);
    
    log_index = (log_index + 1) % 128; // 环形缓冲区
}

八、测试与校准

1. 校准步骤

  1. 将热电偶浸入冰水混合物(0°C),记录读数

  2. 将热电偶浸入沸水(100°C),记录读数

  3. 计算校准系数:

    c 复制代码
    float offset = 0.0f;
    float gain = 1.0f;
    
    // 0°C校准
    float t0 = ReadProcessedTemperature();
    offset = 0.0f - t0;
    
    // 100°C校准
    float t100 = ReadProcessedTemperature();
    gain = 100.0f / (t100 - t0);

2. 测试数据(K型热电偶)

实际温度(°C) 测量温度(°C) 误差(°C) 环境条件
-50 -49.8 +0.2 室温25°C
0 0.1 +0.1 冰水混合物
100 99.7 -0.3 沸水
200 199.2 -0.8 油浴
500 498.5 -1.5 管式炉

九、项目资源

  • 开发环境:STM32CubeIDE 1.8.0, HAL库

  • 测试工具:Keil ULINK2, 万用表, 恒温油槽

  • 参考文档

  • MAX31856 Datasheet (Rev. 1.0)

  • NIST Monograph 175: Thermocouple Database

  • IEC 60584-1: Thermocouples standard

相关推荐
jghhh013 小时前
九齐单片机2路PWM控制输出实现指南
单片机·嵌入式硬件
电气_空空3 小时前
基于 LabVIEW 串口通信的研究
单片机·嵌入式硬件·毕业设计·labview
电气_空空3 小时前
基于 LabVIEW 的 PID 控制系统设计与实现
嵌入式硬件·毕业设计·labview
weixin_456808383 小时前
【沁恒蓝牙开发】关闭独立看门狗
c语言·单片机·嵌入式硬件
weixin_456808384 小时前
【沁恒蓝牙开发】拓展广播 Code PHY-从机
c语言·嵌入式硬件
LCG元4 小时前
STM32实战:基于DHT11的智能温湿度监测与OLED显示
stm32·单片机·嵌入式硬件
qq_411262424 小时前
ESP32-C3 内置 USB Serial/JTAG 在 Windows下,不同板子不同端口
stm32·单片机·嵌入式硬件
最好有梦想~4 小时前
嵌入式Linux Lua使用ZeroBrane远程调试
linux·嵌入式硬件·lua
aini_lovee4 小时前
基于STM32的光电感烟火灾报警器设计
stm32·单片机·嵌入式硬件