STM32保姆级入门教程|第8章:PT100高精度测温实战 + ADS1232驱动 + 24位ADC数据解析(功能超详细+CubeIDE手把手)

原创 ✍️ | 新手零门槛,全程新建工程,手把手不踩坑!

文章标签:#stm32 #stm32cubeide #ADS1232 #PT100 #24位ADC #高精度测温 #工业实战 #嵌入式

系列前置博客(必看!否则跟不上哦😜):

  1. 第1章:零基础必看,从认知到实战全解析
  2. 第2章:STM32CubeIDE 使用+点亮LED
  3. 第3章:从新建工程到LED闪烁
  4. 第4章:GPIO输入+外部中断 实现按键控制LED
  5. 第5章:GPIO内部结构 + 8种模式详解
  6. 第6章:定时器中断原理 + 精准LED闪烁
  7. 第7章:串口通信 + printf重定向打印调试(本章续篇)

💡 一、前言(为什么学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位数据读取、查表法温度换算全部讲到最细,全程用"大白话+表情包+一步一截图",新手看完不仅能学会,还能直接用在自己的项目里,爽到飞起!

🎯 二、本章核心功能目标(清晰明确,学完不迷茫)

  1. 搞懂 ✅ PT100铂电阻测温原理(电阻怎么变成温度?)
  2. 掌握 ✅ ADS1232 24位ADC的读取方法(比普通SPI多一个特殊引脚!)
  3. 学会 ✅ 24位有符号数据的读取与拼接(三个字节怎么拼成一个数?)
  4. 学会 ✅ 利用内置高分辨率分度表快速查表(0.1℃精度,0~570℃)
  5. 实现 ✅ :
    • 每秒采集一次温度数据
    • 通过串口打印实时温度值(精确到0.1℃)
    • LED每秒闪烁一次(指示系统运行)
  6. 理解 ✅ 为什么这套方案是工业测温的"黄金搭档"(电桥+基准+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,它有两个功能:

  1. 数据就绪标志(DRDY)

    • 根据数据手册,当 ADC 正在转换时,DOUT 保持 高电平 ;转换完成后变为 低电平
    • ⚠️ 特别注意 :由于某些硬件电路设计差异(如加了反相器),你手上的模块可能是 DOUT=1 表示就绪 。本章代码以实际验证的逻辑为准:等待 DOUT == 1 才读取数据
  2. 数据输出(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工程(参考之前的项目 我就不上截图了)

  1. 打开STM32CubeIDE,点击 Start a new STM32 project
  2. 搜索芯片型号 STM32F103RCT6,选中后点击 Next
  3. 填写工程名称(例如 PT100_ADS1232_Demo),选择保存路径,点击 Finish

步骤2:配置串口USART1(用于printf打印)

  1. 左侧点击 ConnectivityUSART1,Mode 选择 Asynchronous
  2. 参数设置:Baud Rate = 115200,Word Length = 8 Bits,Parity = None,Stop Bits = 1


步骤3:开启USART1全局中断

切换到 NVIC Settings 界面,勾选 USART1 global interruptEnable

步骤4:配置LED引脚(用于温度指示)

配置 PA0GPIO_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。示例:

c 复制代码
fac_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);
    }
}

✨ 下载验证(见证奇迹的时刻!)

  1. 连接ST-Link和USB转TTL模块
  2. 打开串口助手(115200-8-N-1)
  3. 下载程序,观察输出

实际输出如下:

🎉 恭喜!你已经学会了STM32 + ADS1232 + PT100的高精度测温方案!使用预置的0.1℃分度表,无需复杂换算,直接查表即可得到温度值!

📝 关键功能总结(新手必背,避免踩坑!)

  1. 接口:PB12=A0, PB13=CS, PB14=SCK, PB15=DOUT
  2. 硬件参数:Vref=2.5V, Gain=128, Iex=1mA
  3. 读取关键:本模块 DOUT=1 表示就绪,读取 24 位后右移 8 位
  4. 温度换算:直接查分度表,表值除以 10 即实际温度(0.1℃分辨率)
  5. printf重定向 :重写 __io_putchar,结尾加 \r\n
  6. 精确延时 :基于 SysTick 实现 delay_us,需在 main 中初始化 fac_us
  7. 中值滤波: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相关学习资料包
相关推荐
危桥带雨2 小时前
FLASH理论基础
stm32·单片机·嵌入式硬件
feifeigo1233 小时前
STM32 LCD彩色液晶屏显示汉字、英文、数字
stm32·单片机·嵌入式硬件
实在太懒于是不想取名5 小时前
STM32N6的开发日记(4):快速上手LTDC显示图片-让屏幕刷新丝滑流畅
stm32·单片机·嵌入式硬件
实在太懒于是不想取名5 小时前
STM32N6的开发日记(1):上手难度拉满的N6有哪些不同?
stm32·单片机·嵌入式硬件
d111111111d7 小时前
STM32-UART抽象层封装
笔记·stm32·单片机·嵌入式硬件·学习
华清远见IT开放实验室7 小时前
嵌入式系统化课程 学习内容与服务说明
linux·stm32·学习·嵌入式·全栈·虚拟仿真·测评中心
LCMICRO-133108477468 小时前
长芯微LCMDC7616完全P2P替代AD7616,16通道16位模数转换器(ADC)
stm32·嵌入式硬件·fpga开发·硬件工程·模数转换器adc·电力线监测
Joseph Cooper9 小时前
STM32MP157 Linux驱动学习笔记(五):子系统与工程边界(V4L2/IIO/devmem/UIO)
linux·stm32·学习
恶魔泡泡糖11 小时前
stm32F103C8T6标准库反射(反射式红外)传感器触发蜂鸣器
stm32·单片机·嵌入式硬件