原创 ✍️ | 新手零门槛,全程新建工程,手把手不踩坑!
文章标签:#stm32 #stm32cubeide #ADS1232 #PT100 #24位ADC #高精度测温 #工业实战 #嵌入式
系列前置博客(必看!否则跟不上哦😜):
💡 一、前言(为什么学PT100测温?工业实战必备!)
大家好,我是BackCatK Chen😎!
前面7章我们学完了GPIO、中断、串口、定时器这些STM32的"基础技能",你已经能让LED闪烁、能用按键控制、能用串口打印调试信息了。但接下来问题来了:单片机最有价值的应用是什么?
答案是:采集真实世界的物理量!
温度,是工业生产和日常生活中最常测量的物理量之一。从恒温箱、热水器到工业窑炉,都离不开精准的温度测量。今天咱们就学嵌入式测温的"黄金组合"------PT100铂电阻 + ADS1232 24位高精度ADC + MCP1525精密基准源 。这套方案精度可达0.1℃级别,在工业领域广泛应用,学会它,你就掌握了工业测温的核心技术!
重点说明💥:本章不沿用任何前序工程 ,全程从0新建工程,手把手教你配置ADS1232、采集24位数据、通过预置的0.1℃高分辨率PT100分度表 直接查表得到温度值。本章基于 STM32CubeIDE 1.19.0 版本编写,哪怕你没保存前序工程,也能轻松跟上!
本章我把PT100原理、ADS1232芯片、24位数据读取、查表法温度换算全部讲到最细,全程用"大白话+表情包+一步一截图",新手看完不仅能学会,还能直接用在自己的项目里,爽到飞起!
🎯 二、本章核心功能目标(清晰明确,学完不迷茫)
- 搞懂 ✅ PT100铂电阻测温原理(电阻怎么变成温度?)
- 掌握 ✅ ADS1232 24位ADC的读取方法(比普通SPI多一个特殊引脚!)
- 学会 ✅ 24位有符号数据的读取与拼接(三个字节怎么拼成一个数?)
- 学会 ✅ 利用内置高分辨率分度表快速查表(0.1℃精度,0~570℃)
- 实现 ✅ :
- 每秒采集一次温度数据
- 通过串口打印实时温度值(精确到0.1℃)
- LED每秒闪烁一次(指示系统运行)
- 理解 ✅ 为什么这套方案是工业测温的"黄金搭档"(电桥+基准+24位ADC)
🔌 三、硬件接口定义
本章不深入讲解原理图,只给出最简明的接线关系,仅提供我自己的开发版接口,大家根据自己的原理图配置。
3.1 STM32 ↔ ADS1232测温板接线表
| STM32F103RCT6 引脚 | 测温板信号 | 功能说明 |
|---|---|---|
| PB12 | A0 | 通道选择(本示例用通道1,低电平) |
| PB13 | PDWN/CS | 掉电控制(高电平工作) |
| PB14 | SCLK | 串行时钟(STM32输出) |
| PB15 | DOUT | 数据输出/数据就绪(STM32输入) |
| 3.3V | VCC | 电源正 |
| GND | GND | 共地(必须接!) |
3.2 串口打印接线(参考上一篇文章)
| STM32 | USB转TTL |
|---|---|
| PA9 (TX) | RX |
| PA10 (RX) | TX |
| GND | GND |
硬件配置参数(写代码用):
- 基准电压
VREF = 2.5V(MCP1525提供) - PGA增益 = 128倍(硬件已固定)
- 采样率 = 80SPS
- 恒流源电流 = 1mA
🏗️ 四、测温系统总体架构(一张图看懂信号流向)
PT100 → 电桥+差分放大 → ADS1232(24位ADC) → STM32(GPIO模拟SPI) → 串口打印/LED指示
📌 核心思想 :PT100把温度变成电阻,电桥把电阻变成电压,ADS1232把电压变成数字量,STM32把数字量算回温度------四条"翻译官"接力完成测温!
🌡️ 五、PT100测温原理简述(通俗不烧脑)
- PT100:0℃时阻值 = 100Ω,每升高1℃,阻值增加约0.385Ω(非线性,需查表修正)
- 三线制:工业标配,可消除导线电阻影响
- 温度换算 :本章采用预置的0.1℃高分辨率分度表,直接根据ADC原始码值查表得到温度,无需复杂的公式计算

