前阵子去现场处理一套自助洗车设备,客户反复强调“扫码枪扫了码,钱也扣了,但PLC就是没动作”。我蹲在控制柜前,先把PLC的通信参数调出来看了一眼:波特率19200、偶校验;再翻出扫码支付盒子的说明书,默认是9600、8N1。问题一目了然——两边的“暗号”都没对上,Modbus报文发得再标准也白搭。这种场景这几年越来越多:自动售货机、充电桩、共享设备、自助加工站,都开始要求PLC直接对接扫码支付模块,而用得最多的就是串口加Modbus RTU。这篇文章就把整条链路从物理层到应用层拆开讲,包括怎么接线、怎么定参数、怎么读报文、怎么写PLC程序、怎么用调试工具排雷,适合现场电气工程师、自动化集成商和设备维护人员照着做。
1. 先搞清现场架构:支付模块与PLC之间到底谁该说什么
1.1 两种典型的扫码支付设备形态
市面上能跟PLC直接通信的扫码支付设备,大致分两类。一类是一体式支付盒,很多自助设备上都能看到,体积不大,带RS232或RS485接口,有些还直接支持Modbus RTU从站。这种盒子内部集成了扫码模块和支付通道,用户扫完码,支付平台确认之后,盒子内部的状态寄存器会变化,PLC作为主站去读这个寄存器就行。
另一类是“扫码枪+网关/触摸屏”的组合。扫码枪通过USB或串口接在触摸屏上(常见的是威纶通、昆仑通态),触摸屏做协议转换,再把扫码结果通过Modbus从站方式暴露给PLC。这种架构的好处是不用单独采购支付盒子,适合本来就有触摸屏的老设备改造,坏处是中间多了一层,出问题时排查链路更长,触摸屏的刷新周期也可能拖慢响应速度。
1.2 为什么串口加Modbus是主流,而不是以太网
很多人一上来就问“能不能用TCP/IP”,答案是可以,但串口和Modbus RTU仍是目前最稳的组合。原因很现实:很多中小型PLC的低端型号没有以太网口,但几乎每一个都至少留了一个串口;串口线缆成本低,抗现场干扰能力也不差;Modbus是公开协议,支付模块厂商基本都支持,不像某些私有协议那样还要另付授权费。
以太网方案要操心的事情更多:IP地址规划、跨网段访问、交换机端口配置、防火墙拦截、虚拟机网络模式,任何一个环节出问题,现场调试就变成拉锯战。串口只要把波特率、校验位这些参数对上,一根线就能通,维护门槛低得多。所以除非客户明确要求远程监管和联网对账,否则我一般首选串口方案。
1.3 一单扫码支付的数据流向
把整条链路画出来,各位就清楚PLC在里面扮演什么角色了。用户扫二维码之后,支付平台扣款成功,支付模块内部逻辑会把某个状态寄存器从0改成1,同时可能写入订单号、支付金额等信息。PLC作为Modbus主站,按一定周期轮询这个寄存器,发现状态变化后,就会去置位输出点,控制继电器、电锁、电机或者语音播报模块。
这里有个特别容易忽视的点:PLC是“主动去读”的,不是等中断,所以轮询周期必须合理。太短了,通信把PLC扫描周期拖长;太长了,用户扫完码半天没反应。我一般把轮询间隔设在300到500毫秒,既能保证响应速度,又不会给PLC和支付模块造成负担。
2. 串口物理层与通信参数:先让两边对上暗号
2.1 RS232与RS485怎么选
串口通信的物理层基本就是RS232和RS485两种,选错了后面全白搭。RS232是全双工,三根线就能通信(TXD、RXD、GND),简单直接,但抗干扰能力弱,传输距离一般不超过15米,适合控制柜内几十厘米到两三米的场景。RS485是半双工,用A/B两根差分线传输,抗干扰能力强,理论距离能到1000米以上,适合从控制柜到户外支付模块这种跨距离的布线。
| 对比项 | RS232 | RS485 |
|---|---|---|
| 传输方向 | 全双工 | 半双工 |
| 接线方式 | TXD、RXD、GND | A、B差分线 |
| 最大距离 | 约15米 | 1200米(实际看波特率) |
| 抗干扰能力 | 一般 | 强 |
| 典型应用 | 电柜内短距离 | 跨机台、户外设备 |
选型经验就一条:支付模块和PLC在同一个控制柜里,用RS232;要跨到柜外、距离超过10米,或者现场有大功率设备启停,用RS485。用RS485时记得A接A、B接B,有些设备标的是D+、D-,别接反了。
2.2 波特率、校验位、停止位,一个都不能错
串口通信参数就像两个人对暗号,波特率、数据位、校验位、停止位四项必须完全一致,否则收到的都是乱码或者干脆没响应。波特率是每秒传输的比特数,最常见的是9600和19200;数据位一般是8,个别老设备用7;校验位有N(无校验)、E(偶校验)、O(奇校验);停止位一般是1,特殊情况下用2。
在扫码支付这种场景,默认从9600、8、N、1开始尝试成功率最高。但也别迷信这个默认值,我遇到过某个支付盒子出厂是19200、8、E、1,如果直接套9600、8N1,PLC永远是超时。动手接线之前,第一件事就是翻设备手册,或者用配置软件把协议参数读出来。如果支付模块支持配置,我习惯统一改成9600、8N1,这样PLC程序里写死也不会后期出乱子。
2.3 USB转串口、驱动器与COM号的那些坑
现场调试你不可能扛着一台带原生串口的电脑,USB转串口模块是必备工具。但很多问题恰恰出在这个小模块上。芯片方案最常见的两种:CH340和FTDI。CH340便宜,某些缩水版稳定性一般;FTDI兼容性好但价格高,而且假货多。Win10以上系统一般能自动装驱动,但有时候系统更新会把驱动搞坏,设备管理器里显示黄色的感叹号,串口自然打不开。
还有一个高频坑是COM号漂移。同一块USB转串口,今天插前面板是COM3,明天插后面板变成COM9,调试软件里选半天才发现选错口。解决办法是在设备管理器的端口设置里,把“COM端口号”固定成一个不太常用的号码(比如COM15),省得来回确认。“串口烧写失败”这个现象,十有七八不是PLC的问题,而是驱动没装好、COM号被蓝牙虚拟串口占用,或者下载软件的波特率设得太高。
3. Modbus RTU报文拆解:读懂支付模块返回的那几个字节
3.1 一条完整报文的逐字节拆解
Modbus RTU的帧结构不复杂,一共四段:从站地址、功能码、数据区、CRC校验,RTU格式要求字节之间连续传输,中间停顿不能超过1.5个字符时间,否则接收方会认为帧结束。
举例,PLC要向站号为1的支付模块读取地址0x0001的保持寄存器(假设这个寄存器就是支付状态),请求帧是这样的(十六进制):
01 03 00 01 00 01 D5 CA- 01:从站地址,表示发给1号设备
- 03:功能码,读保持寄存器
- 00 01:起始寄存器地址
- 00 01:要读1个寄存器
- D5 CA:CRC16校验值,低字节在前
正常情况下,支付模块会回复这样一帧:
01 03 02 00 01 79 84- 01:从站地址
- 03:功能码
- 02:返回数据字节数(2字节)
- 00 01:寄存器值,这里表示支付成功
- 79 84:CRC
如果PLC读到0x0001,就可以触发后面的动作了。
3.2 CRC16校验是怎么算出来的
Modbus RTU用的是CRC16-IBM/MODBUS校验,多项式是0xA001(反转后),低字节先发。算起来不复杂,但书面手算很麻烦,一般用查表法或在线工具。如果在PLC里用自由口自己拼报文,就需要在程序里实现一个CRC功能块。核心思路很简单:把待校验的每个字节,先跟CRC寄存器异或,然后右移8次,每次根据最低位决定是否跟0xA001异或。
这里要特别提醒新手:CRC低字节在前,高字节在后,所以在报文里先看到的是D5,后看到的是CA。搞反了,设备会报错或者直接忽略帧,这是自由口通信里最隐蔽的坑之一。
3.3 功能码和寄存器映射才是核心
扫码支付模块的手册一般会给出功能码和寄存器地址表。最常见的功能码有三个:03读保持寄存器、06写单个寄存器、16(0x10)写多个寄存器。支付模块作为从站时,PLC主要用03去读状态、订单号;有时候PLC要告诉支付模块“我已经确认过了”,就会用06或16去写寄存器。
寄存器映射是另一个高发误区。很多PLC通信指令里的“DATA_ADDR”和Modbus协议地址不是同一个值。举个例子,西门子S7-1200的MB_MASTER指令,Modbus地址40001对应协议地址0x0000,40002对应0x0001。手册上写“支付状态在寄存器0x0001”,你在DATA_ADDR里填40002,而不是40001或01。三菱和台达的PLC也有类似的偏移,踩之前先看清指令手册的地址映射表。
3.4 Modbus模拟器该用在哪一步
Modbus Poll和Modbus Slave是调试Modbus设备的两大神器。Poll模拟主站,用来主动发起读写请求;Slave模拟从站,用来假装自己是支付模块。网上确实流传各种版本的“密钥”或注册码,但说实话,官方试用版或者开源替代品(比如QModMaster)应付现场调试绰绰有余。没必要把宝贵时间花在折腾破解上,省下的时间多检查一遍接线更值钱。
调试时最实用的操作,是用Modbus Slave模拟支付模块,先在电脑上验证PLC的程序逻辑。把Slave的从站地址设成1,保持寄存器0x0001填1,然后让PLC去读。PLC读到1能触发输出,就说明程序逻辑没问题,剩下的事就是核对现场物理链路。
4. PLC侧程序实现:走Modbus库还是自由口
4.1 自带Modbus主站指令的用法
现在主流PLC基本都自带Modbus主站功能块,最省事,也最不容易出错。西门子S7-1200在TIA Portal里要先用MB_COMM_LOAD配置串口参数,再用MB_MASTER发起读写。MB_MASTER的关键参数就几项:REQ触发位、RW方向、MODE功能模式、DATA_ADDR数据地址、DATA_LEN数据长度、DATA_PTR数据存放地址、STATUS错误代码。拉一个M0.5的100ms脉冲出来当REQ,轮询就建好了。
三菱FX系列用ADPRW指令,格式大概是:
ADPRW D10 K1 H3 D20 K1意思是读站号K1的保持寄存器(H3),把结果放到D20开始的1个字里。台达、汇川的PLC则用MODRW指令,用法和三菱类似,只是操作数排列不一样。这类指令的好处是CRC、帧封装、超时处理全帮你做了,只要地址和参数填对,一次就能通。
4.2 自由口通信自己拼报文
老PLC或者一些国产特殊设备没有现成Modbus库,就得走自由口通信。西门子S7-200 SMART用XMT/RCV指令,三菱FX用RS指令。用自由口,等于把Modbus报文拆成字节自己组装,然后通过发送指令发出去,再通过接收中断或接收完成标志拿回应答。
拿三菱举例,你用RS指令前,得先把特殊寄存器D8120设置成通信格式,比如波特率9600、8位数据、无校验、1位停止位。程序大致思路是:准备发送缓冲区(从站地址、功能码、寄存器地址、数量、CRC),置位发送请求,等到接收完成标志位动作后,再解析接收缓冲区里的响应帧。中间还要处理超时,比如D8129是通信超时设定,超过了就置位异常标志。
这里我要强烈建议:除非你对CRC计算和状态机处理已经很熟,否则只在PLC没有Modbus库的时候才走自由口。自由口代码调试起来比指令块麻烦得多,尤其是CRC算错或高低字节颠倒,问题会很隐蔽。
4.3 轮询逻辑和支付完成标志的处理
不管用哪种路线,PLC程序里都要有几个关键逻辑。定时触发:不要让通信指令每个扫描周期都执行,用定时器或沿脉冲控制,我一般设在300到500ms周期。状态判断:通信指令会返回错误状态,正常时处理数据,异常时重试,连续3次异常就报警给HMI。
支付完成标志的处理更要细心。PLC读到支付状态为1后,通常要执行某个动作(开门、启动电机等),这个动作在商业上要求“只能执行一次”。所以必须用上升沿检测指令触发,而不是单纯看状态值是否为1。否则在最后一次扫描后如果PLC重启,而支付模块寄存器里的状态没复位,设备就会再动作一次,这在自助设备上会造成严重的重复扣款问题。我的习惯是:PLC确认支付成功后,立即通过写寄存器指令把该状态位清零,相当于给支付模块一个“我已经收到”的确认信号。
4.4 关于AI生成PLC代码的一点提醒
现在网上很多人用AI工具生成PLC代码,这在某些常见需求上确实能提速,比如生成CRC计算函数、Modbus轮询框架。但AI对具体PLC品牌指令的记忆经常出错,特别是老型号三菱FX的软元件编号、统合口设置这类细节。用AI生成的代码,必须对照指令手册逐项核对地址、功能码和通信参数,别直接下载到PLC里就能指望跑起来,我见过太多AI生成代码里寄存器地址偏移算错的情况。
5. 联调实战:用串口助手和Modbus模拟器把问题压在桌面上
5.1 用串口调试助手单独验证支付设备
到了现场,别直接上PLC,先拿电脑接支付模块,用串口调试助手单独测。步骤很固定:USB转串口插电脑,设备管理器确认COM号,打开串口调试助手,选择COM号,设置波特率9600、8N1,打开串口。然后在发送区输入Modbus RTU请求帧,例如读寄存器0x0001的请求:01 03 00 01 00 01 D5 CA,点发送,看接收区有没有返回。
如果返回的是01 03 02 00 01 79 84,说明设备物理层和协议层都正常。如果不返回,先用万用表量一下RXD、TXD电压;用RS485时量A、B之间的电压,静止状态应该在1.5V到5V之间。把问题定位在“模块坏了”“线没接对”“参数不对”三者之一,再上PLC就不至于一头雾水。
5.2 Modbus Slave模拟支付设备,先骗过PLC
如果你在开发阶段,或者支付模块还没发货,可以用Modbus Slave在电脑上模拟一个支付模块。打开Modbus Slave,新建个从站连接,从站地址设1,功能码选03 Holding Register,起始地址0x0000,创建一个寄存器表。然后把0x0001这个寄存器的值改成1,保存。
PLC程序里指向从站地址1,读取地址40002(对应0x0001)。运行PLC,如果程序能触发输出,说明你的通信组态、数据地址、程序逻辑都是对的。这种方法在项目交付之前验证程序和HMI画面特别有用,能避免设备到场后才发现底层地址全错。
5.3 抓包定位,看异常码就知道问题出在哪
如果接上真实设备后怎么都不通,就要抓包分析。笔记本电脑上可以用虚拟串口工具(比如Virtual Serial Port Driver)把物理串口复制一份,一个窗口自己发数据,另一个窗口监听收发内容。或者直接买一个带串口监听的调试工具。抓包的目的就是看PLC发出的请求帧和收到的响应帧到底是什么样。
如果收到异常响应,Modbus协议里有标准异常码,最常见三个:
| 异常码 | 含义 | 常见原因 |
|---|---|---|
| 01 | 非法功能 | 设备不支持该功能码,或没开放该命令 |
| 02 | 非法数据地址 | 寄存器地址越界或偏移算错 |
| 03 | 非法数据值 | 请求中的数据值超出允许范围 |
有一次我在现场看到PLC一直报超时,抓包发现请求帧CRC算错了。原因是那次用的自由口程序,低字节和高字节颠倒。纠正CRC计算后,通信立刻恢复正常。这就是抓包的价值:它能帮你把问题从“玄学”变成“具体错误”。
6. 现场高频故障排查:从串口烧写失败到数据丢帧
6.1 一张表看清常见故障
以前现场跑多了,我把高频故障都汇总成一张表,每次排查对着看:
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| PLC报通信超时 | 接线错误、串口参数不一致、从站地址错 | 用串口助手直连设备,核对参数 |
| 收到数据全是乱码 | 波特率/校验位不一致、共地不良 | 重新确认参数,确保RS232共地 |
| 偶发超时或数据跳变 | 干扰、终端电阻缺失、线缆过长 | 换屏蔽双绞线,加120Ω终端电阻 |
| 串口打开失败 | 驱动异常、COM号冲突 | 重装驱动,固定COM号 |
| 程序能查但下载失败 | 串口被调试软件占用、PLC处于RUN状态 | 释放端口,需要时切到STOP |
| 支付状态重复触发 | 没做上升沿检测、状态寄存器未清零 | 程序增加沿触发,写确认寄存器 |
6.2 串口烧写失败,并不总是程序的问题
“串口烧写失败”这个词其实很泛,有给PLC下载程序失败的,也有给某些单片机模块烧录出错的。在PLC这里,我遇到最多的情况是USB转串口驱动出了问题,或者COM号被占用。Win10之后,系统更新可能导致CH340驱动文件被替换,设备管理器出现感叹号,这时候重新装一次驱动就好。如果是FTDI芯片的模块,更要留意是不是装到了仿冒芯片的驱动上。
还有一种情况是在虚拟机里跑编程软件。很多人用VMware装TIA博途,结果下载时连不上PLC。网络模式要选桥接(Bridged),虚拟机的IP地址要和PLC在同一网段,Windows防火墙里面放行软件的所有通信。如果是串口下载,则要记得在虚拟机设置里把USB转串口设备“连接”到虚拟机,不能在物理机和虚拟机之间抢这个USB设备。
6.3 485丢帧和干扰的根治方法
RS485链路用起来比RS232复杂,最容易出现的问题就是距离一长就丢帧。丢帧的根源往往是阻抗不匹配和线缆质量问题,而不是Modbus协议的问题。所以当PLC偶发超时、数据抖动时,第一反应不是改波特率,而是先检查线缆和终端电阻。
正确做法是:用屏蔽双绞线,屏蔽层单端接地(一般接地端在PLC电柜内),首尾两端各并联一个120欧姆终端电阻,尽量远离动力电缆,不要跟电机线、变频器输出线走同一个线槽。我处理过一台室外充电桩,支付模块在立柱上,和PLC相距40米,用的还是普通平行线,每十几分钟就超时一次。后来换成屏蔽双绞线、两端加120Ω匹配电阻,再调低到9600波特率,连续运行一周没有一次超时。现场环境复杂时,物理层永远要先于应用层怀疑。
6.4 我自己的排查顺序
调试这类系统,我给自己定了一个固定顺序,基本没翻过车。第一步,核对串口参数,把电脑直连设备,确认设备是好的、协议是通的。第二步,隔离物理层,用万用表量接线端子有无松动、电压是否正常、485的A/B有没有接反。第三步,上PLC但只读一个寄存器,用最简单的功能码,排除程序逻辑干扰。第四步,观察现象,抓包看收发帧,根据异常码定位。最后,再做真正的支付流程联调。
这套顺序的核心逻辑,是“先物理层再协议层,先简单后复杂”。很多工程师一上来就翻PLC程序,其实是本末倒置,因为大部分“通信不上的问题”,根源都藏在USB驱动、COM号、波特率、A/B反接这些看起来不起眼的地方。把这几个基本点焊死,Modbus这条链路的稳定性自然就上来了。
最后再分享一个我常用的土办法:遇到死活调不通的情况,先把支付模块当一个独立的Modbus设备来“盘”一遍,用串口调试助手把它支持的所有寄存器都读一遍,搞明白它每个地址的行为,然后再回到PLC侧用一个最简单的指令只读一个寄存器。通了,再加功能。这个“由简到繁”的思路,能让你少走很多弯路,也最适合发给带你的徒弟慢慢练手。