基于STM32与ZAM6228的8通道PT100温度采集系统设计

📅 发布时间:2026/9/7 7:43:09
基于STM32与ZAM6228的8通道PT100温度采集系统设计
简介致远电子ZAM6228八通道PT100温度测量模块的单片机实战程序面向嵌入式学习者与工业测温项目开发者解决缺少官方协议文档时如何用STM32单片机的普通IO口模拟IIC时序完成八路PT100电阻温度数据读取并驱动OLED显示屏实时呈现温度参数。整个工程结构清晰可用于多点温度采集、温控系统原型验证也可作为STM32裸机编程、外设驱动及通信协议模拟的综合学习案例。包内共185个文件压缩后约4.66MB主要包含C语言源文件与头文件以及Keil工程配置文件、编译链接生成的中间文件便于直接打开工程查看代码结构、理解编译流程。此包已被553人学习下载受到一定关注。除核心的PT100采集与OLED显示驱动外代码中还涉及STM32标准外设库的时钟、定时器、ADC、串口等模块配置从内容预览可见stm32f10x_rcc.c、stm32f10x_tim.c、stm32f10x_i2c.c等文件能够帮助读者掌握外设初始化方法、IO模拟IIC的时序实现以及整个Keil工程的组织方式方便实际项目移植与二次开发。 前段时间因为项目需要要做一套8通道的多点温度采集温度范围覆盖室温到一百多度现场传感器线缆还不算短。一开始用DS18B20试过抗干扰和一致性都不理想后来换成致远电子的ZAM62288通道PT100温度测量模块配一块STM32F103C8T6最小系统板用IO模拟IIC总线把数据读回来最后在0.96寸OLED屏上把8路温度都显示出来。整套东西做完运行了几个月都很稳。这篇把实战过程整理一遍从硬件接线、软件IIC模拟、ZAM6228读写、OLED显示到调试中踩过的坑都写清楚给正在做温度采集、多点监测或者想学软件IIC的朋友一个可复现的参考。1. 项目整体设计与方案选型1.1 8通道PT100测温的场景定位多路温度采集最典型的应用就是电池充放电测试柜、机房/配电柜温度监测、恒温箱多点验证这类需要同时看多个点位温度的场景。ZAM6228这类模块的价值在于它把所有PT100的模拟前端都做完了包括恒流源激励、信号放大、ADC采集、线性化校准对外只需要通过IIC总线读取温度结果。相比之下如果你用裸的PT100传感器自己做采集需要搭电桥、用高精度运放、再配一个24位ADC硬件成本高不说PCB设计、调零、校准一套流程下来非常耗时。所以项目里我直接选了成品模块。选PT100而不选DS18B20的原因也很直接。PT100的测温范围更宽长期稳定性好适合工业环境DS18B20虽然便宜、单总线方便但在现场干扰较强的场合容易出现通信错误而且传感器批次间的一致性差异比较明显。1.2 为什么选STM32加软件模拟IIC主控用STM32没什么悬念资料多、开发工具成熟、引脚便宜。真正值得说清楚的是为什么不用硬件IIC而要用IO口去模拟IIC时序。很多老工程师都知道标准外设库版本的硬件IIC在处理NACK、总线忙、时钟拉伸这些情况时状态机的判断比较绕调试起来很头疼。尤其是当系统里有中断频繁打断时硬件IIC跑着跑着就可能卡在某个状态上没有超时机制的话就只能复位总线。而软件模拟IIC本质上是GPIO电平翻转加延时逻辑完全由自己控制出问题可以用示波器一根一根看波形定位非常快。对于ZAM6228这种IIC速率不高的从设备软件模拟完全够用。而且IO选择特别灵活不用绑定芯片固定的IIC引脚画PCB或者飞线时自由度很高。这个项目里我把ZAM6228和OLED都挂到同一组模拟IIC总线上两个设备的从机地址不同所以可以共用SCL和SDA两根线节省引脚占用。2. 硬件连接与准备2.1 ZAM6228模块与PT100三线制接法PT100传感器常见有2线、3线、4线接法。这个项目用的是3线制模块上的接线端子一般标有RTD、RTD-、TC具体对应关系要以模块丝印和手册为准。3线制的核心作用是为了消除导线电阻。PT100在0℃时标称电阻是100欧姆导线本身如果有个1欧姆电阻带来的温度误差大约2.5℃这在长线传输时完全不可接受。3线制接法通过一根额外的补偿线把线路电阻的影响在测量回路里抵消掉。连接时要把PT100的两根同色线分别接到激励端和补偿端另一根异色线接到对应通道的另一端。如果手头只有2线制PT100也能测但温度值会偏高一些现场距离短才能接受。2.2 STM32与ZAM6228、OLED的引脚分配我用的主控是STM32F103C8T6引脚分配如下功能STM32引脚连接目标IIC_SCLPB6ZAM6228 SCL、OLED SCLIIC_SDAPB7ZAM6228 SDA、OLED SDAVCC_3.3V3.3VOLED VCCVCC_5V5VZAM6228供电以手册为准GNDGNDZAM6228、OLED共地这里有一个特别需要注意的地方就是电平匹配。如果ZAM6228是5V供电它的IIC引脚高电平可能达到5V而STM32的IO口是3.3V电平直接连接有风险。我实际处理时是把STM32的PB6/PB7配置为开漏输出外部上拉到3.3V这样即使从设备是高电平也不会灌入超过3.3V的电压。如果模块的IIC电平本来就和3.3V兼容那就不需要额外处理。2.3 上拉电阻与电源分配细节IIC总线必须在SCL和SDA上接上拉电阻。很多模块和OLED板载已经带了上拉如果模块没有就自己在两根线上各接一个4.7k欧姆电阻到3.3V。当总线上同时挂着两个设备而两个设备都有上拉电阻时等效上拉电阻会并联变小。比如两个都是4.7k并联之后约2.35k这在实际工程中问题不大反而让信号边沿更陡。如果总线长度超过20cm建议上拉电阻换成2.2k防止电容过大导致上升沿太缓。电源方面ZAM6228和STM32必须共地否则IIC信号没有统一参考点。PT100的传感器线尽量远离继电器、电机这类大功率干扰源否则模拟前端依然会有一定程度的噪声耦合进来。3. IO模拟IIC的实现要点3.1 软件IIC协议基础IIC总线就两根线SCL时钟和SDA数据。空闲状态下两根线都是高电平。一次完整传输由起始条件、数据字节、应答位和停止条件组成。起始条件是SCL为高电平时SDA从高拉低。停止条件是SCL为高电平时SDA从低拉高。每个数据字节8位高位在前SCL低电平期间SDA必须保持稳定SCL高电平期间数据被采样。第9个时钟周期是应答位主机释放SDA从机拉低表示ACK应答从机不拉低就表示NACK。这些时序对于软件模拟来说就是控制GPIO高低电平再加延时。延时的长短决定IIC速率标准模式SCL约100kHz快速模式约400kHz。我第一次用模拟IIC时最怕时序不对后来用逻辑分析仪抓波形才发现只要每个电平翻转之间延时2到5微秒基本都能稳定工作。3.2 IO模拟IIC核心代码框架我用的标准外设库GPIO操作直接操作寄存器加快速度。先定义引脚操作宏#define IIC_SCL_1 GPIOB-BSRR GPIO_Pin_6 #define IIC_SCL_0 GPIOB-BCRR GPIO_Pin_6 #define IIC_SDA_1 GPIOB-BSRR GPIO_Pin_7 #define IIC_SDA_0 GPIOB-BCRR GPIO_Pin_7 #define IIC_SDA_READ GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) void IIC_Delay(void) { volatile uint32_t i 10; while (i--); }起始和停止void IIC_Start(void) { IIC_SDA_1; IIC_SCL_1; IIC_Delay(); IIC_SDA_0; IIC_Delay(); IIC_SCL_0; IIC_Delay(); } void IIC_Stop(void) { IIC_SDA_0; IIC_SCL_1; IIC_Delay(); IIC_SDA_1; IIC_Delay(); }发送一个字节void IIC_SendByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { if (dat 0x80) IIC_SDA_1; else IIC_SDA_0; dat 1; IIC_Delay(); IIC_SCL_1; IIC_Delay(); IIC_SCL_0; IIC_Delay(); } }等待应答unsigned char IIC_WaitAck(void) { unsigned char ack; IIC_SDA_1; IIC_Delay(); IIC_SCL_1; IIC_Delay(); ack IIC_SDA_READ; IIC_SCL_0; IIC_Delay(); return ack; }读一个字节稍微复杂一点轮询8个位SDA设置为输入模式每个SCL高电平期间读SDA电平unsigned char IIC_ReadByte(void) { unsigned char i, dat 0; for (i 0; i 8; i) { dat 1; IIC_SCL_1; IIC_Delay(); if (IIC_SDA_READ) dat | 0x01; IIC_SCL_0; IIC_Delay(); } return dat; }这里面有个容易忽视的细节就是读字节之前MCU的SDA引脚一定要配置为输入模式或者至少释放总线。如果SDA一直是推挽输出SDA电平就会被MCU强行拉死从机根本无法控制SDA。3.3 模拟时序的调整技巧软件模拟IIC最怕的是不按套路出牌的从设备。有些模块会做时钟拉伸也就是从机处理数据时会把SCL拉低让主机等待。用GPIO模拟时如果固定延时不会主动等待从机释放SCL就有可能丢数据。处理方法是把SCL也做成输入模式在置高SCL之后轮询判断SCL是否真的为高如果从机在拉伸时钟它会继续保持SCL低主机就一直等待。ZAM6228实际用下来并没有明显时钟拉伸但我在模拟框架里保留了这种检测逻辑换其他IIC传感器时也能直接用。还有一个技巧是千万不要在IIC的SCL/SDA翻转期间关闭全局中断太久。如果系统用到定时器、串口中断延时会导致SDA上的数据建立时间不足。一般情况下模拟IIC的临界区很短影响不大但要避免在某个耗时的中断服务函数里反转SDA。4. 读取ZAM6228温度数据的完整流程4.1 从设备手册确认寄存器与地址ZAM6228不是裸的ADC它的内部已经有MCU在做采集、线性化和温度换算外部MCU只需要通过IIC读写寄存器就能拿到温度值。所以动手写代码之前最重要的一步是去致远电子官网下载ZAM6228的用户手册确认下面几个关键信息7位从机地址可能是固定地址也可能有地址拨码。控制寄存器地址用于启动连续转换还是单次转换。各通道温度数据寄存器地址以及数据长度是16位还是其他格式。温度值的单位可能是0.1℃为步进也可能是0.01℃步进。设备复位或者软件校准的相关命令。我初拿到模块时一度以为设备地址是常见的0x48结果总线上一直没有ACK后来查手册才发现地址需要根据模块上的地址引脚电平来确认。这种低级错误浪费了不少时间所以建议上来直接先查手册不要凭经验猜。4.2 读取8个通道温度数据的基本流程以常见的IIC从设备读取流程为例第一步是向从设备写入控制命令例如设置连续转换模式。发送流程是发送起始条件发送设备地址加写位7位地址左移一位最低位为0等待ACK发送控制寄存器地址等待ACK发送启动转换命令等待ACK发送停止条件。转换需要时间比如50ms到100ms具体以手册为准。等待结束后再发起读操作发送起始条件发送设备地址加读位等待ACK连续读取16位温度数据从高位到低位每读完一个字节主机发送ACK让从机继续输出读完最后一个字节主机发送NACK然后发送停止条件。如果模块支持连续读还可以一次把8个通道的数据全部读回存在一个数组里unsigned char temp_raw[16]; unsigned char i; for (i 0; i 16; i) { temp_raw[i] IIC_ReadByte(); if (i 15) IIC_MasterAck(); else IIC_MasterNAck(); } IIC_Stop();把原始字节拼接成温度值最常见的格式是int16_t raw (int16_t)((temp_raw[i * 2] 8) | temp_raw[i * 2 1]); float temperature raw * 0.1f; // 具体系数以手册为准如果模块直接输出的是电阻值还需要再对照PT100分度表进行查表换算但ZAM6228这类模块通常已经把温度算好了省去不少麻烦。4.3 数据滤波与异常处理温度数据本身比较平稳但在工业现场开关电源、继电器动作可能会造成瞬时干扰。我在程序里加了一阶低通滤波filtered_value filtered_value 0.2f * (new_sample - filtered_value);系数0.2可以调整系数越小越平滑但跟随真实变化的速度也越慢。如果现场温度变化本来就慢滤波系数设到0.1甚至0.05都没问题。异常处理一定不能省。PT100传感器断线时模块读到的温度值会明显超出正常范围甚至出现0x7FFF之类的标志值。我的程序里做了两个判断一是如果连续多次读到的原始值相同且处于非正常范围就判定该通道故障OLED上显示SENSOR ERR二是如果温度低于-50℃或高于300℃也认为是异常。5. OLED显示温度参数5.1 OLED驱动核心要点0.96寸OLED屏常用主控是SSD1306这个项目里用软件IIC同样可以驱动。OLED的IIC从机地址一般是0x3C也有0x3D的要看背面的电阻配置。OLED刚上电不会自动显示内容必须由MCU发送初始化序列。网上有很多成熟的SSD1306初始化代码基本上就是关闭显示、设置显示时钟分频、设置复用比、设置显示偏移、开启内部充电泵、设置对比度、清除显示、开启显示这一串命令。初始化之后就是往显存里写数据。SSD1306内部有1KB显存一页一页地写。我封装了三个最基本的函数OLED_Write_Cmd(cmd)发送命令字节OLED_Write_Data(data)发送数据字节OLED_SetPos(x, y)设置显示坐标。发送命令和数据的区别在于控制字节命令用0x00数据用0x40。这个控制字节在IIC传输里非常关键写错了屏幕要么完全不显示要么显示乱码。5.2 8通道温度显示布局设计128x64分辨率的OLED如果使用8x16的字符一行16个像素高64像素一共可以显示8行。这个分辨率用来显示8个通道刚刚好一行一个通道不需要翻页。我的布局是每行显示CH1 25.6C。如果使用8x16字体一行大约能显示16个英文字符CH1:25.6C一共10个字符非常宽裕。也可以把字体缩小到6x8在一行里放两个通道但实际显示效果会比较拥挤现场看起来不够直观所以我还是选择了一行一个通道。如果需要在屏幕上显示汉字比如“温度一”、“温度二”就麻烦一些。SSD1306默认是不带汉字字库的必须先用取模软件把汉字转成位图数据再写一个显示汉字的函数。取模软件的选择比较关键参数设置不对显示出来的汉字会左右颠倒或者上下翻转我当时在字模的行列扫描方式上折腾了好一会儿。5.3 刷新策略与主循环组织主循环我建议这样组织定时器每200ms产生一个标志位主循环检测到标志后执行一次温度读取和OLED刷新不要用delay去死等。OLED的刷新如果每次都全屏清屏再重绘会有明显的闪烁感。我实际用的时候只有温度数据变化差异较大时才对相应区域重新绘制。同时要注意温度从25.6变成9.8宽度变了会在屏幕上留下原来的字符残影。解决办法是在显示新字符串之前先用空格填充原来的显示区域。char display_buf[16]; sprintf(display_buf, CH%d:%5.1fC, channel, temperature); OLED_ShowString(0, channel * 8, display_buf);格式化字符串里用%5.1f保证固定宽度比如“25.6”和“9.8”在同样宽度下后面那个数值前面会有空格这样就不会有残影。6. 调试实录与避坑指南6.1 仿真器连接不上芯片调试过程中经常遇到“no stm32 target found”的报错尤其在代码里把SWD相关引脚复用到普通IO之后。我这个项目里PB6/PB7默认是普通IO不涉及SWD但如果同时调试用到PB3/PB4就可能把SWD占掉。解决办法是按住开发板复位键不放点击连接仿真器保持复位状态下快速擦除Flash。如果这个方法不行就用串口ISP把Flash清空然后再用ST-Link连接。还有一种情况是供电不足ST-Link的3.3V输出能力有限如果是ST-Link给整个板子供电芯片启动瞬间电流过大就可能连不上这种情况单独给板子供5V电源会更稳定。6.2 IIC总线上没有ACK这是最常遇到的问题现象是第一个字节发出去之后就收到NACK。排查优先级如下用逻辑分析仪或示波器抓SCL/SDA波形确认起始条件正常。确认发送的从机地址和设备实际地址一致。检查SCL和SDA有没有接反这是新手最容易犯的错误。检查模块供电是否正常模块没上电的话自然不会有ACK。检查上拉电阻是否缺失。如果总线上同时挂着两个设备还要注意地址冲突。OLED默认0x3C如果ZAM6228的地址也是0x3C就必须把其中一个改到其他地址或者拆成两路IIC。6.3 温度读数乱跳读数乱跳的原因很多我这里分几种情况说。如果读数是25、125、25这样跳这往往不是干扰问题而是字节拼接错误高位和低位顺序反了或者数据对齐出错。先打印原始值出来看如果原始值本身稳定问题就在拼接。如果原始值就在小范围波动比如25.3、25.8、26.0那就是模拟前端受干扰。检查PT100三线制接线是否牢固传感器线是否和电源线走在一起。现场实在没法避开可以加强滤波系数。还有一种情况是读了多个通道但显示顺序错乱。这个问题通常是因为没有读取完所有通道就发停止条件导致从机内部的数据指针没有按预期前进。建议把整个读数组的流程按手册时序完整走下来不要省中间步骤。6.4 OLED上电不亮很多第一次接触OLED的开发者会认为屏幕连上电源就应该亮实际上SSD1306必须收到初始化命令后才会醒过来。如果初始化全做了还黑屏重点检查两点第一是IIC地址是0x3C还是0x3D第二是有没有RES引脚没有拉高。如果OLED模块引出了RES引脚悬空可能导致内部复位状态无法退出需要把RES引脚接高电平或者由MCU在初始化时给一个低脉冲复位。有些模块还有DC引脚如果DC引脚悬空也会影响显示需要按照模块说明接好。写在最后这套方案跑下来我最直观的感受是ZAM6228这类模块把最难的模拟前端问题都解决了单片机工程师只需要把时间和精力花在数字通信和显示逻辑上项目开发周期可以缩短很多。软件模拟IIC虽然看起来比硬件IIC低级但在调试阶段真的很救命。硬件IIC卡在总线上的时候你想干预都难软件IIC则可以用GPIO电平控制的方式一步一坑地对照时序图排查逻辑分析仪抓出来的波形也更容易看懂。最后再分享一个经验拿到任何IIC设备第一天不要急着写业务代码先用最简单的裸机程序只做一件事枚举设备读寄存器。先让CPU成功读到一次ACK再慢慢加显示、加滤波、加异常处理。这一步做扎实了后面基本不会有大问题。本文还有配套的精品资源点击获取