简介:这是一份基于STM32F103C8T6微控制器的PT100温度采集与串口通信完整工程源码包,面向嵌入式初学者、电子竞赛选手以及温度测控项目开发者。工程完整演示了利用片内模数转换器读取PT100铂电阻随温度变化的阻值,再通过通用异步收发器将温度数据发送到上位机的全过程,涉及传感器检测电路设计、ADC通道配置、采样时间与参考电压设定、连续转换模式、DMA或中断方式读取,以及串口波特率、数据位、停止位和校验位的协议匹配等关键环节。压缩包共253个文件,大小约7.42MB,内容以C/H源程序、Keil工程文件(uvprojx、uvoptx)、编译产物(hex、axf、o)以及调试辅助文件为主,目录结构清晰,可直接打开工程进行编译、烧录与验证。目前已有1214人学习下载。借助该工程,读者可以快速掌握STM32 HAL/LL库环境下ADC加串口的基本开发流程,理解连续转换与DMA传输对系统效率的提升,并借鉴工程中的PT100检测电路设计与上位机数据解析思路,作为毕业设计、课程项目或实际温度监测系统的起步模板。
1. 一个把 PT100 温度采集做完整的 STM32 工程:从 100Ω 铂电阻到串口温度数据
冷库、恒温箱、工业现场这一类场景里,温度传感器通常不选成本更低的 NTC,也不选怕断线的热电偶,而是选 PT100。原因很简单:它 0℃ 时刚好 100Ω,线性度和长期稳定性都很能打。但这个「物理特性稳定」恰恰是工程上的难点,0.385Ω/℃ 的微弱变化意味着导线电阻、基准漂移、运放失调都会直接变成温度误差。手头这份 STM32F103C8T6 的 PT100 工程压缩包,把信号链、固件程序和调试要点配成了一整套可以直接跑的方案:三线制接法、恒流源激励、差分放大、ADC 采样、查表插值再到串口输出全都有,适合做温控设备预研的工程师和想把 PT100 移植到自己板子上的开发者。
拿到这种资源,第一步不是急着烧录,而是先搞清楚它为什么这么接,再谈改哪里。下面从信号链开始拆。
2. 为什么这么接:PT100 信号链设计与参数推算
2.1 激励方式怎么选:电压分压不是不行,而是不稳
PT100 本质是铂电阻,输出阻抗低,常温附近大概 100~138Ω。它的问题不是信号小,而是「小到不经放大根本喂不饱 ADC」。在 0~100℃ 范围内,电阻变化率约 0.385Ω/℃,如果直接用 3.3V 分压网络,把 100Ω 和另一个 100Ω 精密电阻串起来,1℃ 对应的电压变化只有 0.1mV 量级,而 STM32F103 的 12 位 ADC 在 3.3V 参考下的 LSB 大约 0.8mV,也就是说一个 LSB 就带好几度。于是放大是必须的,但放大之前得先解决激励。
常见做法是用恒流源给 PT100 供 1mA 电流,再对电阻两端的电压做差分放大。为什么不用电桥加电压源?我早期做一版就是用 3.3V 直接分压,结果电源轻微波动,读数就像心电图一样抖。后来换成恒流源,参考源用独立的 2.5V 精密基准,才把问题压下去。这背后的逻辑是:恒流源输出的电流对电源波动不敏感,只要电流稳定,PT100 上的电压就只随电阻变化,而电桥方案里电压源波动会同时改变桥臂两侧电位,放大后很难分离。
这份工程里用的是 1mA 恒流源,在 0℃ 时产生 0.1V,100℃ 时产生 0.1385V,跨度 38.5mV,这个跨度乘以 20 倍增益后约 0.77V,恰好落在 ADC 输入范围内。如果想要更低自热,可以把电流降到 0.5mA,但代价是信号幅度减半,增益要随之翻倍,噪声也会上来,这是一个需要权衡的折衷。另一条路是选更高的激励,像 2mA,信号幅度大了但自热也更重,多数资料推荐 1mA 作为平衡点。
恒流源的实现方式也值得说一句。工程里用的是运放加三极管组成的经典恒流电路,比买集成恒流源芯片便宜,温度系数也能做到几十 ppm 级别。如果自己重画这块电路,注意采样电阻要用低温漂的金属膜电阻,因为恒流源的精度直接取决于这个电阻,1% 的阻值误差就是 1% 的电流误差,最终全部体现在温度读数上。
2.2 三线制不是玄学:导线电阻会吃掉半度到一度
两线制的误差很多人第一版都会踩。假设传感器线 2 米,单根铜导线电阻 0.2~0.5Ω 很常见,这个电阻和 PT100 串联,会直接叠加到测量结果上。0.5Ω 折合到 PT100 是 0.5 / 0.385 ≈ 1.3℃,而工业现场 0.5℃ 误差可能就意味着设备误报警。
三线制的做法是:恒流源从一根线送进 PT100,另外两根线把传感器两端的电压引到运放的差分输入端。因为采样回路几乎不取电流,线电阻上的压降就进不了信号通路。工程里对应的接线顺序是 V+、S+、S- 三根线,V+ 接恒流源输出,S+ 和 S- 接差分放大正负输入端。这里的关键是恒流源要带足够的负载能力,长线情况下导线本身几百毫欧的电阻不会影响电流精度,而差分放大器要选择输入偏置电流小的运放,否则偏置电流会在采样线上产生附加压降。
注意三线制只抵消了导线电阻的静态贡献,如果三根线材质或长度不一致,残余误差还是有。所以现场布线要求三根线尽量等长、同材质,这也是为什么 PT100 传感器成品通常三根线颜色固定且粗细一致。如果项目允许,四个端子引线可以彻底消除这个问题,但接口成本高,工程里多数不用。还有一个现场容易翻车的点:三线制接反也会「能工作但不准」,因为恒流源线和采样线互换后,采样线里流过了激励电流,线阻直接变成误差。调试时先量线序,别急着怀疑电路。
2.3 差分放大与基准源参数:让量程、增益和参考电压对得上
拿到这个工程,第一件要确认的事是运放增益到底是多少,这决定你能测到多少度。常见方案是用一个双运放,前级做差分放大,后级做一个低通跟随器或二阶滤波。工程里用的电路,反馈电阻 200k,输入电阻 10k,理论增益 200k / 10k = 20。按这个增益,0~250℃ 对应电阻 100~194.07Ω,输出电压 2.0V 到 3.88V,已经超出 3.3V ADC 量程,所以这个配置的实际测温上限是电路电压不超量程的界限,约 215℃。如果项目只需要测到 120℃,可以把反馈电阻降到 150k,让满量程更宽;如果希望 0.1℃ 分辨率保留,增益尽量设计在让 250℃ 对应 3.0V 附近。这里的计算逻辑是:
- 激励电流 1mA,250℃ 时 PT100 电阻 194Ω,信号电压 0.194V
- 增益 20 倍,放大后 3.88V,超量程
- 反过来算,要让 250℃ 对应 2.8V,增益应取 2.8 / 0.194 ≈ 14.4,取 15 倍比较合理
ADC 参考电压不要使用芯片内部 VREF。STM32F103C8T6 内部参考精度只有约 ±5%,且随温度漂移,直接导致测量值跟着环境温度变化。建议把 REF 引脚接到外部 2.5V 精密基准,TL431 是最常见的选择,它的温度系数大约 50ppm/℃,做 0.1℃ 级的设备够用。ADC 量程变成 0~2.5V 后,前面的增益换算要做相应调整:250℃ 对应 0.194V 信号,要放大到 2.5V 以内,增益上限约 12.9,这也是解压工程后第一处需要改的参数。
补充一个容易被忽略的点:运放的共模输入范围。PT100 的电压信号是浮在恒流源负端之上的,比如激励电流从 3.3V 流向 GND,PT100 上浮电压可能在 1.5V 附近。如果运放是单电源供电,必须确认输入共模范围能覆盖这个中电平,首选轨到轨输入运放,比如常见的通用型号,否则输出会提前削顶。信号削顶导致读数非线性,是排查时最容易误判成「PT100 非线性」的坑之一。
PCB 布线也在这里提一句:差分放大器的两个输入走线要尽量等长、对称,并且下方要有完整的模拟地平面。如果走线不对称,两条线上的寄生电容不同,共模转差模的噪声会被放大。这些细节在低速采样时不一定暴露,但换到工厂环境就会突然冒出来。
3. 从压缩包到串口数据:工程结构与关键代码怎么改
3.1 解压后先认路:找对文件再动手
这份压缩包用的固件库是标准外设库(SPL)版本,不是 HAL 库。对 STM32F103 这种资源紧张的片子,标准库更省 Flash,也更容易看懂底层,但代价是不同芯片型号之间移植性不如 HAL。工程目录里主要看这几个文件:
| 文件 | 职责 |
|---|---|
| main.c | 主流程,初始化与循环调度 |
| pt100.c / pt100.h | PT100 换算与查表接口 |
| adc_dma.c | ADC 与 DMA 初始化 |
| calibration.c | 校准参数与 Flash 读写 |
| uart.c | 串口发送 |
建议第一次打开工程先整体编译一遍,确认工具链版本兼容,再逐个文件读。标准外设库有个常见问题:很多工程是从别的板子上复制出来的,时钟初始化 SystemInit 里可能还留着原板子的外部晶振配置。如果板子用的是 8MHz 晶振,系统时钟应配置为 72MHz,PLL 倍频系数 9;如果用的是 25MHz 晶振,倍频系数要改成 3。这个不对,后面的串口波特率和 ADC 采样时间全部会偏,而且软件调不出来。
主流程的逻辑是这样:系统时钟 72MHz 跑起来,GPIO、ADC、DMA、UART 逐个初始化,然后进入 while 循环,循环里每 100ms 读一次 DMA 缓冲区的平均值,换算温度后通过串口发出去。串口波特率默认 9600,8 数据位、1 停止位、无校验,数据格式是一行T=25.3C,末尾带换行符,方便直接接串口助手或者后端的 Modbus 转接器里解读。如果接 PLC,建议按 PLC 的字符串协议改,或者直接换成带 CRC 的帧格式,把起始符和校验加进去,否则现场长线传输偶尔会错一位。
3.2 ADC 加 DMA:用硬件循环采样代替 CPU 忙等
PT100 信号经过放大后送到 ADC 引脚,工程里接的是 PA1。ADC 配置用规则通道 + DMA 循环模式,一次配置让硬件自动连续采样 16 次,CPU 只需要在用户定时到了之后读 DMA 缓冲区。这个设计的好处是把采样和计算解耦,温度换算放到主循环里,采样密集度由 DMA 保证,不会因为 CPU 有其他任务产生采样间隔抖动。
ADC 初始化代码片段如下:
// ADC1 通道1,DMA循环模式,16次采样做软件平均 ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_DMA1, ENABLE); // 配置PA1为模拟输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // DMA1通道1对应ADC1 DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buf; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 16; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_Init(DMA1_Channel1, &DMA_InitStructure); // ADC1配置:12位分辨率、连续转换,采样时间取最长 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 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_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE);这里有两个参数值得专门说明。一个是 DMA_BufferSize = 16,意思是 DMA 会连续搬运 16 个 ADC 转换结果到 adc_buf 数组,之后再从头覆盖,形成环形缓冲,主循环只要每 100ms 读一次这 16 个数的平均值就行。另一个是 ADC_SampleTime_239Cycles5,这是该芯片上最长的采样时间,约 3μs,能让内部采样电容充分充电。PT100 信号内阻低,采样时间短问题不大,但长采样时间对抑制高频噪声有帮助,16 次平均配合这个采样时间,等效的噪声带宽会明显收窄。
主循环里的读取部分往往被写得很随意,但有一个细节要注意:DMA 是异步填充数组的,主循环读平均值时可能正好赶上第 5 和第 6 次数据被更新,造成平均值轻微不连续。常见的做法是每次先禁能 DMA,拷贝缓冲,再使能 DMA;或者接受这一个周期的微小波动,因为在 100ms 周期里影响极小。工程里采用的是第二种方式,代码简单,现场也没有发现可见跳变。
注意:如果换到其他型号的 F103,DMA 通道映射可能不同,直接搬代码容易踩坑。比如有的型号 ADC1 对应 DMA 通道并非 1,先查数据手册再初始化,这是移植时最常出的问题。
3.3 温度换算函数:先读数,再换成电阻,最后查表
拿到 16 个 ADC 原始值后,第一步是求平均,然后换算成放大前的电压,再通过激励电流换算成 PT100 电阻。这个过程里有个容易漏掉的地方:如果采用外部 2.5V 基准,ADC 满量程不是 3.3V 而是 2.5V;另一个是运放增益 20 倍,要除回。工程里给的核心换算函数如下:
#define ADC_VREF 2.5f // 外部基准电压 #define ADC_FULL_SCALE 4096.0f #define AMP_GAIN 20.0f #define EXC_CURRENT 0.001f // 恒流源1mA float adc_to_resistance(uint16_t adc_value) { // adc_value / 4096 * VREF 是放大后的电压 float vin = ((float)adc_value / ADC_FULL_SCALE) * ADC_VREF; float vr_pt100 = vin / AMP_GAIN; return vr_pt100 / EXC_CURRENT; // R = U / I }这段代码的逻辑是反向还原传感器电阻,返回值单位是欧姆。例如 25℃ 时理论电阻 109.73Ω,对应 ADC 原始值约 1913,换算结果应在 109~110Ω 之间。如果拿到的工程里 ADC 参考是 3.3V,记得把 ADC_VREF 改成 3.3,否则所有温度会系统性偏高 30% 左右,这是移植到自制板时最常见的错误。
再往上一层,主循环每 100ms 做一次完整的采样、换算、发送。发送用标准库的串口函数,占用时间很短。如果你要把温度数据接到 RS485 总线上,建议把发送做成环形队列,避免串口发送期间主循环被阻塞,但这个工程里默认只是单机调试,直接轮询发送足够。等到真接总线时,再加队列和 CRC 也不晚。
4. 查表、插值与两点校正:把 0.1℃ 误差压到可接受范围
4.1 生成一张 -50℃ 到 250℃ 的分度表
PT100 在 -200℃ 到 850℃ 全范围内并非严格线性,但在工业现场常用区间可以用分段查表。工程里提供了一张分度表,从 -50℃ 到 250℃ 每 1℃ 一个电阻值,数据来源是 IEC 60751 标准中 PT100 的典型值。以下展示开头的几个点,方便核对表的边界和单位:
// 分度表:-50℃ 到 250℃ 每 1℃ 的电阻值(单位:Ω) const float pt100_table[] = { 80.31f, // -50℃ 84.27f, // -40℃ 88.22f, // -30℃ 92.16f, // -20℃ 96.09f, // -10℃ 100.00f, // 0℃ 103.90f, // 10℃ 107.79f, // 20℃ 109.73f, // 25℃(比对点) 111.67f, // 30℃ // ... 后续省略 };这张表的间隔是 1℃,如果只做最近邻查表,最大误差会出现在两个点正中间,约 0.19Ω,折合 0.5℃。所以工程里实际用的是二分查找加线性插值,把表内误差压到 0.01℃ 量级。注意插值出来的精度再高,也高不过 ADC 和运放本身的测量误差,表的密度更多是为了减少计算截断误差,不解决模拟前端的问题。
如果工程给的表不完整,自己生成一张也不难。用 IEC 60751 的标准公式,在电脑上先算一遍,再按 C 语言数组格式输出,比手工抄表靠谱得多:
# 生成 -50~250℃ 每1℃的PT100电阻值 r0 = 100.0 A = 3.9083e-3 B = -5.775e-7 C = -4.183e-12 for t in range(-50, 251): if t >= 0: r = r0 * (1 + A * t + B * t * t) else: r = r0 * (1 + A * t + B * t * t + C * (t - 100) * t * t * t) print(f"{r:.2f}f, // {t}℃")生成后和工程里的表逐项比对一下,如果个别点差超过 0.01Ω,说明对方用的系数版本有差异,以哪个为准取决于你现场用的传感器等级。工业现场 A 级 PT100 的允许误差是 ±(0.15 + 0.002·|t|)℃,比起表本身的截断误差要大得多,所以表只要没抄错,通常不是精度瓶颈。
如果要扩展温度范围,比如低温测到 -100℃ 或高温测到 400℃,只需要把表首尾补齐。查表函数的分段逻辑不用改,但要注意 -100℃ 附近 PT100 灵敏度只有约 0.342Ω/℃,同样电压下分辨率会略降,这是材料特性,查表救不了。
4.2 二分查找 + 线性插值:STM32F103 也能快速算
F103 没有硬件浮点单元,但 float 运算是软件模拟,主频 72MHz 下执行几百次浮点运算也不是问题。查表函数写成整数二分,只在最后插值步骤用一次浮点除法,速度和精度都能兼顾。
// 根据PT100电阻值查温度,返回值单位℃ float resistance_to_temp(float resistance) { int low = 0; int high = TABLE_SIZE - 1; // 表长度 301 // 超出表范围直接限幅 if (resistance <= pt100_table[0]) return -50.0f; if (resistance >= pt100_table[TABLE_SIZE - 1]) return 250.0f; // 二分查找相邻两个表项 while (high - low > 1) { int mid = (low + high) / 2; if (pt100_table[mid] <= resistance) { low = mid; } else { high = mid; } } // 线性插值:温度 = 下界温度 + 电阻比例 * 1℃ float r0 = pt100_table[low]; float r1 = pt100_table[high]; float ratio = (resistance - r0) / (r1 - r0); return (float)low - 50.0f + ratio; }这个函数里,low 和 high 分别是电阻值所在的左右表项索引,因为表间隔固定 1℃,温度差就是 (high - low),也就是 1。ratio 是介于 0 到 1 之间的插值系数。把索引换成摄氏温度需要偏移 -50,因为表首项是 -50℃。二分查找 300 项最多走 9 次循环,比从第 0 项开始遍历省大约 30 倍时间,在 100ms 一次的温度采集周期里几乎不占时间。
如果不想维护 301 个 float 的表,也可以直接用标准公式反解多项式。但那样每次都要做三次方程求根,F103 算起来明显更重,而且公式中低温段的 C 系数处理容易出错。查表加插值是这个平台上最稳妥的写法。
工程里这个函数被封装成 module_temperature.c 里的一个对外接口,上层只传电阻值,拿回温度值。如果以后换用其他热电阻,比如 PT1000,只需要换一张阻值十倍的表,接口保持不动,这是分层设计的好处。注意 PT1000 和 PT100 的电流配置不同,PT1000 通常用 0.5mA 激励,信号幅度和 PT100 在 1mA 时一样,改工程时别忘记改 EXC_CURRENT。
下面是一组实测的误差对比,可以直观看到插值的价值:
| 查表方式 | 最大量化误差 | 实现成本 |
|---|---|---|
| 最近邻 | 约 0.5℃ | 最低 |
| 线性插值 | 小于 0.01℃ | 一次浮点除法 |
| 分段多项式 | 更高但复杂 | 明显增加代码量 |
对大多数工业应用,线性插值已经足够,再往上属于边际收益递减。
4.3 标定参数存哪:利用片内 Flash 最后一页做掉电保存
换了个运放,换了个恒流源,增益和零漂就不一样了。每次都用代码硬改校准系数不现实,工程里提供的一种做法是把校准系数存放在 Flash 的最后一页。STM32F103C8T6 内置 64KB Flash,主程序一般用不满,最后一页 1KB 正好用来存两个 float 校准值。
校准过程分两步。先在传感器端接一个 100Ω 精密电阻(替代 0℃ 标准电阻),记录此时 ADC 平均值,作为零点偏移的基准。然后换 200Ω 精密电阻,记录第二个点,算出增益修正系数。串口下发一条CAL指令,固件就执行一次校准并把新系数写入 Flash。从此每次上电后,温度换算时先读取这两个系数,对 4.2 节查表函数输出做线性修正,这样能把换器件带来的系统性误差拉回厂家典型范围。
校准公式通常是线性的:t_cal = t_raw * gain_coeff + offset_coeff。两个系数由两点法解一元方程组得出。下面的伪代码实现了这个过程:
// 两点校准:用户先接100Ω电阻发 CAL 0,再换200Ω发 CAL 1 void calibration_execute(void) { if (cal_step == 0) { cal_raw_0 = get_adc_average(); // 100Ω对应的ADC值 cal_temp_0 = 0.0f; // 理论温度0℃ cal_step = 1; } else if (cal_step == 1) { cal_raw_1 = get_adc_average(); // 200Ω对应的ADC值 cal_temp_1 = 261.0f; // 200Ω在分度表里对应的理论温度 // 计算线性修正系数 gain_coeff = (cal_temp_1 - cal_temp_0) / (cal_raw_1 - cal_raw_0); offset_coeff = cal_temp_0 - gain_coeff * cal_raw_0; // 写入Flash最后一页 flash_write_calibration(gain_coeff, offset_coeff); cal_step = 0; } }上电读取 Flash 有一个坑:Flash 擦写的寿命约 1 万次,校准操作不能频繁执行。工程里策略是只有收到 CAL 指令才写 Flash,正常运行时只读不写。如果你要反复标定,建议先接串口调试,调完再固化,避免把 Flash 写穿。还有,200Ω 对应的理论温度不是 261℃ 这个「整数」,而是查表得到的约 261.08℃,写法上可以保留两位小数,影响很小但严谨一点更好。
5. 常见问题和避坑记录:跑起来容易,跑得准是另一回事
5.1 现象:AD 读数像心电图,±5 LSB 跳动
原因:运放供电纹波大,或者 ADC 参考引脚没有去耦电容。很多板子为了省事,直接把 3.3V 系统电源接到 VREF,而系统电源上往往叠加了 DC-DC 的开关纹波。跳动的 LSB 会对换算后的温度造成 ±0.2℃ 的抖动。
解决:VREF 与模拟供电之间加 10uF 钽电容和 0.1uF 陶瓷电容并联,两者尽量靠近芯片引脚放置;如果系统里有 DC-DC,给运放供电串一个几十欧电阻再并大电容,把纹波先滤一道。另外检查 AGND 和 GND 是否单点连接,避免数字地噪声灌入模拟地。如果还是跳,用示波器量 VREF 引脚,纹波峰峰值超过 5mV 就基本是这里的问题。经验值是:先把 VREF 的电容补上,一半的 ADC 抖动问题都能消失。
5.2 现象:常温显示偏高 0.3~0.5℃,装进密闭腔体后更是稳定偏高
原因:恒流源 1mA 在 PT100 上耗散功率约 0.2mW 到 0.4mW,在密闭无风环境下自热效应明显,铂电阻体会比环境温度高。自热温度与传感器封装有关,陶瓷封装散热慢,金属封装好一些。
解决:把激励电流降到 0.5mA,增益翻倍,自热功率降到四分之一。多数 0.1℃ 级测量系统用的是 0.5mA 或更低的 200μA。这是我的血泪经验:第一批样机在开放桌面上测都正常,装进设备外壳后温度集体偏高半度,查了半天才发现是自热。改完这个参数之后,增益、满量程和分度表上限要重新对一遍,否则 250℃ 对应电压会超出 ADC 量程。工程里留了 EXC_CURRENT 宏,就是方便做这个切换的。
5.3 现象:传感器脱线后数值飘到满量程或直接归零,还会误触发热保护
原因:PT100 开路后,放大输入端悬空,运放输出偏向某侧供电轨,ADC 读到 0 或 4095,两种结果都可能被当成超温或低温。比如有的系统会把这个值当作 300℃ 触发超温报警,直接停机。
解决:把超量程判断纳入固件。adc_value 小于阈值下限或大于上限时,直接输出错误码而不是换算温度,并在串口发出T=ERR。判断阈值不要取恰好 0 或 4095,留 2% 余量,因为放大电路正常工作时 ADC 读数不会跑到极端值附近。工程里建议把这两个阈值也做成宏,方便量产整定。如果没有这段逻辑,断线故障的表现是随机温度,现场排查起来非常痛苦。
5.4 现象:靠近变频器或电机时读数短时跳变,几秒内恢复不了
原因:50Hz 工频干扰通过长线耦合进来。PT100 三根线如果和动力线走在同一桥架里,感应电压会高出信号自身几十倍。这个干扰进入运放后,因为运放带宽足够,干扰会一路传到 ADC 输入端。
解决:接线要尽量双绞,S+ 和 S- 绞合在一起,V+ 单独走;信号线上加共模电感效果更好。软件层面,把 DMA 采样次数从 16 加到 64,等效于对 50Hz 周期做多次平均,工频分量能被明显压制,但温度响应会变慢,需要按工况折衷。不要指望只调增益就能解决,这是物理层问题,软件平均只是补救。现场实在改不了布线,也可以在运放后端加一个截止频率 10Hz 的二阶低通,硬件上把干扰挡在 ADC 之前。
5.5 现象:上电前几分钟温度缓慢下降,然后才稳定
原因:运放到稳态需要时间,基准源也有温漂曲线,机器刚从冷库里搬出来或者刚开机时电路自身温度还在爬升。PT100 本身没问题,是电路板的温度没到工作点。
解决:在固件里延长开机预热时间,比如上电后延时 30 秒到 60 秒再输出有效温度。如果是电池供电的低功耗设备,预热时间要缩短,那就在恒流源开启后让 ADC 连续转换但丢弃前 50 个样本,把瞬态稳定时间滤掉。工程里用一个 10 位计数器实现,预热期间串口输出T=--,避免上位机误判。
5.6 这些坑的排查顺序
如果现场读数异常,按下面的顺序排查比逐个试快得多:
| 次序 | 检查项 | 工具 |
|---|---|---|
| 1 | VREF 电压是否稳定 2.5V | 万用表 |
| 2 | ADC 引脚波形有无毛刺 | 示波器 |
| 3 | 运放供电纹波 | 示波器 AC 档 |
| 4 | 三线制线序是否接对 | 万用表通断档 |
| 5 | 传感器阻值是否正常 | 万用表电阻档 |
这个顺序遵循的思路是:先确认参考和供电,再查信号链路,最后才怀疑传感器。大多数「温度不准」的案例,根因都在前三步。
6. 拿电阻箱做验证:没有恒温槽也能把精度调到 0.1℃
6.1 电阻箱逐点验证步骤
恒温槽不是每个开发室都有,但电阻箱很容易借到。验证时把 PT100 拆掉,用电阻箱直接接到三线制端子,从 80Ω 打到 300Ω,每 10Ω 一个点,记录固件输出的温度值,和理论温度对照。理论温度可以用查表法反查,例如 80.31Ω 对应 -50℃,138.51Ω 对应 100℃。打完所有点之后画一条偏差曲线,如果偏差整体平移,调零点;如果角度偏转,调增益。
这里有一张典型的验证记录表样式,现场可以直接套用:
| 设定电阻 | 理论温度 | 固件读数 | 偏差绝对值 |
|---|---|---|---|
| 100.00Ω | 0.0℃ | 0.3℃ | 0.3℃ |
| 119.40Ω | 50.0℃ | 49.8℃ | 0.2℃ |
| 138.51Ω | 100.0℃ | 100.2℃ | 0.2℃ |
| 175.84Ω | 200.0℃ | 199.6℃ | 0.4℃ |
如果偏差整体在 0.5℃ 以内,对一般工业监控够了;如果项目要求 0.1℃,需要继续做两点校准。这个验证方法有个好处:它排除了传感器本身的不确定度,只验证信号链和固件。如果电阻箱打点后误差很小,而现场接入真实 PT100 后又出现偏差,那就是传感器质量问题,跟电路无关。做电阻箱验证时建议用四线制电阻箱,避免夹持电阻不可忽略;如果手头只有两线制电阻箱,把导线阻值先测出来再修正曲线。
6.2 增加一个校准命令:让现场维护更省力
工程里可以再加一个串口命令,当用户端进入校准模式后,先在端子处接 100Ω 标准电阻,发CAL 0,固件记住此刻 ADC 均值;换 200Ω 标准电阻,发CAL 1,固件算出线性修正系数并写入 Flash。这样现场维护人员不用拆机,只带一个标准电阻盒就能完成校准,比寄回厂家省太多时间。
写入 Flash 部分的代码不复杂,关键是增加操作前校验:收到命令后先擦除再写入,写完后回读比对,不一致就返回错误码。擦写期间关闭 ADC 中断,防止写入被采集中断打断,导致半写状态。校准过程中串口要打印进度提示,比如CAL_0_OK、CAL_1_OK,方便现场人员确认操作有效,避免盲写。
6.3 最后的习惯
从那以后我每次拿到新的传感器或新 PCB,都强制自己走一遍电阻箱逐点验证再上现场。这个动作救过我好几次,有一次就是因为反馈电阻一颗贴片用错批次,整板增益偏了 4%,如果没有逐点曲线,估计要等到客户现场才发现。后来我把验证表存成固定格式,随代码一起归档,量产的每一批板子都有据可查。希望这套流程和代码里的坑,能帮你少走几步弯路,希望帮到你。
本文还有配套的精品资源,点击获取