🔬 六、ADS1232 24位ADC深度解析(原理+时序+数据读取全明白)
6.1 ADS1232是什么?
ADS1232 是德州仪器(TI)推出的一款 24 位低功耗 Δ-Σ 模数转换器,专为桥式传感器设计。它内部集成了可编程增益放大器(PGA)、Δ-Σ 调制器和数字滤波器,能将微弱的模拟电压信号转换为高精度的 24 位数字量。
核心参数速览:
| 参数 | 数值/选项 | 说明 |
|---|---|---|
| 分辨率 | 24 位 | 理论可分辨 2²⁴ = 16,777,216 个等级 |
| PGA 增益 | 1 / 2 / 64 / 128 | 通过 GAIN0/GAIN1 引脚配置 |
| 数据速率 | 10 SPS / 80 SPS | 通过 SPEED 引脚选择,速率越低噪声越小 |
| 输入通道 | 2 路差分 | 通过 A0 引脚切换 |
| 接口 | 类 SPI(特殊时序) | SCK + DOUT,DOUT 兼具数据就绪功能 |
6.2 24位ADC意味着什么?
我们常用的 STM32 内部 ADC 是 12 位 ,能区分 2¹² = 4096 个电压等级。而 ADS1232 是 24 位 ,能区分 16,777,216 个等级,精度提升了 4096 倍!
举个直观的例子:假设测量范围是 0~2.5V:
- 12位ADC:最小分辨电压 = 2.5V / 4096 ≈ 0.6mV
- 24位ADC:最小分辨电压 = 2.5V / 16777216 ≈ 0.15μV
这就是为什么 PT100 微小的电阻变化(每℃仅 0.385Ω,对应电压变化约 0.385mV)能被精准捕获的原因。
6.3 DOUT 引脚的双重功能(最核心!)
ADS1232 最特殊的引脚就是 DOUT,它有两个功能:
-
数据就绪标志(DRDY):
- 根据数据手册,当 ADC 正在转换时,DOUT 保持 高电平 ;转换完成后变为 低电平。
- ⚠️ 特别注意 :由于某些硬件电路设计差异(如加了反相器),你手上的模块可能是 DOUT=1 表示就绪 。本章代码以实际验证的逻辑为准:等待 DOUT == 1 才读取数据。
-
数据输出(DOUT):
- DOUT 就绪后,在 SCK 时钟的驱动下,DOUT 会逐位输出 24 位转换结果。
⚠️ 关键要点 :读取数据之前,必须等待 DOUT 变为有效电平(本代码为高电平),否则读出来的是垃圾数据!
6.4 数据读取时序(必看!搞懂才能写代码)
本章代码的实际读取步骤如下:
步骤1:等待 DOUT 变高(本模块的就绪标志)
c
delay_us(5);
if(IO_ADS1232_DO) // DOUT=1 表示数据就绪,继续读取
// 读取数据
else
return 0; // 未就绪则返回0
步骤2:输出 24 个时钟脉冲,读取数据
- 每个 SCK 的 上升沿,DOUT 输出一位数据
- 数据从最高位(MSB,第 23 位)开始输出,一直到最低位(LSB,第 0 位)
- 我们在每个上升沿后读取 DOUT 电平,并移位拼接到变量中
步骤3:数据后处理
- 若最高位为 1,表示负数(低于 0℃),直接置 0
- 右移 8 位(丢弃低 8 位噪声,相当于除以 256)
- 限制上限 5600(对应分度表最大索引)
步骤4:额外时钟
- 再输出一个 SCK 脉冲,让 DOUT 回到空闲状态
6.5 为什么不用硬件SPI?
ADS1232 虽然看起来像 SPI 设备,但用 STM32 的硬件 SPI 驱动会很麻烦,因为:
- 数据位数不是 8 的整数倍(24 位),硬件 SPI 通常只能处理 8/16 位
- 需要精确控制时钟数量
- DOUT 兼具数据就绪功能,硬件 SPI 的 NSS 引脚无法替代
因此,TI 官方推荐用 GPIO 模拟时序(Bit-Bang),我们本章就采用这种方式。
🛠️ 七、CubeIDE 1.19.0 新建工程+手把手配置(一步一截图,零踩坑)
重点🔥:全程从0新建工程,不沿用任何前序工程,每一步都有截图。
步骤1:新建CubeIDE工程(参考之前的项目 我就不上截图了)
- 打开STM32CubeIDE,点击
Start a new STM32 project - 搜索芯片型号
STM32F103RCT6,选中后点击Next - 填写工程名称(例如
PT100_ADS1232_Demo),选择保存路径,点击Finish
步骤2:配置串口USART1(用于printf打印)
- 左侧点击
Connectivity→USART1,Mode 选择Asynchronous - 参数设置:Baud Rate = 115200,Word Length = 8 Bits,Parity = None,Stop Bits = 1


