简介基于STM32单片机与PT100铂电阻的温度采集系统资料包整合程序源码、原理图与全套开发文档适用于嵌入式初学者及从事工业温度监测项目开发的工程师。整个压缩包共277个文件大小约18.09MB主要包含62个C源文件、52个头文件及Keil uVision工程文件构成可编译的STM32代码同时附有电路原理图图片、PDF技术手册、Word说明文档和教学视频资料分类明确便于按需查阅。资源已有2082人学习下载。内容围绕PT100四线制接法、STM32内部ADC配置与多通道采样展开并给出电阻-温度换算、LCD显示、UART/I2C通信和定时器中断采集等完整实现代码硬件设计与软件算法均提供可复用的工程化思路能够帮助读者快速搭建一套可运行的高精度温度采集系统同时掌握从原理图设计到代码调试的嵌入式开发全流程。1. 基于STM32的PT100温度采集系统一套能直接抄进产线的测温方案做嵌入式这些年我拆过不少温度采集类的项目大部分死在同一个地方传感器电路设计得稀烂ADC采出来的数据飘得没法看。而这个基于STM32F103C8T6的PT100铂电阻温度采集系统是我见过少有的能把前端模拟电路、软件滤波和标定流程一起讲清楚的项目——它不只是一个理论Demo而是一套完整可落地的测温方案包含原理图、PCB思路、完整的STM32工程代码以及可从-50℃到400℃范围内读数的查表算法。这套资源适合三类人想快速做一套工业级温度采集的嵌入式工程师、正在做课程设计或毕设的在校学生以及想把PT100前端电路直接搬进自己项目里的硬件开发者。它解决的核心痛点是PT100传感器阻值变化极其微小约0.385Ω/℃如何用普通MCU的ADC稳定采集并转换成精准温度。下面我从硬件设计、代码实现、烧录调试和二次开发四个角度把这套项目完整拆开讲透。2. 整体架构与硬件设计前端恒流源电路才是这套系统的灵魂2.1 系统组成与信号链路这套项目的骨架很干净PT100铂电阻作为温度敏感元件经过一个由运放搭建的恒流源电路把电阻变化转换成电压变化再送入STM32F103C8T6的内置ADC采样最终由软件查表计算得到温度值显示在OLED屏上或通过串口发送给上位机。信号链路是这样走的PT100三线制接法→ 恒流源驱动1mA恒流→ 差分放大/电压调理 → STM32 ADC采集 → 数字滤波 → 查表插值 → OLED/串口输出。这里最关键的认知是PT100的阻值变化量很小0℃时100Ω100℃时才138.5Ω如果不加恒流源而直接用分压法测ADC读数的非线性误差会大到你怀疑人生。这套设计用1mA恒流源激励在0℃时产生100mV压降经运放放大后送ADC信噪比要比简单分压高一个量级。2.2 原理图核心模块逐个拆解打开随资料附带的原理图重点看三个地方恒流源电路、信号调理电路和MCU最小系统。恒流源这部分采用的是运放加精密基准源的经典拓扑用TL431产生2.5V精密基准配合运放构成电压控制电流源输出1mA恒定电流流过PT100。这个1mA的取值是有讲究的——PT100是正温度系数元件流过电流过大会产生自热效应导致测量偏差行业惯例是限制在1mA以下自热功率约0.1mW对精度的影响基本可以忽略。信号调理级用的是仪表放大器结构把PT100两端约100300mV的小信号放大到STM32 ADC的最佳输入范围03.3V。注意STM32的ADC满量程是3.3V12位分辨率下每个LSB约0.8mV如果直接采100mV级别的信号一个LSB的量化误差就对应好几摄氏度的温度跳变所以前级放大是必须的。这部分电路里还有一阶RC低通滤波器截止频率约100Hz用来抑制工频和射频干扰。MCU最小系统使用的意法半导体STM32F103C8T672MHz主频64KB Flash板载8MHz晶振通过内部PLL倍频到72MHz。电源部分用AMS1117-3.3把外部5V稳压到3.3V给MCU和运放供电。ADC参考电压直接用的VDD如果要求更高精度实际项目可以考虑用外部基准芯片这个咱们放到最后一章二次开发的思路里细说。2.3 三线制接法为什么是必须的这块是很多新手最容易踩坑的地方。PT100在工业现场通常用三线制或四线制接线而不是简单的两线制。原因在于PT100本身阻值很小百欧姆级别如果两线制导线的电阻每米约0.10.5Ω会直接叠加进测量值每米引线就能引入0.3℃以上的误差。三线制把两根引线分别接到电桥或恒流源的反馈端用电路的方法抵消导线电阻的影响精度可以控制在0.1℃以内。原理图里PT100的接线端子是三线端口一根接恒流源输出一根接运放正输入端一根接运放负输入端。设计PCB时三根线要尽量等长、并行走线布线时形成差分对减少共模干扰。这个细节在实际温度变送器的设计中是标配但在很多教学项目里都被忽略了。能在一套Demo资料里看到三线制的完整实现说明作者确实是干过实际工程的。3. 软件核心实现从ADC采样到温度换算的完整代码走读3.1 工程结构与代码组织方式STM32工程基于标准外设库构建没有用HAL库这对想深入理解寄存器操作的人来说是好事——你能看到每个外设的初始化细节。工程目录里包含了核心库文件、硬件驱动和主逻辑文件结构大致如下Project/ ├── Libraries/ // 标准外设库包含GPIO、ADC、USART、I2C等驱动 ├── App/ │ ├── main.c // 主逻辑初始化外设并循环采集 │ ├── pt100.c // PT100查表与插值计算 │ ├── pt100.h │ ├── oled.c // OLED显示驱动SSD1306I2C接口 │ └── oled.h └── User/ ├── stm32f10x_it.c // 中断处理 └── system_stm32f10x.c // 时钟配置能看出来作者把传感器逻辑和显示逻辑做了分离直接改main函数或者pt100模块就能适配自己的需求。OLED驱动用的是SSD1306控制器的I2C模式这也是市面最小系统板上最常见的屏资料里给的驱动可以直接用。3.2 ADC采集与均值滤波先让数据稳下来ADC部分的代码是这个项目的第一个关键点。STM32的ADC1通道0PA0接收来自前级调理电路的电压信号配置成软件触发模式连续采样12次后取平均值。这个12次均值不是随意定的——ADC在STM32上本质是个采样保持电容的充放电过程首次转换误差大连续多次采样的均值可以有效滤除随机噪声。同时CPU在ADC转换期间不跑复杂任务避免数字噪声耦合进模拟通道。void ADC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; // 使能GPIOA和ADC1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // 配置PA0为模拟输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); // ADC1 独立模式12位分辨率软件触发 ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); // 配置ADC通道0采样时间设为55.5个周期降低信号源阻抗影响 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_Cmd(ADC1, ENABLE); // 校准 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); }这里有个细节容易忽略ADC_RegularChannelConfig里把采样时间设成55.5个周期。如果你前级的RC滤波电容比较大信号源阻抗偏高采样时间不够会导致ADC采样电容还没充满就开始转换结果偏低。这个参数在阻抗高时要调大到239.5周期代价是采样速率下降但12次均值本来就不追求高速采样所以这套配置在低速采集场景是合理的。3.3 温度查表与线性插值PT100的非线性补偿PT100的阻值与温度之间的关系是近似线性的但不是精确线性——在0100℃区间最大非线性误差约0.4℃。为了把误差压到0.1℃以内代码里用的是查表加线性插值的方法预先在一个数组里存放-50℃到400℃范围内、每隔5℃对应的ADC原始值或者对应温度点典型电阻运行时找到当前ADC值落在哪两个标定点之间再做线性插值。这个表是怎么来的呢工程里用了一个网上常见的PT100分度表数据以1℃为步进存放分度表最精确但Flash占用太大而以5℃步进加插值误差可以控制在0.05℃以内性价比最高。// pt100.c 查表核心逻辑 // 温度点表-50℃ ~ 400℃步进5℃ const int16_t temp_table[] {-50, -45, -40, ..., 395, 400}; // 对应阻值表每5℃一个分度值单位为Ω放大10倍避免浮点 const uint16_t res_table[] {800, 824, 902, ..., 24709, 25027}; // adc_value是滤波后的ADC采样值 // 通过电压反推电阻值然后查表插值 float PT100_GetTemp(uint16_t adc_value) { float voltage (float)adc_value * 3.3f / 4095.0f; // 前级增益为G恒流源1mA电阻 电压 / G / 0.001 float resistance voltage / GAIN / 0.001f * 10.0f; // 放大10倍的电阻值 uint8_t i; for(i 0; i TABLE_SIZE - 1; i) { if(resistance res_table[i] resistance res_table[i1]) { break; } } if(i TABLE_SIZE - 1) return -999.0f; // 超量程 // 线性插值公式temp temp_i (res - res_i) / (res_{i1} - res_i) * 5 float frac (float)(resistance - res_table[i]) / (float)(res_table[i1] - res_table[i]); return temp_table[i] frac * 5.0f; }这段代码的本质是做了一次线性映射把ADC原始值→电压→电阻→温度中间每步都是线性的或准线性的。说明两点一是GAIN这个宏要根据前级运放的放大倍数来修改这是移植这套代码时最需要核对的地方二是代码里用放大10倍的整数近似电阻值避免了浮点运算带来的Flash开销但在400℃高端温度点电阻约250Ω放大10倍后精度损失约0.01℃完全可以接受。4. 运行与调试从编译烧录到串口验证的完整流程4.1 开发环境与工程导入这套工程是基于Keil MDK开发的建议使用MDK 5.23及以上版本更低版本可能打开工程文件时报错。工程文件是.uvprojx格式双击就能打开。打开后第一件事是先检查Target Options里的芯片型号是否选对——STM32F103C8T6在Keil里对应的是STM32F103C8如果选成高密度型号Flash地址分配会错乱。检查完芯片型号后还需要确认三件事一是C/C选项卡里的Define宏是否包含STM32F10X_MD这是标准外设库的芯片型号宏定义缺失会导致编译报错二是Include Paths里是否包含了Libraries、App和User三个目录三是Output选项卡里勾选Create HEX File这样编译才会生成烧录用的hex文件。如果用的是MDK 5.26以上版本编译器默认是AC6而这套工程是用AC5ARMCC写的直接用AC6打开会报一堆警告甚至错误。解决办法是在Target Options→Target→ARM Compiler里切换成Use default compiler version 5。这个坑我第一天就踩了不切换编译器死活编不过去。4.2 编译与烧录步骤编译前建议先把优化等级调到-O0原因后面避坑章节会细说。确认配置无误后按F7或者点Build按钮编译编译器会依次编译标准外设库、硬件驱动和App层的源文件最终生成Project.hex和Project.axf。第一次编译时间会长一些标准库文件多以后增量编译都在几秒内完成。烧录工具我习惯于用ST-Link V2加STM32 ST-LINK Utility也可以用Keil自带的ULINK调试接口。接线方式ST-Link V2的SWDIO接PA13SWCLK接PA14GND接GND3.3V接3.3V。烧录时注意目标板的外部供电要稳定用USB供电的话5V经过板载AMS1117稳压后输出3.3V一般没问题。# 如果习惯命令行烧录可以用STM32CubeProgrammer STM32_Programmer_CLI -c portSWD modeUR -w ./Project/Project.hex -v -rst这里-v参数是做烧录后的校验-rst是烧录完成后自动复位运行。烧录完成后如果OLED屏亮起来并显示当前温度说明基本通路已经通了。如果显示异常优先检查I2C数据线和屏的地址配置后面避坑部分会说。4.3 串口输出与温度标定验证工程里USART1配置了115200-8-N-1PA9TX和PA10RX引出到板载USB转串口芯片通过USB线连到电脑就能看到数据输出每秒打印一次Temp: 25.31 C Temp: 25.38 C Temp: 25.42 C看到这些数据后强烈建议做一次温度标定用冰水混合物0℃和沸水100℃两个标准点分别读取系统显示的温度值然后用偏差值修正代码里的GAIN常数或ADC零偏值。实际测试我试过零偏修正通常能消除12℃的恒定偏差比在软件里加各种玄学滤波都有效。这个项目源码里已经预留了零偏补偿变量直接在main.c里找到zero_offset改成实测偏差即可。5. 避坑指南与常见问题排查三个稳定性和精度相关的血泪经验5.1 为什么ADC读值大幅跳动温度显示在20℃和60℃之间反复横跳现象传感器接上后OLED显示的温度来回跳明显受干扰。原因一是PT100前端没有做滤波或者是用了两线制且引线过长导致空间电磁干扰直接耦合进小信号链路二是ADC采样时间配置太短采样电容没有完全充满。解决先在前级运放输出到ADC之间加一个100nF电容到地构成截止频率约1.6kHz的RC滤波再把ADC_SampleTime_55Cycles5改大比如ADC_SampleTime_239Cycles5最后手不要靠近传感器引线实测人手接近时读数会跳1℃以上这是人体的热辐射和电容耦合共同作用的结果。5.2 温度偏高23℃怎么校准都压不下来现象与标准温度计对比系统读数恒定偏高。原因恒流源的实际输出电流不是精确的1mA。电路里的采样电阻决定恒流值如果用的电阻精度是1%那实际电流可能是1.01mA温度读数就会整体偏高。这个偏差是系统性的靠软件滤波解决不了。解决测量恒流源采样电阻两端的电压用万用表反推出实际电流值然后在PT100_GetTemp的电阻计算公式里把0.001f替换成实际电流值。这是我在实际调试中踩过的一个典型坑——电路参数决定系统基准软件只能在一定范围内补偿系统偏差。5.3 程序更新后第一次温度读数总是不对现象程序烧录后前12秒显示的温度值明显偏离正常范围随后自动恢复正常。原因ADC在刚上电时内部参考电压还没有稳定且前级运放输出电压建立需要时间。这是硬件电路的固有特性不是程序逻辑问题。解决在主循环开始前加一个1秒的延时并且在主流程里丢弃前5次采样结果再开始做均值滤波。int main(void) { SystemInit(); // 系统时钟初始化 Delay_Init(); ADC_Config(); OLED_Init(); // 跳过上电不稳定阶段 Delay_ms(1000); uint16_t adc_buf[5]; uint8_t i; for(i 0; i 5; i) // 丢弃前5次采样 { adc_buf[i] ADC_GetValue(); Delay_ms(10); } while(1) { // 正常采集与显示逻辑 ... } }5.4 编译时提示identifier u8 is undefined现象使用较高版本MDK时代码里用到的u8、u16类型在编译时找不到定义。原因MDK 5.26之后默认的stm32f10x.h头文件里的数据类型定义不再自动包含进去需要单独引入stdint.h。解决在stm32f10x.h的头部添加#include stdint.h或者把所有u8/u16替换成uint8_t/uint16_t。这种问题从AC5编译器换成AC6后也容易出现最好一次性全局替换掉。5.5 烧录报错Internal command error且程序不运行现象ST-Link烧录时到一半报错程序没烧进去。原因目标板供电不稳或者ST-Link连接线过长/接触不良。注意这是“死机不跑”类问题的典型排查路径跟程序逻辑无关。解决先检查板子的5V电源指示灯是否正常再用杜邦线连接ST-Link时尽可能缩短距离超过20cm就很容易烧录失败。把ST-Link的SWD频率从4MHz降到1MHz也能增加烧录的稳定性。6. 二次开发实战从「能用」到「好用」的三个升级方向6.1 方向一替换外部高精度基准源把系统精度从1℃推到0.2℃用原版ADC参考电压即VDD设计时供电电源的波动会直接影响ADC的转换结果。VDD上的纹波通常有几毫伏在12位ADC上就等价于几个LSB的误差。如果把外部的3.3V换成专用的ADC基准源芯片比如ADR420或者REF3030然后把VREF引脚从VDD断开、接到基准源的输出ADC的参考电压稳定度能提升一个量级。需要把ADC_Config函数里的对应配置同步改一下然后重新做一次两点标定。市面上不少温度变送器的精度之所以能标到0.1℃根源之一就在这里。这套源码中ADC配置是独立成函数的所以替换起来空间很大。6.2 方向二增加PID控制逻辑让温度采集系统升级成温度控制系统当前这套项目只做采集和显示没有控制执行机构。如果要在它上面加控制功能可以在主循环里加入一个PID算法模块把实测温度和设定温度做差经过增量式PID计算输出PWM信号控制加热器或风扇。增量式PID的实现思路并不复杂// 位置式PID简化实现适合温度这种大惯性对象 typedef struct { float Kp, Ki, Kd; float err_last; // 上次误差 float err_prev; // 上上次误差 } PID_TypeDef; float PID_Calc(PID_TypeDef *pid, float setpoint, float measure) { float err setpoint - measure; float out; // P比例项I积分项简化版直接用误差累计D微分项 out pid-Kp * err pid-Ki * (err - pid-err_last) pid-Kd * (err - 2*pid-err_last pid-err_prev); // 限幅防止输出越界导致执行器饱和 if(out 100.0f) out 100.0f; if(out 0.0f) out 0.0f; // 更新历史误差 pid-err_prev pid-err_last; pid-err_last err; return out; }这里用的是增量式PID只需要保留最近三次误差就能计算输出在8位、32位微控制器上都很常见。温度对象的特点是惯性大、纯滞后时间长所以Kp要小一些Ki最关键——积分项负责消除稳态误差但设置太大会造成超调甚至震荡。实际调试时建议先置Ki和Kd为0只调Kp让系统不震荡再加Ki消除稳态误差最后加一点Kd抑制超调。这样三步走基本不会翻车。把PID输出映射成PWM占空比时要注意加热器和散热器之间的死区。比如输出在10%以下时加热器功率太低电流根本不足以驱动继电器动作这时候应该直接输出0等误差累积到一定值再开启。这种死区处理是温度控系统里常见的细节工程问题。6.3 方向三引入第二路传感器和均值融合算法如果采集点有双传感器的冗余需求比如冷端补偿和主测温可以把ADC的通道1也配置起来用同样代码参数采第二路PT100然后在软件里做加权平均。加权系数根据两路传感器的安装位置和热响应速度来确定热响应快的路给更高权重。这种多传感器融合架构在大型设备温度监测里很常见比单传感器抗故障能力强很多。从那以后我每次拿到一套嵌入式工程第一步永远是先把人家的系统架构和信号链路摸清——这个项目的价值也正在于此模拟前端、数字采集、软件补偿、显示输出整条链路都能在上面跑通。希望帮到你。本文还有配套的精品资源点击获取