ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32F103+PT100温度采集实战:三线制与FreeModbus移植全解析

STM32F103+PT100温度采集实战:三线制与FreeModbus移植全解析 简介这是一份基于STM32F103C8T6驱动PT100温度传感器的完整工程源码包面向嵌入式开发学习者解决PT100电阻随温度变化后的模拟量采集、ADC配置、数据转换及串口上传计算机等问题。PT100作为高精度铂电阻温度传感器常需配合调理电路与高分辨率ADC读取本工程即围绕此流程给出可运行代码。压缩包共253个文件包含标准外设库的.h/.c源文件、Keil MDK工程文件(.uvprojx)、编译过程中生成的.o/.crf中间文件以及可直接烧录的hex/axf文件和PDF说明文档整体约7.42MB。已有1207人学习下载目录结构完整既适合初次接触STM32 ADC与串口的入门者对照练习也适合开发者快速移植到类似测温项目。项目中重点展示了ADC连续采样、DMA自动搬运数据、USART串口协议配置等核心代码打开工程即可查看引脚分配、时钟配置与数据帧格式能帮助读者系统理解从传感器信号到上位机显示的完整链路。 搞嵌入式这么久温度采集大概是绕不开的活。我手里这个项目就是从“STM32f103PT100.zip”这个压缩包开始的——一个基于STM32F103标准库的PT100温度采集模块带FreeModbus RTU通信最终通过RS232把温度数据送上位机。做之前觉得简单做完才发现PT100前端模拟链路、三线接法、Modbus移植这些环节每一个都能把人拖进坑里。这篇就把整个项目的来龙去脉、硬件设计逻辑、软件实现细节和踩过的坑一次性讲透给正在做类似温度采集项目的朋友一个完整的参考。1. 这个温度采集项目拆开来看其实不复杂1.1 为什么偏偏是PT100加STM32F103先说选型。工业现场测温PT100是绕不开的选择。铂电阻的阻值随温度变化0℃时正好100Ω温度系数稳定、线性度好测温范围能覆盖-200℃到850℃长期稳定性更是甩开热电偶几条街。价格也亲民一个A级PT100探头十来块钱精度能做到±0.15℃家用、工控都够用。主控选STM32F103理由更简单便宜、外设全、资料烂大街。一颗C8T6几块钱12位ADC、多路定时器、USART、CAN、SPI、I2C全都有跑个温度采集加Modbus协议绰绰有余。而且用标准库v3.5的话网上参考代码多到看不完遇到问题随便搜都有答案。CubeMX虽然配置快但FreeModbus v1.6的官方移植例程基本都是标准库风格所以这个项目我还是老老实实回到标准库。1.2 系统的完整组成与设计指标整个系统说白了就是四块PT100传感器探头、信号调理电路恒流源加差分放大、STM32F103最小系统、RS232串口通信。传感器把温度变成电阻电阻通过恒流源变成电压电压放大后送ADC变成数字量STM32里头查表算出温度再按Modbus RTU协议通过串口发给上位机。设计指标也得提前定好不然后期调试没标准测温范围0℃到150℃覆盖大多数工业场景分辨率0.1℃ADC原始分辨率其实能做0.02℃级别软件上按0.1℃上报精度整机误差±0.5℃以内这个指标靠传感器本身和校准流程来保证通信RS2329600波特率8数据位、无校验、1停止位Modbus RTU从站掉电保存Modbus从站地址、校准偏移量存Flash断电不丢2. PT100的前端测量链路三线接法怎么消掉引线电阻2.1 铂电阻的温度特性与测量本质PT100测温的本质就是把温度转换成电阻再把电阻转换成电压。0℃时电阻100Ω温度每升高1℃阻值大约增加0.385Ω。这个关系在0℃以上可以用IEC 60751标准公式描述R(t) R0 * (1 A*t B*t²)其中R0100ΩA3.9083×10⁻³B-5.775×10⁻⁷。t²那个系数非常小0~100℃范围内工程上按0.385Ω/℃做线性估算误差也只有零点几度。但既然MCU算力闲着也是闲着我直接用了二次多项式加查表修正后面软件章节细说。电阻如何变成电压两条路电桥和恒流源。电桥适合配合三线制抵消引线电阻结构对称、好理解恒流源更简单粗暴电阻乘恒定电流就是电压线性度比电桥还好但引线补偿要额外处理。我最终选了恒流源方案原因后面讲。2.2 两线制的缺陷和三线制补偿原理PT100离主机不可能太近信号线一长引线电阻就出来了。铜线电阻率大约是每米0.1Ω级别0.5mm²线两米的线就有0.2Ω算成温度误差就是0.5℃这还没算接插件接触电阻和温度漂移。两线制为什么不行因为激励电流流过引线引线压降和PT100压降混在一起测出来的电压包含了引线电阻。三线制的思路是把引线分开处理一根走激励电流一根走回流电流第三根专门做电压检测。电桥法三线接法最经典——PT100接在一个桥臂另外两根线分别接到桥路的电源端和检测端两个引线电阻被分配到不同桥臂等量变化时电桥输出不受影响。下面是电桥三线制PT100的常用接法示意图R1、R2是桥臂精密电阻VCC --- R1 ------ PT100 --- 引线L1 --- GND | | --- 运放检测端接PT100与L1之间节点 | R2 | GND2.3 恒流源方案激励电流选取与三线制的真实局限性恒流源方案是让1mA恒定电流流过PT1000℃时压降100mV150℃时压降157.7mV变化范围约57.7mV。再用仪表放大器把差分电压放大送ADC。激励电流选1mA是反复权衡过的。电流太小信号压降小信噪比差电流太大PT100自身发热导致测量偏差这个叫自热效应。1mA下PT100自热功率只有0.1mW在静止空气中引起的误差通常小于0.05℃可以接受。熟练的工程师一看就懂但新手往往会踩到“为了信号大把电流调到5mA”的坑。这里必须说实话恒流源配合三线制并不能像电桥那样天然消除引线电阻。激励线L1上的压降I×RL1会老老实实叠加到差分输入里。我的处理办法是工程妥协一是用粗线短引线把单根引线电阻控制在0.1Ω以内折合温度误差约0.26℃二是在软件校准环节把这个固定偏差吃掉。后续要追求高精度直接换四线制测温端单独走两根检测线彻底干净。3. 最小系统与信号调理电路的制作要点3.1 最小系统的正确打开方式STM32F103最小系统网上有一堆现成方案但做温度采集项目有几个细节要注意。电源用AMS1117-3.3稳压输入加10μF钽电容输出加0.1μF陶瓷电容紧贴VDD引脚。每个电源引脚旁边都放104去耦电容这是底线。晶振不要省虽然内部RC也能跑但ADC采样和串口波特率都对时钟精度敏感外部8MHz晶振加两个20pF负载电容是标配。BOOT0接10k下拉电阻到地NRST接上拉电阻加复位电容。PA8这个引脚提前引出来后面要做PWM温度报警用。PCB布局上最容易被忽略的是模拟地和数字地的处理。ADC采样电路是模拟敏感部分降压芯片开关噪声会通过地线耦合进来。我是把模拟地单独走一块在MCU下方单点连接信号调理电路的地线用星形拓扑统一回到这个点实测采样值的跳动明显减少。3.2 信号调理电路增益设定的完整计算放大方案用的是单运放差分放大电路四个电阻组成差动放大器。关键是配平电阻要选0.1%精度否则共模抑制比直接垮掉。增益计算过程如下ADC是12位参考电压3.3V满量程对应数字4096。PT100在0℃到150℃的信号变化是57.7mV。为了让150℃对应2.9V左右留10%余量防止电源波动导致饱和增益 2.9V / 0.0577V ≈ 50倍输入偏置处理0℃时PT100压降是100mV放大50倍就是5V远超ADC量程。所以差分放大器必须先把100mV的共模基线扣掉。做法是将运放负输入端接一个2.048V基准电压对应的比较电平或者用差分放大器直接放大传感器相对于基准的差值。我实际用的是仪表放大器INA128增益电阻选1kΩG50REF引脚接2.5V基准用TL431产生这样0℃时输出2.5V150℃时输出约2.5V57.7mV×50≈5.39V还是超了所以最终把增益调低并配合分压网络让0~150℃映射到0.5到2.9V之间。这里有个经验不要把满量程卡得太死电源纹波、运放失调、基准误差都会把输出顶到饱和宁可让最高温输出留0.3V以上余量。3.3 ADC配置与采样时间的选择STM32F103的ADC在软件层面需要配置的东西不多但有三个参数必须认真对待。一是采样时间ADC内部采样电容大约十几pF信号源内阻稍大就开始拖累精度所以我直接把采样周期拉到239.5个周期这是F103能做到的最大值配合500Ω限流电阻和0.1μF滤波电容稳定性和精度都能保证。二是参考电压。内部VREF不可调外部VREF引脚需要单独加滤波。更稳的做法是用外部基准芯片但对于0.5℃精度级别的设计3.3V电源用LDO输出加LC滤波就够了。三是DMA。不要用轮询方式读ADC转换结果否则主循环一旦被Modbus协议处理占住采样就会卡顿。用定时器触发ADC转换DMA自动搬运结果到内存环形缓冲主循环只读缓冲区的平均值这种架构在实时性上从容得多。4. 软件侧核心从ADC原始值到稳定温度值4.1 采样均值、滑动滤波怎么选ADC原始值拿来直接用会很惨跳动幅度可能有几十个码值对应好几度。硬件滤波只是第一道防线软件滤波必须跟上。我试过三种方案。简单平均连续采64次取平均能压住随机噪声但有一个致命问题——对粗大误差毫无抵抗力比如一次静电干扰导致采样值突然跳到满量程平均值直接报废。中值滤波取N个值排序取中间对尖峰干扰有奇效但计算量大且平滑效果不如平均。最终我用的是复合方案连续采9个值去掉最大值和最小值剩下7个取平均。这个被称为“去极值平均滤波”既抗脉冲干扰又平滑开销也不大非常适合F103的算力。滤波时间窗是64次采样每次采样周期大约70μs合计不到5ms对温度变化来说完全够用。温度本来就是个慢变量滤波狠一点反而舒服。4.2 温度换算查表还是公式ADC值经过定标换算成PT100电阻值再根据IEC 60751公式反算温度。0℃以上我直接用二次多项式求根R R0 * (1 A*t B*t²)移项就是一元二次方程用求根公式反解tt [-A sqrt(A² - 4*B*(1 - R/R0))] / (2*B)A3.9083×10⁻³B-5.775×10⁻⁷。这套公式在0到150℃范围内误差小于0.01℃精度足够。代码实现如下float PT100_GetTempByResistance(float resistance) { float a -5.775e-7f; float b 3.9083e-3f; float c 1.0f - resistance / 100.0f; float delta b * b - 4.0f * a * c; if (delta 0) return -999.9f; // 传感器开路判断 return (-b sqrt(delta)) / (2.0f * a); }等式的匹配、交点、接口参数这里都是在原项目里调试过的。如果要覆盖0℃以下公式里还要加C系数项或者改用分段查表。我的做法是准备一张-50℃到200℃、每5℃一档的电阻对照表先用二分法定位区间再用线性插值。查表加插值的精度优于0.05℃而且计算量比多项式求根小在低主频MCU上更友好。两种方法都保留编译宏切换。4.3 校准流程和参数保存传感器和电路都有误差单个PT100的初始电阻和电路增益不可能完美所以校准是必须的。我做了两点校准冰水混合物提供0℃基准恒温槽或精密温度计提供100℃附近基准。参数化为两个值零位偏移量和增益系数存到Flash里。Flash擦写代码注意两个坑一是写之前要解锁Flash初始化调一下FLASH_Unlock二是擦除一个扇区前确认没有正在执行的代码在同一个扇区里否则程序直接跑飞。我在工程上把配置参数放在最后一个扇区因为代码量不大基本碰不到那个扇区。掉电保存是另一个需求。F103没有真正的EEPROM替代方案就是内部Flash模拟EEPROM或者外挂I2C EEPROM。我的做法是检测VDD电压跌落到3.0V以下时触发外部中断把当前校准参数写入Flash。如果只是保存Modbus站地址这种低频参数直接在收到写请求后延迟500ms写入比掉电检测简单可靠得多。5. FreeModbus移植把温度数据送上RS232/RS485总线5.1 移植前的协议理解Modbus RTU帧格式Modbus RTU帧格式异常简单地址码1字节、功能码1字节、数据N字节、CRC16校验2字节。主机发起请求从机应答。帧与帧之间必须间隔至少3.5个字符时间这个间隔是判断一帧是否结束的唯一依据9600波特率下就是大约4ms。协议本身不复杂但自己从头写容易踩坑的地方很多——CRC算错、帧超时判断不准、广播地址处理不对。FreeModbus v1.6把协议栈都封装好了我们只需要做平台移植串口读写、定时器、以及把从站数据挂到寄存器读写回调上。5.2 FreeModbus v1.6的port层到底要改哪些文件FreeModbus下载后核心协议代码在mb/目录我们基本不用动。需要动的是port/目录下的移植层port.h定义ENTER_CRITICAL、EXIT_CRITICAL为关/开中断宏portserial.c实现串口初始化、使能收发、发送/接收字节中断回调porttimer.c实现3.5字符超时定时器我用TIM2配置成1ms中断vMBPortTimerEnable启动vMBPortTimerDisable停止中断回调用MB_TIMER_EXPIRED()上报portevent.c事件回调通知协议栈串口中断的处理逻辑要顺一遍接收一个字节把数据压入协议栈的接收缓冲区调用MB_UART_RX_ISR()通知定时器溢出时调用MB_TIMER_EXPIRED()。FreeModbus内部会根据这些事件推进状态机。void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { uint8_t recv USART_ReceiveData(USART1); xMBPortSerialRxISR(recv); } if (USART_GetITStatus(USART1, USART_IT_TXE) ! RESET) { USART_ClearITPendingBit(USART1, USART_IT_TXE); xMBPortSerialTxISR(); } }移植完成后初始化流程是eMBInit(MB_RTU, ucMBAddress, 0, 9600, MB_PAR_NONE)设置从站地址和波特率然后eMBEnable()主循环里不断调用eMBPoll()。我这里主循环加了调试输出和LED心跳也没影响协议实时性。5.3 寄存器映射与应用层对接FreeModbus里从站数据靠维护一组寄存器数组协议栈读写数组时会回调eMBRegHoldingCB、eMBRegInputCB这些函数。我的映射表很简单保持寄存器40001站地址默认1可写保持寄存器40002校准偏移量可写保持寄存器40003校准增益系数可写输入寄存器30001当前温度单位0.1℃值为973表示97.3℃输入寄存器30002ADC原始值调试用输入寄存器30003传感器状态0正常1开路2短路温度值用0.1℃做单位是Modbus下常见的做法避免浮点数传输上位机读出来除以10就是实际温度。协议栈不直接处理float浮点只在MCU内部用。RS232驱动用的是MAX3232芯片短距离调试完全够用。真要上工业现场把MAX3232换成MAX485就变成RS485总线可以挂32个从站软件层完全不用改。这个预留设计从一开始就考虑到了。6. 扩展功能与工程化收尾6.1 PWM输出PA8引脚做温度报警控制温度采集不能光会读还得会用。我用PA8复用为TIM1_CH1输出PWM做加热器控制信号。温度低于设定值时PWM占空比加大高于时减小。这是最简单的温控逻辑不涉及PID也能跑。这里有个真实的坑PWM占空比设置到100时输出竟然不是满幅。查了半天发现TIM1的PWM模式1下CNT CCR时输出有效电平当CCR ARR时CNT达到ARR那个瞬间会短暂输出无效电平示波器上看就是差那么一丁点。解决方法是CCR直接设ARR 1或者单独用TIM_CtrlPWMOutputs控制。这种细节不实测真的发现不了。6.2 掉电保存小参数学会用Flash站地址和校准参数是低频修改的数据不需要频繁擦写Flash。我维护了一个简单的参数结构体带CRC校验放在最后一个扇区。每次上电读出来校验CRC不对就用默认值。写入时注意STM32的Flash必须整扇区擦除F103一个扇区是1KB或2KB不能像EEPROM那样按字节改。所以每次写参数先把新值整理到RAM里的结构体擦除扇区再把整个结构体写进去。这个流程要关中断执行防止写入过程中被打断。经验是Flash写操作速度不快几百微秒的时间窗口必须保护。如果哪天需要频繁记录温度历史数据那还是老老实实外挂EEPROM或SPI Flash内部Flash模拟EEPROM的方案适合低频参数不适合曲线记录。6.3 工作状态指示与工程化习惯系统板子上加了一颗WS2812B做状态指示最初想过后来放弃了——WS2812B是单总线时序驱动对中断延时极敏感Modbus通信一忙灯色就乱闪得不偿失。最后用两颗普通LED一颗电源指示常亮一颗心跳灯每500ms翻转一次。系统卡没卡死一眼就能看出来。工程化上还有个小习惯每次编译完看map文件检查Flash和RAM占用率。F103C8T6有64KB Flash和20KB RAM我代码加协议栈总共用了大约18KB Flash、6KB RAM余量充足。如果RAM快满了优先检查是不是数组开太大尤其是Modbus缓冲区默认128字节足够别盲目开大。7. 实测结果与几个值得复盘的坑7.1 用冰水混合物和恒温槽做的验证硬件焊完软件烧进去第一件事不是接PT100而是用精密电阻箱模拟传感器输入。100Ω对应0℃138.5Ω对应100℃157.3Ω对应150℃。这个阶段能快速验证整条链路。电阻箱验证通过后上真传感器。冰水混合物测0℃多次采样读数在-0.1℃到0.2℃之间浮动中位数0.1℃表现可以。100℃沸水不太好测海拔影响沸点我改用恒温槽设定100℃读数稳定在99.7℃到100.3℃区间在设计的±0.5℃指标内。连续通电48小时温度读数漂移不超过0.1℃自热效应控制得很理想。Modbus通信也做了完整验证PC上用Modbus Poll工具读取30001寄存器设定站地址写入校准参数重启后参数还在Flash读写稳定。7.2 调试过程中踩过的几个坑第一个坑是ADC采样值整体偏小。排查了半天发现是差分运放REF脚接的2.5V基准用的是普通TL431的分压电路没有加缓冲器运放REF引脚拉电流导致基准跌落。改成运放缓冲输出后问题解决。这种问题示波器一测就看出来了但新手容易陷入“程序逻辑有bug”的误区。第二个坑是传感器开路时运放输出被拉向电源轨ADC读满量程换算出来的温度飙到几百上千度。最开始没做异常判断上位机显示一个离谱的数把现场人员吓一跳。后来在代码里判断电阻值超过一定范围就标记开路输出0xFFFF表示故障Modbus寄存器返回状态码3。第三个坑最有迷惑性ADC值很稳定但换算后的温度跟着电源电压轻微波动。查到最后是激励电流源用的是简易三极管恒流源对电源变化抑制不够。换成了LM334恒流源芯片或者用基准电压源加运放构成的恒流源问题彻底消失。做模拟电路恒流源的电源抑制比怎么强调都不过分。整个项目从画板到调通前后用了大概两周其中硬件调试占了大半时间。软件部分FreeModbus一跑通剩下的就是写业务逻辑。最后再分享一个心得这种带模拟前端的产品调不通的时候先用电阻箱代替传感器验证软件再用示波器量准每一级信号一步一步缩小排查范围不要一上来就怀疑协议和算法。这次实测下来这套方法帮我省的时间足够再写一个驱动了。本文还有配套的精品资源点击获取
返回列表