步骤3:开启USART1全局中断
切换到 NVIC Settings 界面,勾选 USART1 global interrupt → Enable

步骤4:配置LED引脚(用于温度指示)
配置 PA0 为 GPIO_Output,别名设为 LED,推挽输出、无上下拉。

步骤5:配置ADS1232控制引脚
根据接口定义,依次配置以下引脚:
| 引脚 | 别名 | 模式 |
|---|---|---|
| PB12 | ADS1232_A0 | GPIO_Output |
| PB13 | ADS1232_CS | GPIO_Output |
| PB14 | ADS1232_SCK | GPIO_Output |
| PB15 | ADS1232_OUT | GPIO_Input |

步骤6:生成代码
点击右上角 GENERATE CODE 按钮,等待代码生成完成。
💻 八、代码功能详解 + 实战编写(逐行拆解,新手也能懂)
✅ 本章代码已经过实际硬件验证,可直接使用。代码采用位带操作 简化 GPIO 控制,基于 SysTick 实现精确微秒延时 ,并加入中值滤波 与通道切换机制,测温稳定可靠。
8.1 包含头文件与位带操作
c
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "bitband.h" // 位带操作头文件(需自行创建,见配套源码)
/* USER CODE END Includes */
bitband.h 内容如下(简化版,完整见源码):
c
#ifndef _BITBAND_H
#define _BITBAND_H
#include "stm32f1xx.h"
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x02000000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOB_ODR_Addr (GPIOB_BASE + 0x0C)
#define GPIOB_IDR_Addr (GPIOB_BASE + 0x08)
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr, n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr, n)
#define IO_ADS1232_SK PBout(14) // PB14
#define IO_ADS1232_CS PBout(13) // PB13
#define IO_ADS1232_CH PBout(12) // PB12
#define IO_ADS1232_DO PBin(15) // PB15
#endif
8.2 全局变量与分度表
c
/* USER CODE BEGIN PV */
#define LED_Pin GPIO_PIN_0
#define LED_GPIO_Port GPIOA
unsigned char Count0, Step0, ADS_CH, ADC_Cnt; // 计数变量
int temperature = 0; // 温度值(实际使用中可为 float)
// PT100 高分辨率分度表(0.1℃精度,0~570℃)
unsigned int const ADS1232_Tab[] = {
// 0℃ ~ 5℃
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20,
// ...(完整数组见配套工程,共 5700+ 个元素)
// 565℃ ~ 570℃
2646, 2646, 2647, 2647, 2648, 2648, 2649, 2649, 2650, 2650, 2651, 2651, 2652, 2652, 2653, 2653, 2654, 2654, 2655, 2655, 2656, 2656, 2657, 2657, 2658, 2658, 2659, 2659, 2660, 2660, 2661, 2661, 2662, 2662, 2663, 2663, 2664, 2664, 2665, 2665, 2666, 2666, 2667, 2667, 2668, 2668, 2669, 2669, 2670, 2670
};
#define TABLE_SIZE (sizeof(ADS1232_Tab) / sizeof(ADS1232_Tab[0]))
/* USER CODE END PV */
8.3 精确微秒/毫秒延时(基于 SysTick)
c
/* USER CODE BEGIN 0 */
static uint32_t fac_us = 0; // us 延时倍乘数(需在系统时钟初始化后赋值)
// 微秒延时(利用 SysTick 滴答定时器,精确延时)
void delay_us(uint32_t nus) {
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
ticks = nus * fac_us;
told = SysTick->VAL;
while(1) {
tnow = SysTick->VAL;
if(tnow != told) {
if(tnow < told) tcnt += told - tnow;
else tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks) break;
}
}
}
// 毫秒延时
void delay_ms(uint16_t nms) {
uint32_t i;
for(i = 0; i < nms; i++) delay_us(1000);
}
// printf 重定向
int __io_putchar(int ch) {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
💡 提示 :
fac_us需在main函数中调用HAL_Init()和SystemClock_Config()之后赋值为SystemCoreClock / 1000000。示例:
cfac_us = SystemCoreClock / 1000000;
8.4 ADS1232 初始化函数(含校准脉冲)
c
void ADS1232_Init(void) {
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull = GPIO_PULLUP;
GPIO_Initure.Speed = GPIO_SPEED_HIGH;
GPIO_Initure.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14;
HAL_GPIO_Init(GPIOB, &GPIO_Initure);
GPIO_Initure.Mode = GPIO_MODE_INPUT;
GPIO_Initure.Pull = GPIO_PULLUP;
GPIO_Initure.Pin = GPIO_PIN_15;
HAL_GPIO_Init(GPIOB, &GPIO_Initure);
IO_ADS1232_CH = 0;
delay_ms(100);
IO_ADS1232_SK = 0;
IO_ADS1232_CS = 1;
delay_ms(100);
IO_ADS1232_CS = 0;
delay_ms(100);
IO_ADS1232_CS = 1;
delay_ms(100);
// 等待 DOUT 变高(本模块的就绪标志)
for(Count0 = 0; Count0 < 250; Count0 = 0) {
if(IO_ADS1232_DO == 1) break;
delay_ms(100);
}
// 发送 25 个校准脉冲
for(Step0 = 0; Step0 <= 24; Step0++) {
Count0 = 1;
Count0 = 0;
IO_ADS1232_SK = 1;
delay_us(100);
if(IO_ADS1232_DO) Count0 = 0;
IO_ADS1232_SK = 0;
Count0 = 1;
Count0 = 0;
}
IO_ADS1232_SK = 1;
Count0 = 1;
Step0 = 1;
IO_ADS1232_SK = 0;
Count0 = 0;
Step0 = 0;
printf("ADS1232 Init OK!\r\n");
}
8.5 读取 ADC 原始数据(24位 → 16位有效值)
c
int32_t ADS1232_ReadData(void) {
uint32_t temp_count = 0;
uint32_t ADS1232Value;
unsigned char ads_i = 0;
delay_us(5);
if(IO_ADS1232_DO) // DOUT=1 表示数据就绪
{
// 读取 24 位数据
for(ads_i = 0; ads_i < 24; ads_i++) {
IO_ADS1232_SK = 1;
delay_us(1);
temp_count <<= 1;
if(IO_ADS1232_DO) temp_count |= 1;
IO_ADS1232_SK = 0;
delay_us(1);
}
IO_ADS1232_SK = 1;
delay_us(1);
IO_ADS1232_SK = 0;
if(temp_count & 0x800000) temp_count = 0; // 负数置零
temp_count >>= 8; // 丢弃低8位噪声
ADS1232Value = (temp_count > 5600) ? 5600 : temp_count;
return ADS1232Value;
}
return 0; // 未就绪返回0
}
8.6 通道切换与中值滤波
c
// 切换 ADC 通道(CH=0 或 1)
void Set_ADS1232(unsigned char CH) {
delay_us(10);
IO_ADS1232_CS = 0;
delay_us(10);
IO_ADS1232_CH = (CH ? 1 : 0);
delay_us(10);
IO_ADS1232_CS = 1;
delay_us(10);
}
// 中值滤波(窗口大小 8,去掉最大最小值后取平均)
unsigned int Ad_Fillter(unsigned int data, unsigned int *p) {
uint8_t AD_Fi;
uint32_t AD_Max, AD_Min, AD_Avg;
for(AD_Fi = 0; AD_Fi < 7; AD_Fi++) {
*(p + AD_Fi) = *(p + AD_Fi + 1);
}
*(p + 7) = data;
AD_Avg = AD_Max = AD_Min = *p;
for(AD_Fi = 1; AD_Fi < 8; AD_Fi++) {
if(*(p + AD_Fi) > AD_Max) AD_Max = *(p + AD_Fi);
if(*(p + AD_Fi) < AD_Min) AD_Min = *(p + AD_Fi);
AD_Avg += *(p + AD_Fi);
}
AD_Avg = AD_Avg - (AD_Max + AD_Min);
AD_Avg += 3;
AD_Avg /= 6;
return AD_Avg;
}
8.7 主函数
c
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
fac_us = SystemCoreClock / 1000000; // 初始化微秒延时系数
ADS1232_Init();
printf("\r\n===== PT100 高精度测温 =====\r\n");
printf("分辨率: 0.1℃, 范围: 0~570℃\r\n\r\n");
while(1) {
int32_t adc = ADS1232_ReadData();
printf("ADC: %8ld | Temp: %f ℃\r\n", (long)adc,(float) ADS1232_Tab[adc]/10);
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(1000);
}
}
✨ 下载验证(见证奇迹的时刻!)
- 连接ST-Link和USB转TTL模块
- 打开串口助手(115200-8-N-1)
- 下载程序,观察输出
实际输出如下:

