1. 先想清楚这28个仪表背后的真实需求
做这个项目之前,我先去现场蹲了半天。检测线上28台测试仪表分布在三个工位区,每台仪表测不同的参数,有些是压力、有些是流量、还有几台是温度。现场工人每小时抄一次表,把数据填到纸质记录单上,到点没抄还要补。月底统计报表的时候,光整理这些纸质数据就要花整整一天,而且经常发现同一时刻的记录对不上。
所以这个项目的真实需求不是"把28个仪表接上PLC"这么简单,而是要解决三件事:一是把实时测量值自动采集上来,该显示的显示、该报警的报警;二是让操作员在中控触摸屏上随时切换查看任意一台仪表的数据,历史变化也能查;三是给后续联网上传、统计报表留好接口。
这个项目最适合谁参考?如果你手上也有一批Modbus RTU协议的仪表(不管是压力变送器、温控器、流量计还是其他带485接口的智能仪表),数量在几台到几十台之间,想统一接入S7-1200 PLC再加触摸屏显示,那这篇文章基本能帮你把整个链路捋清楚。我踩过的坑和最终的解决方案都会写出来,包括硬件接线、PLC轮询逻辑、触摸屏联动以及调试方法,照着做就能少走大半弯路。
核心点先放在这里:28个仪表走485总线,本质上是"一条线上挂28个从站,主站一个一个点名",PLC这边要做的就是高效地"点名",触摸屏那边要做的就是别让操作员在28个页面里迷路。下面我从需求推导开始,逐步展开整个实现过程。
2. 通信工程量摸底:仪表参数、地址分配与485接线方案
2.1 仪表侧通信参数核对的必要性
很多项目卡在第一步,不是程序写不出来,而是根本没搞清楚每台仪表到底支持什么通信参数。485总线上的所有设备,波特率、数据位、校验位、停止位必须完全一致,只要有一台仪表设置不对,轻则这台设备不响应,重则把整条总线上的通信搅乱。
我接手的这批仪表,出厂默认大多是9600bps、8数据位、1位停止位、无校验,但里面有几台被人改过参数,地址也有重复的。开工之前我拿USB转485适配器,接上电脑逐个访问了一遍,把每台仪表的实际参数摸清楚。这里建议做一个表格,就像下面这样:
| 仪表编号 | 安装位置 | 从站地址 | 波特率 | 校验方式 | 读取寄存器起始地址 | 寄存器数量 | 数据含义 |
|---|---|---|---|---|---|---|---|
| 1 | 一区A工位 | 1 | 9600 | 无校验 | 40001 | 2 | 当前压力、单位 |
| 2 | 一区A工位 | 2 | 9600 | 无校验 | 40001 | 2 | 当前压力、峰值 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 28 | 三区F工位 | 66 | 19200 | 偶校验 | 40003 | 3 | 温度、上限、下限 |
这一步千万不要凭印象写。某些仪表通过面板按键改了地址后,波特率显示值并不代表实际生效值,我遇到过一台表在面板上看到的是9600,断电重启后实际是19200,最后还是靠串口调试工具反复探测才确认。建议每台仪表用上位机测试软件单独通信一次,确认能稳定读到数据后,再统一分配地址。
2.2 RS485总线的接线和终端电阻处理
28台仪表挂在同一条485总线上,线缆长度加起来大约有120米,中间还经过几个接线端子排。RS485的A、B两根线必须用双绞线,屏蔽层在PLC侧单端接地。这里有个容易犯的错:有些人把屏蔽层在两头都接地,结果地电位差导致通信干扰比不接地还严重。
终端电阻的处理也有讲究。RS485标准要求在总线两端各接一个120欧终端电阻。现场28台仪表,最远端的那个从站设备内部通常可以跳线接入终端电阻,PLC侧的通信模块上也带终端电阻开关。中段的仪表一律不要接,否则会拉低总线电平。
节点连接方式也需要注意:我从PLC通信模块出来,先到一区接线端子排,再从端子排分别跳到每台仪表,而不是一条线串到底再折返。因为仪表分散在不同工位,串联走线会绕很多弯,而且中间任何一台仪表接线松动会导致后面所有设备失联。星形分支虽然理论上对485不友好,但实际几十米内的分支影响不大,前提是分支线尽量短,仪表端出来的线控制在3米以内。
2.3 PLC侧通信口的选择
S7-1200 PLC这边,CPU自带的485口不一定能满足需求,项目里用的是CM1241 RS485通信模块。选这个模块有两个理由:第一,它和CPU之间通过背板总线交换数据,通信过程不占用CPU的循环扫描时间;第二,CM1241支持Modbus RTU主站功能,用系统自带的MB_COMM_LOAD和MB_MASTER指令就能实现,不必自己拼CRC校验帧。
如果你手头项目预算紧张,也可以用CB1241通信板,但它只能插在特定型号的CPU上,而且通信处理能力弱一些。28台仪表的轮询量,CM1241跑起来比较从容,CB1241会有点吃力。我在这个项目里没用第三方通信网关,因为S7-1200自带的指令块足够完成全部功能。
3. PLC轮询读写程序:把28个仪表串成一个协作队列
3.1 轮询策略的选择:为什么不用定时器逐台触发
最直观的写法是:用28个定时器,每台仪表一个,定时到了就触发一次读取。这个方案在只有三四台设备时能用,但到了28台就乱了——不仅定时器数量多,而且会造成总线冲突:如果两台仪表同时到达触发时间,Modbus指令块同一时刻只能处理一个请求,多余的REQ会被忽略,日志里全是通信错误。
所以我用的是状态机轮询方式。程序中定义一个"轮询队列",里面记录当前正在访问的从站编号,一次请求发出后,等待完成信号,然后再自动跳转到下一个从站。这样在同一时刻总线上只有一个请求在传输,从源头上避免了冲突。
轮询队列的数据结构我放在了独立数据块里,核心要素包括:当前轮询到的仪表序号、完成标志、错误计数、以及每个仪表最近一次读取的时间戳。这样后续在触摸屏上显示"通信正常/超时"就有数据依据。
3.2 MB_COMM_LOAD初始化与MB_MASTER读写的完整调用逻辑
CM1241模块使用Modbus RTU主站功能,需要先在OB100初始化程序里调用一次MB_COMM_LOAD来配置串口参数。关键参数如下:
- PORT:CM1241模块的硬件标识符,在硬件组态里能看到,比如269;
- BAUD:波特率,设为9600;
- PARITY:无校验NONE;
- MB_DB:指向MB_COMM_LOAD的背景数据块;
- 其他参数使用默认值。
初始化完成后,每次读写调用MB_MASTER指令,触发方式不是常开信号,而是上升沿。我的做法是在轮询状态机里,当进入一个新的从站序号时,置位一个"请求触发"标志,MB_MASTER的REQ管脚接这个标志,指令执行完成后会复位BUSY,同时把"请求触发"标志清掉。
MB_MASTER指令的几个关键输入管脚:
| 管脚 | 作用 | 本项目设置 |
|---|---|---|
| MB_ADDR | 从站地址 | 来自轮询队列的仪表地址 |
| MB_MODE | 0=读,1=写 | 读取设为0,写设定值时设为1 |
| MB_DATA_ADDR | 寄存器起始地址 | 按仪表参数表填写 |
| MB_DATA_LEN | 数据长度 | 字数量 |
| MB_DATA_PTR | 数据指针 | 指向对应的PLC数据块地址 |
读回来的数据存放位置,我定义了与仪表序号一一对应的数组结构。比如第5台仪表的两个测量值,就放在DB结构中的"仪表数据[5].测量值1"和"仪表数据[5].测量值2"里。这样无论是触摸屏画面还是后续的数据分析,引用起来都非常方便。
3.3 轮询周期估算与超时重试机制的设计
轮询周期直接影响数据刷新频率,这是方案能否实用的关键。先做个粗算:9600波特率下,1字节约耗时1毫秒,一条读请求约8字节,正常响应约11字节,加上仪表内部的响应延迟(一般在10到50毫秒),单台仪表完成一次读取大约需要60到100毫秒。28台全部轮询一次,理想情况需要1.7到2.8秒。但MB_MASTER执行期间,CPU和通信模块之间还有数据交换开销,实际测下来完整轮询一圈大约3.5秒。
也就是说,触摸屏上任意一台仪表的数据,最坏情况下会有3.5秒的刷新延迟。对于压力、温度这类变化不快的参数完全够用。如果是流量计需要秒级刷新,就另当别论了。我在项目里给每台仪表读取之间加了10毫秒的间隔,防止通信模块还没完全释放总线就发起下一帧请求。
超时设置要小心。MB_MASTER的响应超时默认是1000毫秒,对于9600波特率来说太长——如果某台仪表断电或者地址错误,请求发出后要等满1秒才会报错,28台里坏掉一台,轮询周期会瞬间恶化到接近30秒。我把超时时间调到200毫秒,连续3次通信失败才判定这台仪表故障,并把它的地址从轮询队列里临时移除,隔一段时间再尝试恢复。这样单台故障不会拖垮整个系统。
3.4 写入操作怎么与轮询共存
28台仪表里,有一部分不只是读取,还需要远程修改设定值。写入操作不能随便插在轮询队列中间,否则可能造成总线竞争。我的做法是在轮询状态机里增加一个"写请求"分支:操作员在触摸屏上修改设定值后,PLC先把目标仪表序号和写入值存到一个"待写缓冲区",状态机在完成当前仪表的读取后,优先处理一次写入操作,然后再继续轮询。这样一个循环里最多插入一次写操作,对轮询周期的影响不超过100毫秒,完全可以接受。
写入的数据还会被原样读回来做校验,比如设定值写进去后,下一次轮询到该仪表时,读取返回的当前设定值,与缓存的值比对,一致则确认写入成功,不一致则记录错误并在触摸屏上提示。
3.5 关于Modbus地址的两种理解方式
调试时容易被绕晕的一点,是"寄存器地址到底从几开始"。很多仪表说明书上说的是"40001对应地址0",有的说"起始地址是40001",还有的用十六进制"0x0000"表示。MB_MASTER指令的MB_DATA_ADDR管脚填的是真正的寄存器地址偏移量——比如说明书里写的保持寄存器起始是40001,那MB_DATA_ADDR就填0;要读40003,就填2。如果填成了40001这个十进制的40001,指令会直接报3号错误代码(非法数据地址)。
这个细节我在现场调试时困扰了很久,后来用Modbus调试助手逐个对照才彻底搞明白。各位做类似项目时,请务必先拿串口调试软件把仪表地址摸清楚,再往PLC指令里填。
4. 触摸屏的画面设计与数据联动要点
4.1 28台仪表在屏上怎么摆,操作员才不晕
触摸屏上直接排28个仪表画面是灾难性的,操作员会找不到想看的那一台。我用了三级页面结构:
- 总览页:一张画面分三个区域,对应三个工位区。每个区域列出该区所有仪表的最新数值和通信状态,绿色表示正常、红色表示通信故障、黄色表示数值越限。这一屏是操作员用得最多的,扫一眼就知道全线状态。
- 分组详情页:点击总览页某个工位区,进入该区的仪表列表,每台仪表一行,显示实时值、单位、报警状态、统计信息。
- 单表详情页:点击某一行,进入仪表详情,显示完整的测量参数、设定值、上下限,以及最近24小时的数据趋势。
页面切换要用触摸屏的"区域指针"功能,这样总览页点选哪台仪表,详情页就知道要显示哪台的数据。实际效果是:操作员在总览页点一下某台仪表,直接跳到该表详情,不用来回翻页面。
4.2 数据有效性标志:防止显示假数据
这是我在设计阶段就考虑到的:当某台仪表通信超时后,PLC数据块里保存的是最后一次成功读取的值,如果触摸屏上还显示这个值,操作员会误以为这是实时数据,这是很危险的。我在PLC里给每台仪表增加了一个"数据有效性"字,通信正常时为16#0001,超时或故障时为16#0000,第一次上电尚未读到数据时也是16#0000。触摸屏上所有数值显示都关联这个有效性标志,无效数据显示为"####"或者空,同时在状态栏提示"通信中断"。这一条强烈建议复制到你的项目里——别让一个过期数据引发误判。
4.3 报警与历史记录的落地方式
报警在触摸屏侧做比较方便。S7-1200 PLC把每台仪表的越限标志(上限越限、下限越限、通信故障)汇总到一个整数字里,触摸屏通过报警变量关联,触发时弹出报警条并记录时间戳。操作员确认报警后,报警状态在HMI侧消除,但PLC侧的越限标志仍然保留,直到仪表数值恢复正常并再次轮询确认,这是为了避免"报警确认后就不再提醒"的漏洞。
历史趋势我用了触摸屏自带的缓冲区记录功能,把28台仪表的关键测量值按每分钟一个点循环记录到HMI的存储区,保留最近24小时。这样既不用额外增加存储硬件,也够现场查看趋势用了。如果你需要保存更长时间的数据,建议后续用上位机系统做数据库存储,触摸屏这边只做显示。
4.4 触摸屏与PLC的数据交换方式
这一步没有特殊之处,就是通过HMI变量关联PLC数据块地址。需要注意的点是:触摸屏访问PLC数据块时并不会影响Modbus轮询,因为S7-1200的通信模块和HMI访问走的是不同通道。不过在设计DB时,建议把触摸屏要频繁读取的变量放在同一个数据块里,并且连续排列,这样触摸屏的批量读取效率更高,画面切换响应也更快。
我把"仪表当前值"数组和"仪表通信状态"数组集中放在一个专用DB中,触摸屏以"数据块访问"的方式按地址连续读取,实测画面切换时间在1秒以内,操作员反馈和直接操作本地仪表面板差不多。
5. 现场调试踩坑记录:三处卡壳的问题与排查过程
5.1 后半段仪表全部超时:终端电阻与线缆走向的连锁问题
调试到第三天,现场情况是第一区9台仪表通信全部正常,二区、三区19台仪表基本连不上,偶尔能通上一两台。一开始我怀疑是仪表地址分配错误,但用电脑接在总线末端单独测二区的仪表,却都能正常通信——这说明仪表本身没问题,问题出在整条总线的物理链路上。
排查过程:先用万用表量总线末端A、B之间的电阻,发现只有约60欧,再量PLC侧也是60欧。按照120欧终端电阻的并联规则,如果总线两端各接一个120欧电阻,总阻值应该在60欧左右,看起来似乎正常。但我单独把总线中间某段断开再量,发现二区末端那边竟然有两个120欧电阻并联。原来二区最后一台仪表内部的终端电阻跳线被设置成了"接入",而现场接线端子排上又有人额外跨接了一个120欧电阻,导致这段分支的反射信号特别严重,长距离传输下信号质量崩溃。
解决方法是:把二区末端仪表内部的终端电阻跳线取消,只保留总线终点(三区F工位最后一台仪表)内部的一个120欧终端电阻,PLC侧的终端电阻开关保持接通。同时把二区和三区之间的线缆走向优化了一下,去掉了几个多余的绕线,改走最短路径。处理后整条总线28台仪表全部稳定通信,连续运行48小时无一次超时。
5.2 连续读取到第6台仪表时总线挂死:地址冲突与波特率混用
还有一次调试时发现:PLC轮询到第6台仪表后,后续所有仪表全部无响应,但把第6台仪表从总线上摘掉后一切恢复正常。用电脑逐个检测发现,第6台仪表的地址被设置成了1,和第一台仪表冲突。Modbus协议中虽然允许两个从站使用同一地址,但实际表现往往是"谁响应速度快谁先回",第三、第五次请求时另一台响应,数据就乱了。
更隐蔽的问题是波特率不一致。现场有2台仪表的波特率是19200,其余是9600,这在总线上造成的现象特别迷惑:PLC按9600发送请求帧,19200的仪表收不到完整请求,但它的接收电路仍然会向总线发送噪声信号,导致同一时刻总线上有多个设备在驱动电平,正常9600的仪表也无法正确接收主站请求。最终表现就是"轮询到某个位置后整条总线通信质量下降",而不是明确报错。
排查方法是把总线上所有仪表地址重新核对,并统一设置成9600、无校验、8数据位、1停止位。这里有个小技巧:把仪表逐个从总线摘除再观察故障是否消失,就能快速定位干扰源。确定是那2台19200的设备后,通过面板菜单改回9600,地址全部改为1-28连续编号,重新分配后问题彻底消失。
5.3 触摸屏上偶发出现"9999.9":数据字与符号位处理问题
项目快交付时,操作员反映温度画面偶尔会跳出一个9999.9的离谱值,过几秒钟又恢复正常。排查后发现,这是一台温度仪表的测量值寄存器是带符号整数,而我在PLC里定义的是无符号整数,当温度测量值为负值时(现场有时会测冰水温度),无符号解读导致数据变为65535减去某个偏移,触摸屏上就显示成了异常大值。
修正方法:把数据块中该仪表的测量值变量类型改为Int(带符号16位整数),触摸屏侧同步改为带符号显示,并在画面里把绝对值的线性转换做好。还有一个更好的做法:在PLC里增加一个工程量转换功能块,把原始寄存器值(比如0-27648)线性映射到实际工程值(比如-50到150摄氏度)。很多仪表本身就支持直接输出工程值,但有些只输出原始码,这时候必须在PLC侧做一次量程转换。建议所有模拟量仪表都在PLC里做统一转换,以便触摸屏侧只管显示,不用重复处理换算逻辑。
5.4 调试阶段最实用的两个工具与一套流程
调试485通信,我强烈建议准备一个USB转RS485适配器,以及一套Modbus调试软件(常用的有Modbus Poll或者串口调试助手)。流程是这样:先把电脑接到总线上,以主站身份手动读取每台仪表的数据,确认地址、寄存器、长度、数据类型都正确,再断开电脑把总线交给PLC去轮询。PLC轮询过程中,如果出问题,再把电脑以"监听模式"挂到总线上,观察PLC发出的请求帧和仪表响应的数据帧,很容易定位是哪台设备不回帧还是回帧异常。
这个流程看起来多了一步,但能省掉大量盲调的时间。我第一次盲目改了好几轮程序都没找到问题,后来老老实实把电脑挂上去看报文,五分钟就发现了是地址冲突。调试串口通信,数据帧才是最诚实的。
6. 这套方案的适用边界与后续扩展思路
6.1 什么时候需要拆分总线
28台仪表9600波特率下,轮询一圈大约3.5秒,这个刷新速率对压力、温度、液位这种缓变参数完全够用。但如果你的项目是几十台流量计或者分析仪表,要求数据刷新周期在1秒以内,那就不能靠提高波特率硬扛——优先把仪表分成两条甚至三条RS485总线,每条线挂14-15台,用两个通信模块或者加一路扩展模块,这样刷新时间直接减半。我的经验是单条485总线挂载不超过20台比较稳妥,超过20台建议分线。
还有什么情况需要分线?如果现场线缆非常长(超过500米),或者经过变频器、大功率电机等强干扰区域,就算终端电阻处理得好,通信稳定性也会下降。这种场景下要么用带隔离的RS485中继器,要么干脆拉两条总线。隔离器在高干扰场合基本是必需品,400元左右的投资能换回后续大量调试时间,值。
6.2 往上位机系统和远程监控方向扩展
这套PLC+触摸屏的方案,后续往信息化方向扩展很方便。S7-1200本体带有以太网口,可以直接与上位机通信。如果想做远程监控,可以在交换机上接入网关模块,把PLC数据网络上传到服务器,手机端通过网页或者APP查看28台仪表的实时数据和历史报警。PLC程序侧几乎不用改动,只需要把数据块里的内容映射到网关的变量表即可。
还有一种扩展思路:把仪表数据同步到工厂的MES系统。因为PLC数据块里的数据结构已经和仪表编号一一对应,MES那边通过OPC UA协议读取数据块即可,不需要关心底层Modbus轮询的细节。这也是当时我把所有仪表数据集中放在一个独立DB里的原因——模块化结构让后续对接省了很多事。
6.3 这个项目里最值得保留的三条设计习惯
第一个习惯:通信相关的所有参数集中到一个数据块里,包括波特率、超时时间、重试次数、每个从站的使能标志。这样后期维护时不需要翻遍整个程序找参数,一个DB改完所有。第二个习惯:所有数据有效性标志和设备状态在PLC里统一管理,触摸屏只做显示和操作确认,避免两边各搞一套逻辑导致状态不一致。第三个习惯:每一台仪表的数据结构都留了备用字节和时间戳字段,万一后续要加仪表信息联动或者做数据溯源,不需要大规模改动程序结构。
这三个习惯从本次项目的实际维护过程来看,让我节省了大量时间。项目交付后,现场陆续换过几台仪表,每次都只需要改数据块里的从站地址和寄存器地址,程序主体结构完全不用动。如果你也在做类似的多设备采集项目,建议从一开始就按这个思路设计程序架构,收益是长期的。