接到这个测试台项目的时候,我在地铁上把需求翻了一遍:28台测试仪表,全部走RS485串到同一台西门子S7-1200 PLC里,要求程序连续轮询读写,交付物包括完整的PLC程序和触摸屏程序。第一反应是觉得工作量不大,轮询嘛,谁没写过几十个从站的轮询?可真到了现场联调才发现,仪表通讯链路上一堆细节能把人磨到怀疑人生——哪些仪表响应慢、哪个寄存器地址看着对但数据类型不对、哪台从站一旦掉线就让后续全部排队超时。这篇文章把我这次1200 PLC + 28个RS485从站轮询读写的整体设计思路、程序结构、触摸屏组态和现场踩坑记录都整理出来,给正在做多仪表采集、PLC串口轮询项目的你一个可以参考的完整方案。
1. 需求拆解与方案选型
1.1 先摸清这28台表是什么“脾气”
拿到项目第一件事不是写程序,而是把28台仪表的通讯协议说明书翻一遍。我这边的配置是:12台温度巡检仪、8台压力变送器、8台流量计,分布在一条约80米长的测试车间里。每台表都自带RS485接口,通讯协议绝大多数是Modbus RTU,但具体寄存器地址、数据格式、字节顺序千差万别,有的支持写操作,有的只读。这一步如果偷懒,后面联调就是灾难。
你需要从仪表手册里确认的信息包括:从站地址范围(默认值)、通讯波特率上限、数据位/校验位/停止位、读取数值对应的寄存器起始地址和数据类型(16位无符号、32位浮点、BCD码等)、是否支持写寄存器及支持什么功能码。把每台表的信息整理成一张“仪表通讯参数确认表”,这比任何编程都快。
1.2 为什么锁定了S7-1200 + RS485轮询
刚看到28台设备的规模,有人可能会想:直接用分布式IO?或者加一个串口采集网关?但实际判断下来,分布式IO成本高,而且仪表已经具备RS485通讯能力,没必要再走模拟量采集。串口网关多一层设备,还得多学一套网关组态软件,现场维护更麻烦。S7-1200配一个串口通讯模块,本身就支持Modbus RTU主站,轮询逻辑可以完全写在PLC里,触摸屏直接读PLC的DB区,链路最短、可控性最强。
S7-1200的以太网接口留给触摸屏和程序下载,RS485串口专门做仪表轮询,两个环节互不干扰。28台从站的规模对于一条485总线来说属于中等偏上,只要物理层做扎实、程序里的超时恢复逻辑写干净,完全跑得起来。如果你对轮询周期有更苛刻的要求,比如要在1秒内完成28台的全部刷新,那可以考虑拆成两路RS485通道,但这个项目需求是显示刷新周期3秒左右,单通道足够,我就没有再增加硬件成本。
1.3 硬件清单和项目范围整理
我的硬件配置是:CPU 1214C DC/DC/DC一台、CM1241 RS485通讯模块一块、KTP700 Basic PN触摸屏一块、普通工业交换机一台、若干DP/RS485专用屏蔽线缆。这里特别说明一下,S7-1200本体不带RS485口,必须依靠CM1241 RS485或者CB1241背板通讯板,这个是根据CPU型号选型的。CM1241支持Modbus RTU主站/从站,也支持自由口协议,可以覆盖绝大多数仪表的通讯需求。
TIA Portal里建项目时,要注意PLC固件版本和CM1241模块的固件版本匹配,Modbus通讯指令库在不同TIA版本里名称有差异,比如MB_COMM_LOAD、MB_MASTER这几个块的调用方式虽然基本一致,但参数定义细节最好以你当前版本的帮助文档为准。项目范围其实就三块:485通讯硬件链路、PLC轮询程序和触摸屏画面,我会按这个顺序展开。
2. 485通讯链路:物理层做扎实
2.1 手拉手布线、接地和终端电阻
RS485最忌讳的拓扑就是星形分支。28台仪表分布在车间各处,现场施工如果图省事,把每台表的线像树枝一样接到某个汇线端子排上,总线反射会非常严重,表现出来就是通讯时好时坏、随机数据帧错误。正确做法是手拉手菊花链,从PLC的CM1241出来,第一台表进、第二台表出,一路串到最后一台,中间尽量少做分支。
线材我建议用带屏蔽层的双绞线,A和B两根线一定不能接反。很多仪表端子上标注的是D+和D-,对应到PLC就是A(D+)和B(D-)?这里恰恰是坑最多的地方:不同厂家的标注习惯不一样,有的标A+和B-,有的标Data+和Data-,你最好用万用表通断挡先确认同一条总线上所有设备的同名端子是连通的,再上电测试。
终端电阻原则是总线两端各接一个120Ω电阻,对应到28台表的场景,就是PLC侧和最后一台表侧。如果PLC到第一台表的距离很短,可以把PLC侧当一端,最后一台表当另一端。屏蔽层接地点选在PLC侧单端接地,避免两端接地产生地环路电流。仪表分散较远的话,建议把各仪表地线通过等电位连接起来,减少共模电压差,这个细节对长距离、多节点的485总线影响很大。
2.2 通讯参数和从站地址规划
我见过太多项目死在通讯参数不统一上。28台仪表,每台都可能被出厂设置为9600、8、N、1或19200、8、E、1,如果PLC侧按统一参数配置,总有几台表不响应。我的做法是:先逐台确认每台表的实际参数,然后趁仪表离线配置阶段把它们统一改成同一套参数。统一到多少?要看最慢的那台表支持到什么档位,这28台表普遍支持9600和19200,我选了19200、8数据位、偶校验、1停止位。为什么选偶校验?多数仪表默认的Modbus RTU参数就是偶校验,现场一致性高,当然这不是绝对标准,最终以你手里仪表的说明书为准。
从站地址我做了带间隔的规划:温度巡检仪用1~12,压力变送器用21~28,流量计用31~38,中间留出空号。这么做的好处是后续加表、替换设备时不需要重排所有地址,也不容易在调试时把相近型号的表认混。地址是仪表侧设置的,每台都要确认好。
2.3 Modbus报文、寄存器类型和32位浮点字序
Modbus RTU报文本身不复杂,一条请求帧包括从站地址、功能码、起始寄存器地址、寄存器数量和CRC校验。S7-1200的MB_MASTER指令会自动打包和解包报文,你只需要告诉它从站地址、功能码(通过MODE和DATA_ADDR体现)、数据起始地址和长度。
寄存器类型要分清:0xxxx对应线圈,1xxxx对应离散输入,3xxxx对应输入寄存器,4xxxx对应保持寄存器。测试仪表里常用的数据大多在4xxxx保持寄存器区,部分表支持3xxxx输入寄存器区。MB_MASTER的DATA_ADDR参数写法是40001代表保持寄存器第一个字,30001代表输入寄存器第一个字,如果你手里的仪表手册把地址写成“寄存器40021”,那DATA_ADDR就填40021。
32位浮点数据是最容易出问题的点。仪表上位传来的温度、压力、流量数值通常是两个连续的16位寄存器拼成的一个32位IEEE 754浮点。两个字的先后顺序在不同品牌仪表上不一致:有的高位字在前,有的低位字在前。PLC读出原始INT数组后,需要手动拼成DWORD再转REAL,如果拼反了,数值就变成天文数字或者接近0的乱数。调试时看到这种特征,第一反应就是字序反了,把两个WORD的拼接顺序对调即可。
3. PLC轮询程序:从数据块到状态机
3.1 用一张轮询表把28个从站组织起来
轮询程序的核心思想是表驱动。不要在主程序里写28个MB_MASTER调用块硬编码,那样后期维护量巨大。我建了一个轮询配置DB,里面是一个长度为28的数组,每条记录描述一个从站的请求参数和结果存放位置。伪结构示意如下:
PollTable[0..27] STRUCT SlaveAddr : INT // 从站地址 1~28 FuncCode : INT // 3=读保持寄存器 6/16=写 DataAddr : INT // 起始寄存器地址,如 40021 DataLen : INT // 数据长度(字) ResultDBNo : INT // 结果存放DB编号 ResultOffset: INT // 结果存放偏移字节 IsReal : BOOL // 是否需拼装为REAL WordSwap : BOOL // 字序是否需要交换 FailCnt : INT // 连续失败次数 Status : INT // 状态字:0未通 1正常 2超时 Skip : BOOL // 故障跳过标志 END_STRUCT这套结构的好处是:仪表地址变了只改配置DB,不需要动逻辑代码;28台表的参数一目了然;后续增加到32台只需要扩展数组长度。结果区我单独建了一个“结果DB”,里面按仪表类型划分数组,比如温度值数组、压力值数组、流量值数组、累计量数组,以及一份对应28台从站的通讯状态数组。这样触摸屏访问变量时很有规律,画面组态也省事。
3.2 MB_COMM_LOAD初始化和MB_MASTER动态调用
MB_COMM_LOAD负责配置串口参数,程序里用一次上升沿调用即可,相当于给CM1241设置波特率、校验和数据格式。RESULT_TIMEOUT参数我设在300ms,这个值要根据现场实测微调,太短仪表还没回复就被判超时,太长单台掉线后整轮轮询会被拖慢。RTS相关参数保持默认,CM1241的RS485方向切换由模块自动完成,遇到回显异常再单独调。
MB_MASTER是真正发送请求的块,难点在于同一时刻只能有一个请求在总线上,否则冲突。所以我就调用了一个MB_MASTER实例,通过修改它的输入参数来实现28个从站的顺序请求。轮询状态机用SCL写比较直观,核心逻辑类似下面这样:
// 轮询调度主状态机 CASE #state OF 0: // 启动下一轮,从第0台开始 #idx := 0; #state := 1; 1: // 根据轮询表装载MB_MASTER请求参数 #reqData := #PollTable[#idx]; #reqTrigger := TRUE; // 置上升沿 #state := 2; 2: // 等待MB_MASTER完成 IF #mbDone THEN // 正常:拷贝结果、清失败计数 #state := 3; ELSIF #mbError THEN // 异常:失败计数加1,记录状态码 #state := 3; END_IF; 3: // 索引下移,准备下一台 #idx := #idx + 1; IF #idx > 27 THEN #idx := 0; END_IF; #reqTrigger := FALSE; #state := 1; END_CASE触发MB_MASTER的REQ必须是一个边沿信号,不能在等待期间一直保持TRUE。状态机在等待阶段时MB_MASTER块本身仍在执行,它的DONE/ERROR输出会自然触发状态切换,这里不需要额外延时常数。这样做的好处是,即使某台仪表不响应,300ms超时后错误码会过来,状态机继续往前走,总线永远堵不住。
3.3 数据搬运、超时统计和自动恢复
每台表读完的原始数据要按轮询表里的IsReal和WordSwap标志做后处理。以32位浮点为例,读出两个16位字之后,先判断WordSwap,按要求交换顺序,再拼接成DWORD,最后转成REAL存到结果DB。这部分用SCL写一个通用转换小函数,28台表共用一份逻辑,不要每台单独写。
错误处理是我这轮项目里最看重的一环。每台从站维护一个连续失败计数FailCnt,正常一次就清零;连续失败3次,置对应的通讯故障位,触摸屏上这台表的状态灯变红。故障表不建议立即从轮询里摘除,因为很多仪表是现场临时断电、重启,摘除后恢复检测更麻烦。我的做法是:故障表继续轮询,但把它排到整轮的最后,避免它占掉正常表的时间片。某台表恢复响应后,FailCnt清零,故障位自动复位,画面状态灯回绿,完全不需要人工干预。这种自动恢复机制在实际生产中非常实用。
写操作我单独设计了一个“写请求队列”。比如从触摸屏点击“累计量清零”按钮,PLC侧不是立刻直写仪表,而是往队列里放一条写记录,由轮询状态机在空闲时执行。为什么这么做?因为写操作也是总线请求,如果和正常读请求同时发出,必然冲突;用队列统一调度,读写的优先级、互斥关系都清晰可控。写记录包含从站地址、写入寄存器、写入值和完成标志,执行完把完成标志回传给HMI。
4. 触摸屏程序:数据呈现和操作交互
4.1 HMI变量映射的省力做法
KTP700这级别的触摸屏和S7-1200在TIA Portal里同项目组态时,可以直接引用PLC的DB变量,不需要在HMI侧重新建一堆地址相同的重复变量。结果DB建议设置成非优化访问,这样HMI可以通过绝对地址直接访问数据,在线监视也更直观。变量命名我推荐“仪表类型_序号_物理量”的风格,比如Temp_01_Value、Pres_03_Value、Flow_05_Instant,28台设备排下来一眼能看明白对应关系。
HMI侧不需要做任何数值换算,PLC已经把所有浮点拼装好了。这么做的好处是,如果后期换了某台仪表的字序或地址,只改PLC侧DB配置,触摸屏画面一个变量都不用动。
4.2 画面布局:总览、分组和单表详情
28台仪表的画面不能只放在一屏里硬塞,那样触摸屏上字太小,操作工看着费劲。我设计了三级画面结构:第一级是总览画面,用一个4×7的网格展示28台仪表的运行状态灯和核心数值,状态灯有绿、红两色,绿色代表通讯正常,红色代表连续通讯失败,数值显示当前实时值;第二级是分组画面,按温度、压力、流量分成三个子页面,每组最多12台,信息密度适中;第三级是单表详情画面,显示完整寄存器数据、通讯状态字、最近一次错误码,以及针对该表的功能按钮。
页面切换按钮统一放在底部导航条,操作工的手指逻辑是从总览→分组→详情一路点进去。总览画面的状态灯要做得显眼,操作工不需要看具体数值就知道哪台设备掉线了。触摸屏和PLC的通讯是走以太网的,不会占用RS485总线,画面刷新率不用担心,基本是秒级同步。
4.3 报警设置和数据记录边界
通讯故障报警我放在PLC侧处理,每台表连续失败3次就置一个BOOL变量,HMI用报警文本关联这个变量,报警触发时显示“仪表XX通讯故障”。这里要注意,报警文本里的设备编号要用HMI的多语言或变量文本功能,否则改仪表编号还要回报警配置里一个个改。写操作完成状态也要做成提示,比如“累计量清零完成”,避免操作工反复点击按钮。
关于历史数据要提醒一句:KTP700 Basic面板的本地历史记录能力有限,如果项目要求长期保存28台仪表的趋势曲线,别指望Basic面板里那点存储空间。要么选Comfort系列面板配存储介质,要么让PLC把数据记录到存储卡的Data Log里,最稳妥的做法是加一个上位机用数据库存。我这个项目只要求实时显示和短时报警,所以没有做强历史功能,但这块需求边界最好在方案阶段就跟用户确认清楚。
5. 现场联调问题排查实录
5.1 从站无响应与整条总线的“卡死”
第一次上电联调就遇到了经典问题:28台表里有一台没配置地址,结果后面排队的全都不出数了。原理很简单,地址冲突的表会对同一请求同时应答,总线上的数据帧互相叠加,CRC必然全错,MB_MASTER返回通讯错误,状态机要等超时才能继续。排查方法是:只留PLC和第一台表在总线上测试,确认无误后逐台增加,每加一台就观察其他台是否受到影响。这种“先少后多”的联调节奏看着慢,实际是排查地址冲突的最高效手段。
总线“卡死”还有一种情况是某台表的485芯片故障,拉低了总线电平,导致所有通讯都失败。这时候最直接的判断方法是断开该台表的总线接线,看其余表是否恢复。如果一台一台断开才能定位,那基本上就是硬件故障而不是参数问题。注意热拔插485线时最好断电操作,带电拔插容易烧通讯芯片。
5.2 数值串位、跳变和字序颠倒
在线监视时看到某台压力值显示成类似“-2.3E+38”这种乱码,基本可以断定是32位浮点字序反了或者寄存器起始地址错了。我的排查顺序是:先看原始两个字的数值是否符合预期,比如压力量程0~1.6MPa,原始INT字应该在合理范围内;然后看拼接后的浮点量级,差几个数量级基本就是对不齐。有些仪表手册会把地址写成“40021-40022”这样的区间,你要确认起始地址到底是40021还是40023,差一个字整个数值就完全错了。
数值偶尔跳变则是另一类问题。我遇到的一台温度巡检仪读上来的值每隔几十秒跳一次,查了好久发现是屏蔽层接地没做好,车间里有一台变频器启动时就受干扰。处理办法是屏蔽层可靠单端接地,同时把总线线缆和动力电缆分开走线槽,至少保持20厘米距离。如果现场确实避免不了干扰源,可以在PLC侧加一个RS485隔离中继器,费用不高,但能把共模干扰切断,这类场景下很有效。
5.3 轮询周期优化和后台维护技巧
全部28台表正常通讯后,我实测了一轮完整轮询周期大约是1.2秒左右。这个速度主要受两个因素影响:每台表的内部响应时间和RESULT_TIMEOUT的大小。如果想把周期压进1秒内,我的建议是不要只靠调小超时时间,因为超时时间太小会让响应稍慢的表误判失败。更合理的做法是分级轮询:重要的温度、压力表每一轮都读,流量累计量这种变化不频繁的数据可以每5轮读一次,相当于把总线时间片留给更关键的实时值。
给后台维护留个后门也很有价值。我在触摸屏隐藏页面加了一个“单表调试”功能,操作方式是指定某台从站地址,手动发送一条读请求并显示原始返回报文。这功能平时不用,但现场排查问题时简直就是神器——你可以绕过正常轮询逻辑,直接验证单台仪表通讯是否正常,判断到底是表的问题还是总线的问题。我在调试时还习惯用TIA Portal的监视表格在线看MB_MASTER的STATUS状态码,配合单表调试功能,基本能覆盖90%的通讯故障定位场景。
做这种多从站轮询项目,我个人的体会是:程序逻辑永远不是主要风险,物理链路和协议细节才是。只要你把通讯参数确认表做细、把布线按规范落地、把轮询表和数据块结构设计得足够抽象,28台表和8台表在程序上几乎没有区别,无非是数组长一点短一点。希望这篇记录能帮你少走几个弯路,遇到“仪表不上数”的时候,先检查接线,再检查参数,最后才去翻程序,顺序对了,问题就解决了一半。