news 2026/9/7 7:43:08

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于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试过,抗干扰和一致性都不理想,后来换成致远电子的ZAM6228,8通道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 SCL
IIC_SDAPB7ZAM6228 SDA、OLED SDA
VCC_3.3V3.3VOLED VCC
VCC_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从设备读取流程为例,第一步是向从设备写入控制命令,例如设置连续转换模式。发送流程是:

  1. 发送起始条件;
  2. 发送设备地址加写位(7位地址左移一位,最低位为0);
  3. 等待ACK;
  4. 发送控制寄存器地址;
  5. 等待ACK;
  6. 发送启动转换命令;
  7. 等待ACK;
  8. 发送停止条件。

转换需要时间,比如50ms到100ms,具体以手册为准。等待结束后再发起读操作:

  1. 发送起始条件;
  2. 发送设备地址加读位;
  3. 等待ACK;
  4. 连续读取16位温度数据,从高位到低位;
  5. 每读完一个字节,主机发送ACK,让从机继续输出;
  6. 读完最后一个字节,主机发送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,再慢慢加显示、加滤波、加异常处理。这一步做扎实了,后面基本不会有大问题。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 7:39:35

日本GMP中文版PDF怎么读?从条款翻译到体系落地的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 7:37:30

HomeAssistant 接入 DeepSeek 与 ChatGPT 大模型实战指南

HomeAssistant 接大模型&#xff0c;不是把 ChatGPT 塞进 HA 那么玄乎&#xff0c;本质就是让智能家居中枢多一个会自然语言对话、能理解传感器状态、还能在自动化里做判断的大脑。DeepSeek 因为提供 OpenAI 兼容接口、国内访问体验好、API 价格相对可控&#xff0c;是当前比较…

作者头像 李华
网站建设 2026/9/7 7:37:22

24LE1无线SoC开发实战:从工程文件到射频调试全流程解析

简介&#xff1a;面向嵌入式开发者的24LE1单片机与LIS3DH/LIS3DSH三轴加速度计INT1中断唤醒应用资源包&#xff0c;内容涵盖SPI通信配置、外部中断触发、低功耗唤醒及数据读取等关键环节&#xff0c;适合正在学习8051内核MCU与传感器交互、并希望实现低功耗实时系统的开发人员参…

作者头像 李华
网站建设 2026/9/7 7:37:03

OpenCV 4.9.0 实战指南:从轮廓提取到相机标定与DNN模型部署

简介&#xff1a;OpenCV 4.9.0 是面向图像处理与计算机视觉开发者的开源库版本&#xff0c;本包特别包含 contrib 贡献模块&#xff0c;适合在 Windows 下用 C 或 Python 进行特征提取、物体识别、目标跟踪、深度学习推理等工作的中高级开发者&#xff0c;可直接集成到 VS 等项…

作者头像 李华