ARTICLE DETAIL

资讯详情

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

MCGS触摸屏与Modbus TCP通讯实战:寄存器映射、字符串处理与多设备轮询

MCGS触摸屏与Modbus TCP通讯实战:寄存器映射、字符串处理与多设备轮询 1. 为什么是MCGS触摸屏 Modbus TCP这套组合做自动化项目这么多年我见过太多工程师在触摸屏和PLC之间选型时纠结半天其实根本没必要。如果你手里的设备是西门子、三菱、欧姆龙这类自带以太网口的PLC或者是一些支持标准Modbus TCP协议的第三方仪表、驱动器、IO模块那MCGS触摸屏配上Modbus TCP通讯基本就是一套万金油方案能覆盖绝大多数现场需求。先说MCGS。昆仑通态的触摸屏在国产组态软件里占有率一直很高原因很简单组态方便、驱动丰富、价格亲民。尤其是它的设备驱动里内置了多种Modbus协议驱动从串口Modbus RTU到网络Modbus TCP都有工程师不需要自己写一大摞通讯代码只需要在组态环境里把变量和寄存器地址对应好就能实现读写。这一点在中大型项目里特别吃香因为现场的设备品牌千奇百怪但只要你愿意几乎所有设备都能通过Modbus协议翻译成同一套语言。再说Modbus TCP。它本质上就是把传统Modbus RTU的报文封装在TCP/IP协议里端口固定为502。相比Modbus RTU它不需要考虑串口波特率、数据位、校验位这些繁琐参数只要IP能通、端口能通基本问题就不大。而且Modbus TCP支持一个主站同时连接多个从站多台设备之间轮流通讯是常态这对后续描述的多设备轮询场景非常重要。我个人的习惯是只要现场设备支持Modbus TCP我就优先走TCP因为排查链路比串口省心太多。你拿一台电脑跑Modbus Poll或者直接在命令行ping一下IP就知道设备在不在线不像是串口通讯还得反复检查接线、终端电阻、地电位头大得很。这篇文章就从我的实际项目出发把MCGS触摸屏通过Modbus TCP读写寄存器、处理字符串、多设备轮询以及现场调试踩坑全部串起来讲一遍。没有废话都是能直接拿去参考的东西。2. 寄存器映射与功能码通讯之前先把这张表搞懂很多初学者上来就在MCGS里创建设备、填IP、填端口然后发现数据死活不对要么是地址偏移一位要么是写入不生效。这背后的原因绝大多数出在寄存器映射和功能码上。你如果不清楚Modbus协议里寄存器地址是怎么定义的那摆在面前的设备和触摸屏就完全是两个世界。2.1 四种对象类型与对应功能码Modbus协议把数据分为四类线圈Coil、离散输入Discrete Input、输入寄存器Input Register、保持寄存器Holding Register。在实际项目中最常用的是保持寄存器和线圈因为PLC和多数仪表都支持读写这两类。对象类型读写属性对应功能码MCGS变量类型线圈只读/可写01读、05写单、15写多开关型离散输入只读02读开关型输入寄存器只读04读数值型保持寄存器可读写03读、06写单、16写多数值型这里有个容易混淆的点有些人会用03功能码去读输入寄存器或者用04功能码去读保持寄存器然后发现读回来的数值完全对不上。其实Modbus协议规定得很清楚03对应保持寄存器04对应输入寄存器不能混着来。MCGS的驱动里一般会区分只读寄存器和读写寄存器你在组态时先想清楚设备端的数据到底是只读还是可读写再选择对应类型。2.2 地址偏移无数人踩过的第一道坎Modbus协议文档里通常用PLC地址来标记比如40001、40002这种。但MCGS驱动里填地址时你看到的往往是数据偏移地址比如0、1、2。这中间差着1也就是著名的地址偏移1问题。举个例子你设备手册上说保持寄存器地址是40010那在MCGS里填的偏移地址应该是9而不是10。如果在MCGS里直接填10你实际读写的是PLC侧的40011差别就在一个寄存器上。这个坑我在现场见过太多次很多人怀疑是通讯问题折腾半天结果就是地址没换算好。还有一种情况更隐蔽有些设备手册直接给的是寄存器地址的十六进制形式比如0x000A那MCGS里填10还是9这要看手册里有没有把协议地址和PLC地址分开。稳妥的办法是先用Modbus调试工具读一遍确认实际能读到的地址范围再往MCGS里填。2.3 MCGS里的变量类型对照MCGS里建变量时数据类型也要和寄存器匹配。保持寄存器默认是16位无符号整数如果你需要读32位浮点数比如变频器的频率、温度传感器的数值就必须把两个相邻寄存器拼起来同时选择32位浮点数数据格式。这时候务必注意字节顺序有的设备是高字在前有的是低字在前MCGS里一般有字节顺序设置项用对了才能显示正常数值。我在做项目时第一步永远是拿Modbus Poll这类工具按设备手册把每个寄存器扫一遍确认数值能读出来再把同样的参数搬到MCGS里。这个习惯帮我省下了很多排查时间后面也会提到。3. MCGS设备组态实操从添加驱动到变量绑定的完整流程3.1 新建工程与添加Modbus TCP驱动打开MCGS嵌入版组态环境先新建工程然后在设备窗口里右键添加设备。找到通用TCP/IP父设备和Modbus TCP数据转发设备前者负责网络连接后者负责协议解析。这两个设备是父子关系必须一起添加顺序也要注意先把父设备加上再挂子设备。父设备里填触摸屏的IP地址和端口号。实际场景中触摸屏作为Modbus TCP主站它的IP需要是固定的不能自动获取否则PLC或者工程师电脑没法稳定找到它。我习惯给触摸屏分配一个独立网段的固定IP比如192.168.1.10子网掩码255.255.255.0网关看现场需要同网段内通讯通常不用填网关。3.2 子设备参数配置的关键项子设备Modbus TCP数据转发设备里有一堆参数但真正影响通讯的就那么几个最小采集周期默认可能是100ms到1000ms这个值决定了触摸屏多久向从站发一次请求。如果你有快速变化的实时数据比如伺服位置、模拟量采集尽量设小一点比如50ms到100ms如果只是温度、液位这类慢变量500ms完全足够还能减轻网络负担。通讯等待时间默认300ms左右指触摸屏发出请求后等待从站回应的最长时间。如果设备响应慢必须调大否则触摸屏会认为超时频繁报错。重试次数建议设为3次避免一次通讯失败就直接弹故障。3.3 变量连接从设备寄存器到画面变量在MCGS的实时数据库里新建变量然后在设备窗口的通道连接里把变量连到对应寄存器通道。这里要理解一个概念可变数据触摸屏在采集周期内自动更新只写数据只在画面触发写入时才发送。举个例子我要把一个温度值从温控器读到触摸屏上显示就建一个数值型变量alarm temp然后在设备窗口的寄存器通道里选03读保持寄存器地址填偏移地址数据类型选16位无符号或32位浮点通道连接指向这个变量。这样触摸屏就会周期性地从温控器读取数值并更新到变量上。写入则相反。比如画面上有个设定温度输入框绑定的变量叫set temp通道类型选06写单个保持寄存器地址指向目标寄存器。这样操作员在画面上输入新温度并确认后MCGS会把数值写入设备对应寄存器。整个过程不需要写一行通讯代码全靠组态配置完成。3.4 通道批量添加几十个变量别再一个个建当设备有几十个连续寄存器要读写时千万别一个个手动添加通道效率太低。MCGS支持批量添加通道选起始地址、通道个数、读写属性一次性生成一批通道然后再批量关联变量。我一般先把设备的寄存器地址表整理成Excel标好每个通道对应哪个变量再复制到MCGS里批量生成既快又不容易出错。4. 寄存器写入的完整代码解析单寄存器与多寄存器写法虽然MCGS的组态方式能覆盖大部分需求但有些场景下还是得写脚本比如根据某个条件自动算出一批数据写到PLC、或者从PLC读取一组参数做校验。MCGS的脚本语言里读写寄存器是通过设备指令函数实现的最常见的就是!DeviceCtrl和WriteRegister这类指令。4.1 单寄存器写入最基础的写操作假设我要把变量new_value的值写入到1号从站保持寄存器的偏移地址0也就是PLC侧40001脚本写法如下!DeviceCtrl(设备0, 3, new_value)这里稍微解释一下参数。设备0是你在设备窗口里给Modbus TCP子设备起的名字3表示功能码03注意这地方容易混淆。MCGS的DeviceCtrl函数里的功能码参数和Modbus协议功能码不是一回事它是MCGS内部定义的操作码。实际项目里我更喜欢用更直接的写入指令。在MCGS的脚本里写单个寄存器最常用的是!WriteRegister(设备0, 0, new_value)意思是向设备0的偏移地址0写入new_value这个变量的值。这条指令隐含使用的是写单个保持寄存器功能码06。如果你要写多个连续寄存器用!WriteRegisters(设备0, 0, array, count)其中array是一个数组变量count是要写入的寄存器个数。注意MCGS脚本里数组下标从1开始这个和C语言不同我一开始就被坑过。4.2 为什么推荐用组态通道而不是纯脚本我在项目里有个体会能用组态通道绑定解决的问题尽量别用纯脚本。原因有三点。第一组态通道的读写是周期性的由MCGS内部调度稳定可靠不会因为脚本逻辑错误漏发或重复发送。第二组态变量的值在画面、报警、曲线、用户权限里都能直接用而脚本临时读到的值还得手动转存到变量里多了一步。第三排查问题时组态通道的状态在设备调试窗口里一目了然脚本跑没跑都不知道。所以我的应用模式是常规读写走组态通道特殊逻辑走脚本。脚本主要用于数据处理、条件判断、批量计算和字符串拼接而不是替代驱动去收发报文。4.3 32位数据写入拆字与合字现场经常遇到32位浮点数或32位整数要写入。MCGS里可以通过变量类型来简化但如果你必须从设备读回两个16位寄存器再拼成一个32位值还是得自己处理。公式也不复杂lng_value reg_high * 65536 reg_low写入时反过来reg_high Int(lng_value / 65536) reg_low lng_value Mod 65536这里的顺序取决于设备的字顺序。有些设备高字在低地址有些低字在低地址你必须在调试时确认一次然后把它固定下来写成函数复用。5. 字符串处理Modbus TCP里搬动ASCII字符的坑如果说寄存器写入是小学二年级那字符串处理就是高年级的附加题。Modbus协议本身并没有定义字符串的专属寄存器类型它只能传输16位寄存器所以字符串最常见的做法是一个寄存器装两个ASCII字符高八位放第一个字符低八位放第二个字符。这种编码方式有几种叫法有的厂商叫ASCII打包有的叫字内小端本质上都是一个意思。5.1 字符串编码一个寄存器塞两个字节字符串OK在ASCII表里分别是0x4F和0x4B。如果不加处理地放到一个保持寄存器里有的设备是高位在前0x4F4B有的设备是低位在前0x4B4F。这两种情况在Modbus TCP上下文中都存在非常考验人的耐心。你在MCGS里读字符串变量时同样会遇到字节顺序问题。MCGS的字符串变量通常能直接绑定到连续寄存器通道上但它的解析规则是固定的如果你的设备和MCGS的默认字节顺序不一致读出来的字符就是乱的比如OK变成了KO。解决思路有两种一种是找到设备手册里的字节顺序说明然后在MCGS驱动里调整字节顺序设置另一种是用脚本自己拼字符串。我个人更倾向于后者尤其是在设备文档不清晰的时候因为脚本的灵活性更高。5.2 用脚本把寄存器数组转成字符串假设我从连续4个寄存器里读回了一组数值现在要把它转成8个字符的ASCII字符串脚本大致可以这样写Dim str_result As String Dim temp_high As Integer Dim temp_low As Integer Dim i As Integer str_result For i 0 To 3 temp_high reg_array(i) \ 256 temp_low reg_array(i) Mod 256 str_result str_result Chr(temp_low) Chr(temp_high) Next i这段逻辑里最关键的是字符拼接顺序。每一位的转换都需要根据设备实际字节序来定如果你的设备是高字节在地址低位那就应该反过来str_result str_result Chr(temp_high) Chr(temp_low)实际上我在项目里就是把这两种写法封装成两个函数一个处理标准顺序一个处理交换顺序然后在调试时切换测试直到画面显示正常为止。5.3 字符串写入把文本塞进寄存器写入字符串是另一个方向。比如操作员在MCGS画面里输入一段物料批次号要发给PLC处理。脚本先算出字符串长度然后循环把每两个字符拼成一个整数再写入连续寄存器。Dim input_str As String Dim i As Integer, j As Integer Dim reg_value As Integer input_str !StrVar(设备0, batch_no) j 0 For i 1 To Len(input_str) Step 2 reg_value Asc(Mid(input_str, i, 1)) * 256 If i 1 Len(input_str) Then reg_value reg_value Asc(Mid(input_str, i 1, 1)) End If j j 1 !WriteRegister(设备0, 100 j, reg_value) Next i这个逻辑在批量写入时有一个问题如果你一次只写一个寄存器那么写10个字符的字符串就要写5次每次都是一个独立的Modbus请求效率低。更好的办法是用数组变量和WriteRegisters一次性把5个寄存器连续写出去Dim data_arr(5) As Integer Dim i As Integer Dim input_str As String input_str !StrVar(设备0, batch_no) For i 1 To 5 data_arr(i) Asc(Mid(input_str, i * 2 - 1, 1)) * 256 If i * 2 Len(input_str) Then data_arr(i) data_arr(i) Asc(Mid(input_str, i * 2, 1)) End If Next i !WriteRegisters(设备0, 100, data_arr, 5)这种写法至少能把5次请求压成1次对网络负载和响应速度都有明显改善。5.4 字符串长度和结束符最容易忽略的两件事字符串还有一个隐性要求PLC或者设备接收方怎么知道字符串到哪结束有的设备约定固定长度比如总是占用10个寄存器有的用0x00结尾有的用特殊ASCII字符结尾。你和设备厂商打交道时必须问清楚它的字符串格式约定。另外如果MCGS里输入的字符串比协议定义的寄存器区短建议手动补空格而不是直接发送短字符串。因为很多PLC侧的处理逻辑是按固定长度切割数据的短了会出现空字符后续正则或者比较逻辑直接出错。现场出过这种问题之后我现在都会在脚本里做一次长度检查和填充处理。6. 一主多从轮询MCGS同时读写多台Modbus TCP从站一个实际项目里很难只连一台设备。最常见的场景是S7-1200 PLC作为Modbus TCP客户端同时轮询4台支持Modbus TCP的第三方设备仪表、阀岛、变频器反过来MCGS触摸屏当主站时也可能同时要读好几台设备的参数。这里就涉及一主多从轮询机制。6.1 轮询的本质时间片轮流访问Modbus TCP是单请求单响应的模式一个主站在同一个时刻只能和一个从站通信。如果你需要和4个从站交换数据就必须把时间划分成片轮流访问1号站、2号站、3号站、4号站然后再回到1号站无限循环。在MCGS里你不需要手动写这个轮询逻辑驱动内部会自动处理多从站通讯。但前提是你要正确配置每个从站的IP地址和单元标识Unit ID。6.2 MCGS下的多设备配置方法在MCGS工程里如果需要访问多台Modbus TCP从站有两种做法第一种添加多个Modbus TCP子设备挂在同一个TCP/IP父设备下。每个子设备对应一台从站填不同IP然后各自配置寄存器通道和变量。第二种如果从站挂在同一个网关或路由器下你也可以只用一个子设备然后通过改变从站地址参数来区分不同单元但IP不同的情况下一般还是走第一种。我的经验是用多个子设备管理多个从站界面清晰后续维护也方便。每个子设备里都有独立的采集周期和通讯等待时间你可以给重要的设备设短周期给不重要的设备设长周期实现差异化采集。6.3 轮询时间怎么估算这一步对实时性要求高的项目特别关键。假设你接4台设备每台设备要在100ms内完成一次所有寄存器读取那么你至少要保证每台设备的采集周期加起来不超过100ms。如果每台设备的采集周期是20ms通讯等待时间是10ms那一轮下来就是4 * (20 10) 120ms已经超过100ms了这时候你就得调参数。通常现场里设备响应速度远快于10ms所以等待时间不是瓶颈真正的瓶颈是触摸屏的采集周期下限。有的MCGS型号最小采集周期只能到50ms左右再加上4台设备轮流访问一轮下来就是200ms以上。如果这个刷新速度满足不了生产要求你就得考虑换用更高性能的触摸屏或者减少读取寄存器数量把不必要的数据挪到手动刷新或者报警时才去读。6.4 轮询中的通讯失败导致的数据抖动轮询最怕的是某台从站突然响应超时。比如4号设备电源掉了触摸屏发请求后一直等待其他设备的数据也跟着一起卡住因为整个轮询序列被卡在了4号设备上。解决办法有两个。一是把通讯等待时间设短一点比如100ms超时后直接跳过继续访问下一台MCGS驱动里有通讯失败后继续运行的参数可以打开。另一个是给每台从站单独配置重试次数不要让一台设备的故障拖垮整个链路。我一般把重试次数设为0或1因为现场只需要知道它掉线了不需要一直接着重试。7. 调试实录三天里踩过的通讯坑这部分我挑了几个最有代表性的现场问题来复盘每个问题都把我折腾得够呛但最终都验证了一个道理Modbus TCP通讯的绝大多数问题都不是协议本身的问题而是配置和环境的匹配问题。7.1 现象能读不能写一写就报错最初遇到过触摸屏读设备数值正常但操作员在画面上点击写入时MCGS直接提示通讯失败。排查了很久最后发现是设备侧的寄存器属性不是保持寄存器而是输入寄存器。输入寄存器天生不可写你用06功能码去写它当然会失败。这个问题的教训是组态前一定要确认设备手册上每个寄存器的读写属性不要想当然地认为所有地址都能写。尤其是很多第三方仪表的说明书写得含糊寄存器三个字下面可能包含多种类型你最好自己用Modbus Poll分别用03和04功能码去读同一个地址能读到值但不让你写的多半是输入寄存器。7.2 现象读回来的数值总是乘以10或除以10有个案例是触摸屏上显示的温度是25.3但设备端实际是253。原因很简单设备端寄存器里存的是整数253和25.3之间隔着10倍缩放关系。Modbus寄存器本身不带小数点所有浮点语义都是由上位机或触摸屏二次处理的。解决方式有两种一种是在MCGS里对变量做工程转换设置线性变送的高低限和量程另一种是写脚本对原始值进行数学运算。我偏向于用工程转换因为它不消耗脚本执行时间而且变量在报警、历史曲线里的数值是一致的。需要注意转换公式的斜率要设置正确否则又会出现非线性偏差。7.3 现象字符串显示乱码或者首字母丢失之前做一台包装线触摸屏要从扫码枪设备读一个SN码字符串结果画面上显示的字符顺序混乱甚至比实际字符串少一位。排查后发现问题出在两点第一SN码是奇数个字符最后一个寄存器只有一个有效ASCII字符另一个字节是空位而设备在空位里填充了0x00MCGS解析时把0x00当成了结束符后面的内容全部被丢弃第二字节顺序和设备手册写的不一致。处理方式是在脚本里做容错先读出寄存器数组手动拼成字符串然后去掉末尾的0x00再检查长度是否和预期一致。只有长度一致时才更新画面变量避免显示半截数据。7.4 现象触摸屏IP能ping通但MCGS还是不通讯这是网络环境问题里最典型的一种。IP能ping通只说明链路层通不代表应用层端口502也通。检查三步在PC上用Modbus Poll模拟主站填触摸屏的IP地址和从站的IP地址看是否能通讯。如果PC能通说明网络没有问题问题出在触摸屏配置。在PC的命令行用telnet测试从站的502端口是否开放telnet 192.168.1.20 502。如果端口不通很可能是从站设备的防火墙或网关做了端口拦截。确认触摸屏和从站是否在同一VLAN。有些现场有多个交换机不同VLAN之间默认隔离你需要在交换机上放通502端口。有一次折腾半天最后发现是交换机上开了端口隔离PC能ping通但是502端口被隔离了放通之后一切正常。就是这种看起来网络通但实际上不通的情况最坑人。7.5 现象寄存器写入成功但触摸屏重启后数值还原这种问题一般发生在需要掉电保持的场景。很多PLC的寄存器分两种一种是RAM区断电丢失一种是掉电保持区断电不丢失。如果你写入的是RAM区寄存器触摸屏重启或者PLC断电重启数值自然恢复默认。解决办法是确认设备手册里的掉电保持寄存器范围写入时避开地址不在保持区的寄存器。如果设备不支持保持区就得由触摸屏侧做上电初始化重新写入的逻辑在脚本启动时把所有关键参数重新下发一遍。8. 项目落地前我建议你先做的三件事最后分享几个我从多次实践中沉淀下来的习惯不敢说有多高级但确实帮我省了很多现场时间。第一先建一个独立的调试页面。在这个页面上放几个文本显示框和输入框直接绑定你最关心的原始寄存器数值。开发和调试的时候永远不要依赖最终画面因为最终画面做了太多量程转换和逻辑判断反而掩盖了问题本身。调试页面上看到的是最原始的值能立刻判断通讯和寄存器映射是不是正确。第二每一个Modbus TCP从站都要记录它的IP、单元ID、寄存器地址表、字节顺序、数据类型。我习惯用Excel做一张寄存器映射表然后打印出来贴在现场电控柜里。这张表的价值在后面维护时能体现出来——现场电气工程师换设备后靠这张表几分钟就能完成参数恢复。第三给触摸屏配置一个通讯状态变量。这个变量绑定的寄存器可以由专门的通讯状态字提供也可以由脚本根据通讯故障标志位来更新。画面顶部放一个小指示灯绿色表示通讯正常红色表示异常这样操作工不用等数据错了才发现问题能第一时间报故障。Modbus TCP这套东西难点真的不在于协议本身而在于它和现场设备的匹配过程。寄存器地址、字节顺序、数据类型、轮询周期每一个参数都是一道关卡过了就通了。希望这篇内容能帮你少走几步弯路如果你也在做类似的项目欢迎按这个思路去排查你自己的工程。
返回列表