🎉 恭喜!你已经学会了STM32 + ADS1232 + PT100的高精度测温方案!使用预置的0.1℃分度表,无需复杂换算,直接查表即可得到温度值!
📝 关键功能总结(新手必背,避免踩坑!)
- 接口:PB12=A0, PB13=CS, PB14=SCK, PB15=DOUT
- 硬件参数:Vref=2.5V, Gain=128, Iex=1mA
- 读取关键:本模块 DOUT=1 表示就绪,读取 24 位后右移 8 位
- 温度换算:直接查分度表,表值除以 10 即实际温度(0.1℃分辨率)
- printf重定向 :重写
__io_putchar,结尾加\r\n - 精确延时 :基于 SysTick 实现
delay_us,需在 main 中初始化fac_us - 中值滤波:8 窗口滤波,有效抑制随机噪声
❌ 常见问题排查(遇到问题不用慌,对照排查!)
| 现象 | 可能原因 |
|---|---|
| 读数始终0 | CS未拉高、DOUT判断逻辑反了、增益错误 |
| 卡死 | DOUT未变高(就绪标志错误)、时钟太快、虚焊 |
| 温度偏差大 | 分度表与实际电路不匹配,需重新校准 |
| 温度跳变 | 电源纹波大、基准电压不稳、未启用中值滤波 |
| LED不闪 | 程序卡死、LED引脚配置错误 |
| 编译报错"undefined reference to delay_us" | 函数未声明或未实现,检查 delay_us 是否在 /* USER CODE BEGIN 0 */ 内 |
| printf 浮点数打印异常 | 添加 -u _printf_float 链接器标志 |
📢 下篇预告(精彩不容错过!新手必追)
STM32保姆级入门教程|第9章:工业PID温度控制实战 + PWM加热驱动 + 串口实时调参
手把手教你:从零实现经典 PID 控制算法,用 PWM 控制加热元件,通过串口实时调整 PID 参数并观察温度响应曲线。把你的测温系统升级为完整的闭环控制系统,迈入自动化控制的大门!
原创不易,创作花费大量时间和精力💦,如果本文对你有帮助,欢迎
点赞👍、收藏⭐、关注➕ ,有任何问题,评论区留言,我会一一回复!你的支持,就是我持续更新的动力~本文所使用的工程文件已上传至配套资源中,如有需要可自行下载。也可关注博主后留言获取
🎁欢迎关注公众号,获取更多技术干货!
博主准备的资料包涵盖了从硬件电路设计 到STM32单片机开发 ,再到Linux系统学习的全链路内容,适合不同阶段的学习者:
- 硬件基础:包含硬件电路合集、硬件设计开发工具包,帮你打牢底层基础。
- STM32专项:从环境搭建、开发工具、传感器模块到项目实战,还有书籍和芯片手册,一站式搞定STM32学习。
- C语言进阶:C语言学习资料包,助你掌握嵌入式开发的核心语言。
- 面试求职:嵌入式面试题合集,提前备战技术面试。
- Linux拓展:Linux相关学习资料包,拓宽技术视野。

📂资料包目录
- 00-STM32单片机环境搭建
- 01-硬件电路合集
- 02-硬件设计开发工具包
- 03-C语言学习资料包
- 04-STM32单片机开发工具包
- 05-STM32传感器模块合集
- 06-STM32项目合集
- 07-STM32单片机书籍&芯片手册
- 08-Linux相关学习资料包
