简介:针对 STM32F103 与 PT100 温度测量场景,这份压缩包以 ADC 电压采集为核心,覆盖 PT100 在 0°C 时 100Ω 的基准特性、恒流源将阻值变化转换为电压,再到 STM32 内置 ADC 采样与温度换算的完整处理链路。特别适合嵌入式初学者理解模拟前端、ADC 配置和分度表查算流程,也可作为工业测温项目的基础模型。压缩包为 RAR 格式,共 4 个文件,包含 2 个 C 源文件与 2 个头文件,分别承担 ADC 初始化与 PT100 温度计算,整体仅 4KB,结构清晰、便于裁剪。已有 5540 人学习下载,说明该方案在常见 PT100 测量设计中具有一定参考价值。代码中对 Adc 模块的初始化流程、采样触发与中断处理给出了明确注释,并基于 PT100 分度表实现了电压到温度的转换逻辑,保留了标定与线性化处理的思路,可直接移植,帮助开发者快速搭建可用的温度测量原型并进一步优化精度。 前几天有个做温控设备的朋友问我,用STM32F103的ADC直接读PT100,为什么温度值总是跳来跳去,隔着半米都能看出显示在抖。这个问题我太熟悉了。PT100先把温度变成电阻,采样电路把电阻变成电压,STM32F103的ADC再把电压变成数字,最后软件把数字换算成温度——链路里任何一环处理不好,最后表现出来就是"精度翻车"。
这篇东西不打算讲太虚的概念,就围绕"STM32F103 + PT100 + ADC 电压转换温度"这条主线,把选型、采样电路、ADC配置、换算算法、噪声排查这几个环节挨个说清楚。适合刚接触ADC测温、或者做出来的东西精度始终不达标的朋友,也适合打算量产、正在纠结成本与精度之间如何取舍的工程师。
1. 为什么是PT100:选型背后的温度测量逻辑
1.1 从一次"NTC翻车"说起
我最早做测温产品用的是NTC热敏电阻,理由很朴素:便宜,电路简单,一个电阻分压就完事。结果产品进入批量测试后问题来了——同一批传感器,同一套标定参数,换到下一批物料就不准了,偏差能到2~3℃。后来一查,NTC的B值离散度大,而且长期稳定性一般,库存放上半年,阻值能漂出不少。
换PT100之后,这批麻烦少了很多。铂电阻的核心优势是材料特性稳定,0℃时标称100Ω,温度系数大约0.385Ω/℃,重复性和互换性都挺好。对"工业级温度测量"这种需求来说,PT100属于"多花几块钱买省心"的典型方案。STM32F103这类MCU本身没有模拟前端,但配上合适的采样电路,完全能把它读准。
1.2 100 Ω 背后的线性度陷阱
PT100虽然比NTC线性很多,但并不是完全的线性电阻。它在0~100℃范围内,平均温度系数约为0.385Ω/℃,但实际阻值-温度曲线是略微弯曲的。IEC 60751标准里给的是Callendar-Van Dusen方程,后文会详细说。
很多新手会直接把"当前电阻除以0.385得到温度",这在窄范围(比如20~30℃)内误差不大,一旦范围拉到-20~100℃,误差能达到好几度。所以,电压转换温度这步,不能图省事用一阶线性公式硬套。
简单说,选PT100看中的是稳定性和可重复性,但它也不是免校准的万能器件,后端的换算算法和校准流程照样得认真做。
2. 传感器到ADC:采样电路才是精度的第一道分水岭
2.1 二线制、三线制、四线制,怎么选
PT100接线方式直接决定测量误差来源,这是很多人第一次做样机时忽略的点。
- 二线制:两根线既是激励线又是测量线,导线电阻会直接叠加到PT100的阻值上。如果导线电阻是1Ω,温度误差就有约2.6℃。样机用短线可能无所谓,实际工程布线一长就露馅。
- 三线制:一根线走激励电流,另外两根分别走测量回线和反馈线。因为测量回路中不流过激励电流,所以导线电阻带来的压降可以被抵消掉一部分。工业现场普遍用这种接法,性价比最高。
- 四线制:用独立的四根线,激励和检测完全分开,精度最高,但成本和接线复杂。适合实验室级测量,普通产品很少用。
STM32F103的ADC读的是电压,不是电阻,所以电路的关键是让"PT100的电阻变化"尽可能无失真地变成"地参考的电压信号"。三线制是日常项目里平衡成本和精度的最优解。
2.2 恒流源电路设计实例
把PT100电阻变成电压,有两种最常用的办法。一是电阻分压,PT100串联一个精密电阻,从中间取分压点。这电路简单,但有两个毛病:供电电压波动会直接影响输出电压;串联电阻自身的温漂也会混入结果。二是恒流源激励,让恒定电流流过PT100,两端的电压就等于I × R,电压和电阻严格成正比,这个思路更干净。
我习惯用1mA恒流源做激励。为什么是1mA?因为PT100在0℃时是100Ω,1mA下压降只有100mV,后续放大电路容易处理;电流再大,比如5mA,自热效应就会变得明显,PT100自身发热会抬高测量值。
一个简单实用的恒流源可以用运放搭建:用TL431或者REF3030产生一个2.5V基准,经过精密电阻转换为电流,运放加三极管扩流后输出给PT100。大致的计算逻辑是:
- 基准电压2.5V,取样电阻2.5kΩ,基准端到运放反相输入端虚短,三极管发射极电阻上的电流约为 2.5V / 2.5kΩ = 1mA。
- 这个电流流过PT100,0℃时输出电压 = 1mA × 100Ω = 100mV。
- 100℃时PT100阻值约138.51Ω,输出电压 = 138.51mV。
这个100mV级别的信号,如果直接进STM32F103的ADC,12位分辨率满量程3.3V,一个LSB约0.8mV。算下来0.8mV对应约2Ω多,也就是温度分辨率将近5~6℃,显然不够用。所以必须在ADC之前加一级放大,把100~140mV的区间放大到接近ADC的满量程。
2.3 ADC输入范围与参考电压匹配
STM32F103的ADC是12位,输入范围一般到VREF+,通常是3.3V。把PT100的电压放大到哪个范围,决定了分辨率。
我以前常用的做法是:仪表放大器(比如AD620或者国产的INA128)把差分信号放大,使0℃时输出约0.3V,100℃时输出约2.5V,这样整个动态范围覆盖了ADC比较好用的区间。
如果你是分压法加运放跟随,务必把公共端处理好,不要让ADC输入引脚悬空,也不要把信号直接拉到超出VREF+的范围。ADC输入引脚电压超过VDDA或低于VSSA,轻则读数错误,重则损坏IO。
小结一下:采样电路的核心是"恒流源激励 + 精密放大 + 匹配ADC量程",这决定了系统精度的上限。而STM32F103的ADC配置决定的是能把这个模拟信号还原到什么程度。
3. STM32F103的ADC采样:配置、采样时间与软件滤波
3.1 ADC基本配置与参考电压
STM32F103的ADC是12位逐次逼近型,时钟最高14MHz,采样周期可配置。这里有两个容易踩的坑:第一,ADC时钟不要直接拿PCLK2不分频上去跑,APB2时钟如果是72MHz,ADC分频器至少要6分频到12MHz,稳定一点用8分频到9MHz,太高会影响转换准确度。
第二,VREF+引脚如果直接从3.3V供电取,3.3V上的纹波就会直接进到测量结果里。要求高时,用单独的基准电压芯片给VREF+供电,或者至少加LC滤波。我之前遇到过ADC全码跳动,排查半天,最后发现是DC-DC模块的开关纹波串到了VREF+上。
3.2 采样时间:为什么不能一直用默认值
ADC内部有个采样电容,采样期间开关闭合,外部信号源通过等效阻抗给这个电容充电。如果外部阻抗很大,采样时间不够,电容电压还没充到信号的实际值,转换结果就会偏低。
PT100配合1mA恒流源时,输出阻抗不算太大,但后面加了放大器和RC滤波后,源阻抗可能到几十kΩ。此时建议把采样时间配置到最大档,比如STM32F103支持的最高239.5周期,如果ADC时钟9MHz,一次采样时间约26.6μs。对温度测量这种慢速信号,这点时间完全不影响实时性,但能显著减少采样电容充电不足造成的读数误差。
3.3 软件滤波:滑动平均不能无脑用
ADC读数跳动,很多人第一反应是滤波。我一开始也直接上滑动平均,结果发现偶尔一个大的毛刺会把平均值拉偏好多,温度曲线出现"尖刺"。后来改成先做中位值剔除,再做滑动平均:
- 连续采样5次,去掉最大值和最小值,剩下的3次取平均;
- 再对这个均值做宽度为10的滑动平均。
这样既能压制随机噪声,又能避免单一毛刺污染结果。代码实现也不复杂:
#define FILTER_LEN 10 uint16_t filter_buf[FILTER_LEN]; uint8_t filter_index = 0; uint32_t filter_sum = 0; uint16_t adc_filter(uint16_t adc_value) { filter_sum -= filter_buf[filter_index]; filter_buf[filter_index] = adc_value; filter_sum += adc_value; filter_index = (filter_index + 1) % FILTER_LEN; return (uint16_t)(filter_sum / FILTER_LEN); }对于5取3中位均值,可以单独写个小函数,把5次ADC原始值排序后取中间三个平均。这比直接平均多不了几行代码,滤波效果却好很多。
3.4 用DMA省CPU
温度采样没必要让CPU每轮询一次,用ADC的多通道扫描加DMA是更合理的做法。配置ADC为连续转换模式,开启DMA循环传输,采到的数据自动落到内存数组里:
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buf, 4);这样每次转换完成后数据自动更新,主循环只用去数组里取值做滤波,CPU占用率几乎为零。实测在STM32F103上跑多个通道采样、LCD显示和通信协议,一点压力都没有。
4. 电压转温度:查表、公式还是查表加插值
4.1 先把ADC值还原成电阻值
不管后面用什么算法,第一步都是从ADC码值反推PT100的电阻值。假设放大电路输出函数是线性的:Vout = gain × (Rpt100 × I) + offset,那么反向计算就是:
float v_out = (float)adc_value / 4095.0f * 3.3f; float r_pt100 = (v_out - offset) / (gain * 0.001f);这里的gain和offset需要在装机后实测标定,不能只看理论值。因为电阻容差、基准电压偏差、运放失调都会让真实增益和设计值有出入。
4.2 Callendar-Van Dusen方程:教科书级的精确解法
IEC 60751标准给出的PT100阻值-温度关系比较复杂,但工程上可以用简化的Callendar-Van Dusen方程。0℃以上时:
R(t) = R0 × (1 + A×t + B×t²)
其中R0=100Ω,A=3.9083×10⁻³,B=-5.775×10⁻⁷。0℃以下时要多一项C×(t-100)×t³,C=-4.183×10⁻¹²。对大多数0~100℃的应用,只用A和B两项就够。
反过来,由电阻求温度,可以直接解一元二次方程:
float pt100_res_to_temp(float r) { const float R0 = 100.0f; const float A = 3.9083e-3f; const float B = -5.775e-7f; float delta, t1, t2; delta = A * A - 4.0f * B * (1.0f - r / R0); if (delta < 0.0f) return -40.0f; t1 = (-A + sqrtf(delta)) / (2.0f * B); t2 = (-A - sqrtf(delta)) / (2.0f * B); return (t2 > t1) ? t2 : t1; }这段代码在Cortex-M3上运行没压力,算一次也就几十微秒,完全可以对每帧数据实时计算。
4.3 查表法:适合资源紧张或追求固定耗时的场合
查表法的思路是:预先在代码里放一张"电阻-温度"对应表,比如每5℃一个点,运行时用二分查找找到区间,再线性插值。
好处是计算量固定、没有浮点运算也能做(用整数定点),缺点是表要占一些Flash。温度范围从0到200℃,每5℃一个点,才41个条目,一张表没多大。
typedef struct { uint16_t resistance_x10; /* 阻值×10,避免浮点 */ int16_t temperature; /* 温度×10 */ } pt100_entry_t; static const pt100_entry_t pt100_table[] = { { 1000, 0 }, /* 100.0Ω -> 0.0℃ */ { 1019, 50 }, /* 101.9Ω -> 5.0℃ */ { 1039, 100 }, /* 103.9Ω -> 10.0℃ */ /* ... */ };线性插值处理一下,查表法在常温段能轻松做到0.1℃级别的显示分辨率。注意表里的值必须是"实测标定后的修正值",否则表再精确也救不了前端电路的偏差。
4.4 三种换算方式的实测对比
拿同一个信号源在恒温槽里做对比,我的测试结果大致是:
| 算法 | 常温段误差 | 高温段(200℃)误差 | 额外开销 |
|---|---|---|---|
| 一阶线性近似 0.385Ω/℃ | 0.8~1.5℃ | 4~5℃ | 最低 |
| C-VD方程直接计算 | 0.1~0.3℃ | 0.3~0.5℃ | 低 |
| 5℃查表+线性插值 | 0.1~0.2℃ | 0.3~0.6℃ | 极低 |
结论很直接:如果你的产品只工作在20~30℃窄范围,一阶近似还能凑合;一旦范围拉宽,要么用C-VD方程,要么用查表插值。我更偏向查表插值,因为计算时间固定,而且校准的时候可以直接改表项。
5. 实测排查:从跳数到0.1℃精度的完整链路
5.1 先怀疑电源,再怀疑传感器
ADC读数跳动,最常见的不是软件问题,而是电源问题。用示波器看VREF+和VDDA,如果上面有周期性的毛刺,多半是DC-DC或数字电路串过来的。STM32F103的VDDA和VREF+引脚一定要加去耦电容,而且去耦电容要尽量靠近引脚,引线越短越好。
我踩过一个非常隐蔽的坑:板子上的继电器每次切换,ADC读数就跳几十个码。排查发现继电器线圈没有加续流二极管,关断瞬间产生的反向电动势通过地线干扰了模拟部分。继电器的电源和模拟电源彻底分开走线,问题才消失。
5.2 自热效应与电流选择
恒流源电流越大,信噪比越高,但PT100自身发热也越严重。PT100在空气中散热条件一般,1mA电流下自热误差大约0.02~0.05℃,5mA时可以到0.3℃以上。做高精度测量,激励电流取1mA是很多厂商的默认选择;做快速响应但精度要求不高时,才考虑加大电流。
另外,PCB走线和传感器探头的散热条件也会影响自热,传感器放进金属套管和直接暴露在空气中,自热表现完全不同。这个问题正规厂家的PT100说明书里会标注热阻,选型时留意一下。
5.3 单点校准与两点校准
硬件精度的最后一步是校准。量大的产品不可能逐台精密标定,但至少要做两点校准:
- 冰水混合物(标准大气压下0℃)作为低温点;
- 沸水(100℃,或者用恒温槽)作为高温点。
采集实测ADC值后,反推出真正增益和偏移,存到Flash里。之后每次开机读Flash里的校准值,参与电阻还原计算。
如果现场只能提供一个稳定温度,也可以用单点校准:先把零点偏移校准掉,增益用理论值。这样能显著改善"零点不对"的问题,但对增益误差无能为力。
void calibrate_two_point(float adc_low, float temp_low, float adc_high, float temp_high) { float r_low = 100.0f + 0.385f * temp_low; /* 近似 */ float r_high = 100.0f + 0.385f * temp_high; gain = (adc_high - adc_low) / (r_high - r_low); offset = adc_low - gain * r_low; }5.4 一块小PCB上的模拟设计细节
采样电路和MCU在同一个小板子上时,建议把模拟部分的地单独走线,单点汇接到电源地。ADC采样脚附近不要走高频数字线,尤其是STM32F103的PA1、PA2、PA3这类可以复用的模拟输入脚,周边如果有PWM走线,很可能引入串扰。
模拟地和数字地之间我习惯用0Ω电阻连接,位置在ADC引脚所在那一侧,让模拟回流路径尽量短。电源方面,如果板上只有3.3V一路,用一颗磁珠或小电阻把模拟电源和数字电源隔开,形成"模拟VDD岛",效果立竿见影。
最后再分享一个我自己的习惯:任何温度采集项目,都会在代码里保留"ADC原始值、电阻还原值、滤波后温度"三个变量的调试输出。现场问题八成在模拟链路,没有原始数据做参照,软件调了半天也找不到病根。把这个调试通道留好,比什么玄学调参都管用。
本文还有配套的精品资源,点击获取