STM32F103C8T6蓝莓派上,用HAL库GPIO模拟SPI驱动MAX31865读取PT1000温度(附完整代码)
2026/6/2 3:28:31 网站建设 项目流程

STM32F103C8T6蓝莓派上HAL库GPIO模拟SPI驱动MAX31865读取PT1000温度实战指南

在嵌入式开发中,精确的温度测量常常是项目成败的关键。当手头只有一块廉价的STM32F103C8T6蓝莓派开发板,却需要实现高精度温度采集时,GPIO模拟SPI驱动MAX31865的方案就成为了性价比极高的选择。本文将完整呈现从硬件连接到温度换算的全过程,特别针对PT1000热电阻的应用场景。

1. 硬件准备与连接

STM32F103C8T6蓝莓派作为一款性价比极高的开发板,其有限的硬件资源需要我们精打细算。与MAX31865的连接需要特别注意以下几点:

  • 引脚分配策略:由于是GPIO模拟SPI,理论上任何GPIO都可以使用,但建议选择相邻引脚以简化布线
  • 电源考虑:MAX31865需要3.3V供电,与STM32F103C8T6电平匹配
  • PT1000接线:根据使用的2线、3线或4线制接法不同,配置也会有所差异

具体连接方式如下表所示:

MAX31865引脚STM32F103C8T6引脚方向备注
CSPA4输出片选信号,低电平有效
SCLKPA5输出模拟SPI时钟
SDIPA7输出模拟MOSI
SDOPA6输入模拟MISO
VDD3.3V-电源正极
GNDGND-电源地

提示:如果开发板上的PA4-PA7已被占用,可以更换为其他GPIO组,但需要在代码中相应修改引脚定义。

2. HAL库GPIO模拟SPI的实现原理

硬件SPI外设固然方便,但在引脚冲突或需要多个SPI设备时,GPIO模拟提供了更大的灵活性。理解SPI协议的底层时序是关键:

  1. 空闲状态:SCLK保持高电平,CS保持高电平(未选中状态)
  2. 起始条件:CS拉低,表示通信开始
  3. 数据传输:在SCLK的边沿进行数据采样(通常选择第二个边沿)
  4. 结束条件:CS拉高,结束通信

模拟SPI的核心是精确控制这些时序。以下是GPIO初始化的代码示例:

void GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); // CS, SCLK, MOSI配置为输出 GPIO_InitStruct.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // MISO配置为输入 GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始状态 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS高 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // SCLK高 }

3. MAX31865寄存器配置与通信实现

MAX31865通过寄存器配置其工作模式,关键寄存器包括:

  • 配置寄存器(0x00):设置偏置电压、转换模式、线制选择等
  • RTD数据寄存器(0x01-0x02):存储温度传感器的原始数据
  • 故障阈值寄存器(0x03-0x06):设置高低故障阈值

3.1 寄存器写入实现

写入寄存器需要先发送地址(最高位为1表示写操作),再发送数据。以下是写入函数的实现:

void MAX31865_WriteReg(uint8_t reg, uint8_t value) { uint8_t i; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS拉低 // 发送寄存器地址(最高位设为1表示写操作) for(i = 0; i < 8; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // SCLK下降沿 if(reg & 0x80) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // MOSI高 else HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); // MOSI低 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // SCLK上升沿 reg <<= 1; } // 发送数据 for(i = 0; i < 8; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // SCLK下降沿 if(value & 0x80) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // MOSI高 else HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); // MOSI低 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // SCLK上升沿 value <<= 1; } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS拉高 }

3.2 寄存器读取实现

读取寄存器时,先发送地址(最高位为0表示读操作),然后接收数据:

uint8_t MAX31865_ReadReg(uint8_t reg) { uint8_t i, value = 0; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS拉低 // 发送寄存器地址(最高位设为0表示读操作) for(i = 0; i < 8; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // SCLK下降沿 if(reg & 0x80) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // MOSI高 else HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); // MOSI低 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // SCLK上升沿 reg <<= 1; } // 接收数据 for(i = 0; i < 8; i++) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // SCLK下降沿 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // SCLK上升沿 value <<= 1; if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)) // 读取MISO value |= 0x01; } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS拉高 return value; }

4. PT1000温度计算与系统集成

MAX31865输出的原始数据需要转换为实际温度值。对于PT1000传感器,其电阻与温度的关系可以用Callendar-Van Dusen方程描述:

Rt = R0 * (1 + A*T + B*T²)

其中:

  • Rt是当前温度下的电阻值
  • R0是0°C时的电阻值(PT1000为1000Ω)
  • A = 3.9083×10⁻³
  • B = -5.775×10⁻⁷
  • T是温度值(°C)

温度计算函数实现如下:

float MAX31865_ReadTemp(void) { uint16_t rtd; float resistance, temp; // 读取RTD值 rtd = MAX31865_ReadReg(0x01) << 8; // 高位字节 rtd |= MAX31865_ReadReg(0x02); // 低位字节 rtd >>= 1; // 丢弃故障位 // 计算电阻值 resistance = (float)rtd / 32768.0 * 4300.0; // RREF=4300Ω // 计算温度(简化计算,适用于0°C以上) temp = (resistance - 1000.0) / 3.9083; // 更精确的计算(考虑二次项,适用于全量程) /* float a = 3.9083e-3; float b = -5.775e-7; float Z1, Z2, Z3, Z4; Z1 = -a; Z2 = a * a - 4 * b; Z3 = (4 * b) / 1000.0; Z4 = 2 * b; temp = (sqrt(Z2 + Z3 * resistance) + Z1) / Z4; */ return temp; }

系统初始化时,需要配置MAX31865的工作模式:

void MAX31865_Init(void) { // 配置寄存器设置: // - 偏置电压开启 // - 自动转换模式 // - 4线制RTD // - 50Hz滤波 MAX31865_WriteReg(0x80, 0xC1); // 设置高低故障阈值(可选) MAX31865_WriteReg(0x83, 0xFF); // 高阈值MSB MAX31865_WriteReg(0x84, 0xFF); // 高阈值LSB MAX31865_WriteReg(0x85, 0x00); // 低阈值MSB MAX31865_WriteReg(0x86, 0x00); // 低阈值LSB }

实际应用中,可以通过定时器定期读取温度值:

float temperature; MAX31865_Init(); while(1) { temperature = MAX31865_ReadTemp(); printf("Current temperature: %.2f°C\r\n", temperature); HAL_Delay(1000); }

5. 调试技巧与常见问题解决

在实际项目中,可能会遇到各种问题。以下是一些常见问题及其解决方案:

  • 无数据返回

    • 检查硬件连接是否正确,特别是CS引脚
    • 确认电源电压稳定
    • 用逻辑分析仪检查SPI时序
  • 数据不稳定

    • 确保PT1000接线牢固,接触电阻小
    • 尝试启用50Hz/60Hz滤波
    • 检查周围是否有电磁干扰源
  • 温度值偏差大

    • 确认RREF电阻精度(建议使用0.1%精度)
    • 检查PT1000传感器是否损坏
    • 验证温度计算公式是否正确

注意:GPIO模拟SPI的速度较硬件SPI慢,不适合高速数据采集场景。如果项目对采样率要求高,建议使用硬件SPI或考虑更高性能的MCU。

通过以上步骤,我们成功在STM32F103C8T6蓝莓派上实现了PT1000温度测量系统。这种方案成本低廉但性能可靠,特别适合学生项目和小批量生产应用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询