RS485通信调试到头来发现是参数里一个勾选项惹的祸——这种经历我遇到过不止一次。三菱FX5U本体自带的那颗RS485口,做ASCII无顺序通信时,看着配置项不多,可从帧拼装到完成标志处理,每一步都有它自己的脾气。这篇就围绕FX5U的RS485 ASCII无顺序通信,把参数配置、RS2指令运行机制、ASCII帧拼装、校验和计算、接线处理以及一次完整的联调过程摊开讲,适合刚上手FX5U通信、或者用过FX3U想迁移到FX5U的同行参考,也适合手头正对着一台只认ASCII私有协议的第三方设备发愁的朋友。
1. 先摸清FX5U内置RS485口的能力边界
在动手接线和写程序之前,有必要先明确这颗口的定位。FX5U的CPU单元标配RS485端口,端子是SDA、SDB、RDA、RDB、SG、FG这一组,支持2线制和4线制,最高速率可以到115200bps。它和扩展板、扩展适配器挂出来的485口在指令层面走同一套逻辑,只是通道号不同。很多人第一次接触会把它和FX3U的通信扩展板混为一谈,实际编程时操作数顺序、完成标志位的分布都有差异,这一点后面会专门说。
1.1 无顺序通信、MC协议与MODBUS RTU的取舍
FX5U的485口能干三件事,选哪一件完全取决于对面那台设备。
**无顺序通信(无协议通信)**是自由度最高的一种,帧格式自己定,帧头、站号、命令码、数据区、校验和、结束符全凭对方设备的协议文档说了算。代价是要自己拼帧、自己算校验、自己解析响应。遇到扫码器、称重仪表、老式温控表、自定义协议的变频器,基本只能走这条路。
MC协议是三菱自家的一套通信规范,主要用来和上位机、触摸屏、其他三菱PLC交换软元件数据。它不适合对接第三方设备,除非对方明确说自己支持MC协议。
MODBUS RTU是通用工业标准,FX5U提供ADPRW指令专门做MODBUS主站,参数配置好之后读写从站寄存器只需要一条指令,省掉大量拼帧工作。对面设备如果支持MODBUS,我个人的建议是优先用它,能省一大半调试时间。
判断逻辑很简单:对方协议文档里出现"MODBUS RTU"字样就直接走MODBUS;出现的是自定义命令表、帧头帧尾、校验和算法说明,就走无顺序通信。ASCII和二进制则是在无顺序通信内部的两种数据形态选择。
1.2 ASCII与二进制:数据形态差异决定了程序写法
这两个概念的差别,说白了就是"用字符表示数字"还是"用数字本身表示数字"。
要发送数值1234,二进制方式下这是两个字节0x04D2,紧凑、传输量小,但你在串口助手里看到的是一堆乱码。ASCII方式下则是四个字符'1''2''3''4',对应0x31、0x32、0x33、0x34,占用四个字节,传输量翻倍,但抓包时肉眼就能读懂整帧内容,出问题时对比协议文档非常直观。
FX5U在参数里选择ASCII模式后,RS2指令的发送和接收都以"字节"为单位处理数据,而PLC的软元件是16位的,一个寄存器刚好装两个ASCII字符。**发送时先发寄存器的低字节,再发高字节;接收时先收到的字符落在低字节,后收到的落在高字节。**这个顺序贯穿始终,拼帧和解析时一旦记反,整帧数据就会错位。
ASCII模式的另一个隐性成本是数据转换。你想发一个十进制数100,不能直接把K100塞进发送缓冲区,得先把它拆成'1''0''0'三个ASCII码。同理接收到的"012345"也要还原成数值参与运算。这部分工作FX5U有现成指令可以用,但逻辑得自己理清楚,后面第4节会展开。
二进制模式并非不能用,只是对接第三方ASCII设备时用不上。反过来,如果对面是老式三菱设备或者某些支持二进制帧的仪表,二进制模式传输效率确实更高。选哪种不由喜好决定,协议文档写的是ASCII,你就只能按ASCII来。
2. 通信参数的每一项都不是随便填的
参数配置是无顺序通信里最不起眼、却最容易出问题的环节。GX Works3里那一页参数,看起来就是几个下拉框,但每一项填错都会导致通信完全不通,而且现象往往一模一样——收不到任何数据。所以有必要把每一项的作用和常见取值讲透。
2.1 GX Works3里的模块参数逐项拆解
配置入口在导航窗口:参数 → FX5UCPU → 模块参数 → 485通信 → 通信协议设置。这里选"无顺序通信"之后,会展开一组通信格式参数。
| 参数项 | 常见取值 | 说明 |
|---|---|---|
| 数据长度 | 7位 / 8位 | 绝大多数设备用8位,7位只在少数老设备上出现 |
| 奇偶校验 | 无 / 奇数 / 偶数 | 必须和对侧完全一致,这是最常见的通信失败原因之一 |
| 停止位 | 1位 / 2位 | 一般用1位,长距离或干扰大的场合部分设备要求2位 |
| 传送速度 | 9600~115200bps | 与对侧一致,速率越高允许的通信距离越短 |
| 校验和 | 有 / 无 | 勾选后FX5U自动追加和校验,算法必须和对侧一致 |
| 结束符 | 无 / CR / CR+LF | 决定接收何时判定帧结束 |
| 接收超时 | 可设定 | 超时后接收强制结束,避免程序一直挂等 |
数据长度、校验、停止位、波特率这四项是"四件套",任何一项不一致,物理层看起来连通了,数据也收不进来。调试时我习惯把这四项写在一张纸上,和对方设备说明书逐项划线核对,比凭记忆确认靠谱得多。
校验和与结束符这两项有个非常关键的联动特性,很多人栽在这里:**勾选"校验和"之后,FX5U在发送时会自动在数据末尾追加两位校验字符,接收时会自动校验并剥离;勾选"结束符"之后,发送时FX5U同样会自动补上结束符。**如果你的程序里已经手动拼好了校验和和CRLF,参数里又开着自动追加,对方收到的帧尾部就会多出几个字符,协议解析直接失败。
2.2 结束符、校验和、接收超时——三组最容易踩的设置
先说结束符。接收侧的判定条件有三种,满足任意一种接收就结束:接收字节数达到设定值、检测到设定的结束符、接收超时。对于响应长度不固定的设备,把接收点数设得比最大可能长度还大,然后靠结束符来判定帧尾,是最稳妥的做法。比如某台仪表响应长度在18到26字节之间浮动,接收点数设成64,结束符设CR+LF,收到CRLF立即结束,不会傻等到64字节。
再说校验和。三菱无顺序通信内置的校验和算法是:对发送数据的全部字节做累加,取低8位,转成两位十六进制ASCII字符。问题在于,市面上设备的校验算法五花八门——有累加和取低8位的,有累加和取反的,有异或和的,还有把站号排除在外的。**只要对方的算法和FX5U内置的不完全一致,就必须把"校验和"设成"无",自己在程序里算好再拼进帧里。**我见过有人为了省事强行勾选,结果收发数据都对、就是对方拒收,折腾一整天。
接收超时这一项容易被忽略。默认值偏保守,如果对方设备响应本来就慢,比如某些仪表需要200ms以上才回应,超时设得太短会频繁触发接收异常结束,程序里表现为时好时坏。反过来,超时设得太长,对方掉线时程序会长时间挂起,轮询节奏被打乱。合理做法是实测对方典型响应时间,超时设成它的两到三倍。
还有两个不常用的选项:存在确认和附加代码。前者用于特定设备的存在检测握手,后者用于追加附加字符。绝大多数通用设备用不到,保持"无"即可。
2.3 什么时候该直接动SD寄存器
用GX Works3参数配置的好处是直观、可存档、随程序一起下载。但有两种场合必须改用SD寄存器直接写:
第一种是需要在运行中动态切换通信参数。比如同一条485总线上挂了两种波特率不同的设备,得靠程序分时切换。参数界面是静态的,做不到这一点,只能用MOV指令在初始化时往对应的SD寄存器写值。
第二种是程序移植。从别的项目拷过来的程序,如果对方是用SD寄存器初始化的,你得跟着保持一致,否则两套配置会冲突——参数界面的设定和程序写入的SD值谁生效,取决于执行顺序,非常容易出鬼。
FX5U内置RS485的通信设置对应的系统寄存器集中在SD10680起始的一段区域,分别覆盖通信协议、数据长度与校验位、传送速度、校验和与结束符控制、接收超时这几类。具体编号随固件版本可能有细微差别,动手前对照一下当前机型的手册通信篇附录。写法上通常是在初始化脉冲M8002下用一组MOV指令一次性写入,注意程序里写了SD之后,参数界面里对应的项就会被覆盖,两边不要同时设。
提示:如果项目里既有参数配置又有SD写入,建议二选一。团队协作时更推荐统一走参数配置,配置随工程文件走,接手的人打开就能看到,比在梯形图里翻MOV指令快得多。
3. RS2指令的运行机制与完成标志处理
帧拼好了、参数设对了,接下来就是指令怎么用。FX5U做485无顺序通信的核心指令是RS2,一条指令同时涵盖发送和接收两个动作。它的操作数看着不多,但每个的含义和相互关系必须搞清楚,否则程序能编译通过、跑起来却收发全乱。
3.1 五个操作数的分工
RS2的指令格式是:RS2 (S1) (S2) (S3) (D1) (D2)。
- S1:发送数据的起始软元件。你拼好的ASCII帧从这里开始存放。
- S2:接收数据的存放起始软元件。对方回的数据会填到这里。
- S3:控制数据起始软元件。S3+0存放发送字节数,S3+1存放接收字节数,两个都是字节单位,不是寄存器个数。
- D1:完成标志起始软元件。D1+0是发送完成,D1+1是接收完成,D1+2是异常结束。
- D2:通信通道号。FX5U内置485口用K1,扩展板用K2,扩展适配器用K3。
这里有个高频错误点:S3的发送点数和接收点数单位是字节。发送"@01RD0000"加校验和加CRLF一共13个字节,S3+0就写13,而不是占用的寄存器个数7。我见过有人按寄存器个数填,结果只发出一半的帧,对端自然没反应。
另一个容易混淆的地方是D2通道号。老款机型上通道号的写法和FX5U不完全一样,从FX3U迁过来的人如果不看手册直接照抄,很容易把K1写成别的值,指令照常执行但数据从错误的通道走,现象就是"程序没报错,就是没数据"。
3.2 重复触发与标志清除的实际写法
RS2是连续执行型指令,执行条件保持ON的情况下,一次收发完成后会再次启动。这意味着如果你用一个常开的X0去驱动它,总线上会不停地重复发送同一帧,对方要么被刷屏、要么直接判为通信异常。
正确的触发方式有两种。最简单的是用上升沿:
LDP X0 RS2 D100 D500 D300 M200 K1X0每闭合一次执行一轮收发。但如果需要周期性轮询,用时钟脉冲更省事:
LDP M8013 AND M10 RS2 D100 D500 D300 M200 K1M8013是1秒时钟,上升沿触发一次,配合M10作为通信使能位,整个轮询节奏清晰可控。轮询周期要根据对方设备的响应能力和总线负载来定,一条总线上挂的设备越多、单帧越长,周期就得放得越宽。
完成标志的处理同样不能马虎。M200在发送完成后置ON并保持,直到下一次执行RS2或者用RST清除。如果程序里靠判断M200来触发后续逻辑,那下一步动作执行完后必须把它复位,否则下一轮轮询会误判。我的习惯写法是:
LD M200 RST M200 '此处放发送完成后的处理逻辑接收完成标志M201和异常标志M202同理。异常标志M202置ON说明这一轮收发出了问题,具体原因要结合通信异常代码判断,常见的有超时、校验错误、帧错误几类。程序里对M202的处理不要只做报警,最好加一个重试计数,连续失败若干次再停机报警,能过滤掉偶发的干扰导致的单次失败。
3.3 接收结束条件:点数与结束符谁优先
接收什么时候算结束,是新手最容易误判的地方。三个条件——接收点数达到设定值、检测到结束符、接收超时——任意一个满足就结束,不存在优先级之分。
这个机制意味着两件事。第一,如果你把接收点数设成实际响应的精确字节数,一旦对方某次多回了一个字节,你会一直等到超时才结束,程序里表现为通信周期偶尔变长。第二,如果设了结束符,接收点数可以放心设大,实际收到结束符就结束了,多出来的空间不会被填满。
我一般把接收点数设成协议文档里最大响应长度的1.5倍到2倍,同时确保结束符设置和对方一致。这样正常响应靠结束符判定,异常情况下由超时兜底,两头都不会卡死。
还有一个细节:接收到的数据不足设定的点数时,S2起始区域后面没被填到的寄存器保持原值不变。解析响应时不能假设整个接收区都是有效数据,得靠接收完成时的实际字节数(有些场合需要自己统计)或者结束符位置来判断有效长度。用STR指令把接收区转字符串显示时,如果不做长度截断,会把上一次的残留数据一起显示出来,看起来像是收到了奇怪的内容,其实是缓冲区没清。
4. ASCII帧的拼装与校验和计算
到了真正动手拼帧的环节。ASCII通信的程序量比二进制大不少,核心原因是每个数值都要转成字符。FX5U提供了一组字符串和ASCII转换指令,用对了能省很多事。
4.1 字符串指令ASC在拼帧中的用法
FX5U支持在指令里直接写字符串常量。比如要发送"@01RD0000",最直接的写法是:
$MOV "@01RD0000" D100执行后D100到D104就被填上了对应的ASCII码。具体分布是:D100低字节0x40('@')、高字节0x30('0');D101低字节0x31('1')、高字节0x52('R');依次类推。九个字符占用五个寄存器,最后一个寄存器只有低字节有效。
这里要特别注意字符串在寄存器中的存储顺序和发送顺序是对应的:RS2发送时先发D100的低字节0x40,再发高字节0x30,正好就是"@0"两个字符。所以$MOV写进去之后不需要再做任何字节交换,直接发给RS2就行。这一点让不少人省了心,因为按二进制思维很容易怀疑是不是要交换高低字节。
如果帧内容需要在运行时动态生成,比如站号要跟着配方变,就不能用字符串常亮了。这时候可以用ASC指令把字符串软元件转成ASCII码,或者干脆用MOV逐字节拼。站号是两位数的情况下,用除法取商和余数分别加0x30,两三条指令就能算出来,比调字符串指令还快。
帧头的@、命令码的R和D这些固定内容,用$MOV一次性写进去最省事。地址域如果固定,也一并写;如果地址需要变化,就单独用MOV覆盖对应寄存器。
4.2 累加和校验的完整计算与ASCII化
假设协议要求:帧为@+ 站号(2位) + 命令(2位) + 地址(4位) + 校验和(2位) + CRLF,校验和是对@到地址末位这9个字节做累加、取低8位、转两位十六进制大写ASCII。
第一步求字节和。FX5U有按字节求和的指令BSUM,格式是BSUM (S) (D) (n),对起始软元件S开始的n个字节求和,结果存D。这里n写9,起始写D100:
BSUM D100 D110 K9结果D110里是9个字节的累加和,可能超过255。第二步取低8位:
AND D110 HFF D111D111的低8位就是校验值,高8位清零。
第三步把D111拆成两个十六进制字符。方法是用除法和掩码:D111除以16取商得到高4位,D111和0x0F做与运算得到低4位。然后两个半字节分别转ASCII——0到9加0x30,10到15加0x37。
逐条写比较啰嗦,我通常用一个查表法:预先在D500到D515里放好0到15对应的ASCII码(0x30到0x39、0x41到0x46),然后用变址寄存器取值。FX5U支持软元件加变址的写法:
MOV K0 Z0 MOV D111 D112 AND D112 H0F D113 '低4位 DIV D111 K16 D114 'D114存商,D115存余数 MOV D114 D116 AND D116 H0F D117 '高4位 MOV D500Z0 D120 '低4位查表 MOV D117 Z0 MOV D500Z0 D121 '高4位查表最后把D120、D121的低字节拼进帧的校验和位置。注意校验和是高4位字符在前、低4位字符在后。
注意:如果对方协议里的校验和算法是"累加和取反"或"异或和",就不能用BSUM直接算。取反的话在AND取低8位之后加一条异或0xFF;异或和的话得用一个循环逐字节异或,程序量会大一些,但逻辑同样清晰。动手前务必把协议文档里的公式抄在纸上,照着写。
4.3 高低字节顺序:一个寄存器装两个字符
这一节单独拿出来讲,是因为它在拼帧和解析两个环节都会咬人。
发送方向:寄存器低字节先发、高字节后发。所以往D104里写校验和的两个字符时,第一个字符(高4位对应的大写十六进制字符)应该放在低字节,第二个字符放在高字节。写反了对方算出来校验不通过,现象是"帧内容看着完全正确,就是被拒收"。
接收方向:先收到的字节落在低字节。响应帧"@01"进来,D500的低字节是0x40('@'),高字节是0x30('0')。如果响应长度是奇数,最后一个字节只占低字节,高字节保持原值,解析时要有意识地只取低字节。
拼接发送帧时还有一个长度奇偶的问题。整个帧13个字节,占用7个寄存器,最后一个寄存器D106只有低字节有效(0x0A,即LF),高字节是无效数据。发送点数写13,RS2只会发13个字节,D106的高字节不会被发出去,所以不用担心多余数据混进去。但如果这个寄存器之前被别的逻辑用过、高字节有残留值,虽然不影响发送,读程序的时候容易看花眼,初始化时清一下更清爽。
响应解析时同样是这个道理。假设响应18个字节,占用9个寄存器,第九个寄存器只有低字节是有效数据。用STR指令把D500起始的9个寄存器转成字符串,会得到18个字符的字符串,正好对应实际响应长度,不多不少。如果响应是17个字节,转出来的字符串第18位会是残留的旧数据,这时候就需要按已知长度截断。
5. 接线、终端电阻与现场抗干扰
程序写得再漂亮,线接错了照样不通。485接线的坑主要集中在制式选择和屏蔽接地这两块。
5.1 2线制与4线制的接法差异
FX5U的端子排上有SDA、SDB、RDA、RDB四个信号端,支持4线全双工和2线半双工。无顺序通信绝大多数场合用2线制,因为大部分第三方设备只支持2线。
2线制的接法是把FX5U的SDA和RDA短接、SDB和RDB短接,然后SDA/RDA这组接到对方的A(或标为+、T/R+),SDB/RDB这组接到对方的B(或标为-、T/R-)。A和B接反是最常见的问题,接反了不会烧设备,但通信完全不通,而且不同厂家对A、B的定义偶尔还会反过来,遇到完全没数据的情况,先换一下这两根线,往往就是它。
4线制用在需要全双工的场合,接线是交叉的:FX5U的SDA接对方RDA、SDB接对方RDB,FX5U的RDA接对方SDA、RDB接对方SDB。四根信号线之外,SG必须对接,这是信号参考地,不接的话两端地电位差会让通信变得极不稳定,表现为时通时不通、数据偶发错位。
终端电阻方面,2线制链路的两端各接一个110Ω电阻,中间节点不接。有些设备内置了120Ω终端电阻,通过拨码开关启用,这时候不要再外加,否则阻值并联后偏离设计值,信号质量反而变差。短距离、低波特率的场合不接终端电阻一般也能跑,但工业现场我建议还是按规范接上,省得后期偶发故障时再回头查。
5.2 屏蔽、接地与共地的处理
485用屏蔽双绞线是标配。屏蔽层的处理有个原则:单端接地。一般接在PLC柜的接地排或者FG端子上,另一端悬空。两端都接会形成地环路,反而引入干扰。
走线方面,通信线必须和动力线分开走,至少保持20厘米以上间距,实在避不开就垂直交叉。我遇到过一次偶发丢帧,查了两天参数和程序都没问题,最后发现通信线和某台设备的输出电缆在同一个线槽里并排走了三米多,分开走线之后故障率直接归零。这类问题在实验室里永远复现不出来,只有到现场才会现形。
通信距离和波特率是此消彼长的关系。9600bps下理论距离可以做到上千米,115200bps时建议控制在百米以内。距离长、节点多的场合,适当降速比什么都管用。
还有一点常被忽略:FX5U内置的485口不是隔离型。如果通信双方分处不同的配电柜、地电位差比较大,建议在链路中加隔离中继器,或者在选型阶段就用带隔离的通信扩展模块。隔离不是万能的,但在地电位差明显的现场,它能省掉大量莫名其妙的通信故障。
6. 一次完整的联调实录:从发不出到稳定收发
理论说完了,拿一个实际场景走一遍。通信对象是一台支持ASCII私有协议的称重仪表,要求PLC每隔一秒读一次当前重量。
6.1 协议帧拆解与程序分段
仪表手册给的读重量命令是:@+ 站号(2位) +RD+ 地址0000(4位) + 校验和(2位) + CRLF。校验和是@到地址末位共9个字节的累加和取低8位转两位大写十六进制ASCII。响应格式是:@+ 站号 +RD++或-+ 6位重量值 + 校验和 + CRLF,共19个字节。
站号设为01的情况下,帧头部分固定为"@01RD0000"。按第4节的算法,这9个字节的累加和是0xE7,转成ASCII就是'E'(0x45)和'7'(0x37)。完整发送帧13个字节:@01RD0000E7\r\n。
参数上,波特率9600、8位数据、无校验、1位停止位,校验和设"无"(因为要自己算),结束符设"无"(因为要手动拼CRLF,如果这里设了CR+LF,FX5U会自动再补一遍,对方收到两个CRLF)。
程序分三段写。第一段初始化,M8002下把发送缓冲区清空、把固定的帧头内容写进去、设定发送和接收点数。第二段拼校验和,用BSUM加查表法算出两个字符塞到D104。第三段是轮询主体,M8013上升沿触发RS2,完成标志和异常标志分别处理。
6.2 联调中出现的问题与定位过程
第一轮上电,串口助手挂在总线上监听,能看到PLC确实在发数据,但仪表毫无反应。抓到的帧是@01RD0000E7\r\n,逐字节对照手册,一个不差。问题出在参数——仪表的通信格式是8位数据、偶校验、1位停止位,而我按无校验配的。改成偶校验之后,仪表立刻有响应了。
第二轮,仪表回了数据,但PLC的接收完成标志M201始终不置ON,一直等到超时才结束。原因是我把接收点数设成了19,而仪表实际响应是18个字节——重量值没有负号的时候少了符号位那一个字符。接收点数改成64、结束符设成CR+LF,靠结束符判定,问题解决。
第三轮,运行半小时后开始偶发丢帧,异常标志M202大约每小时亮两三次。参数和程序都查过没问题,最后顺着线槽找,发现通信线和一台设备的动力电缆并排敷设。改道之后连续跑了三天没再出现。
第四轮是解析环节,接收缓冲区里拿到的重量值要还原成数值参与比较。这里用STR指令把接收区转成字符串,再从第5位开始截取6个字符,用VAL指令转成数值。注意截取前要确认响应的符号位是否存在,负数响应比正数多一个字符,不处理的话数值会整体错位。
6.3 稳定运行后的几点收尾工作
联调通过之后有几件事建议顺手做掉。
一是把通信异常的处理逻辑补全。单次失败重试三次,三次都失败才置报警位,同时在HMI上显示累计失败次数和最后一次的异常代码。这样现场出问题时,值班人员一眼就能看到是"从来没通过"还是"偶尔失败",排查方向完全不同。
二是给接收缓冲区加一个清零动作。每轮RS2执行之前,把接收区起始的若干寄存器清成0,避免上一轮的残留数据混进这一轮的解析里。这个动作开销极小,但能省掉很多"数据看着对、细看不对"的怪问题。
三是把站号、波特率、超时时间这些可调参数放到HMI上,别写死在程序里。现场换设备、加节点的时候,改个数字比重下程序快得多。
四是留一份串口抓包记录。调试期间抓到的正常帧和异常帧都存下来,后面再出问题时拿出来对比,能快速判断是设备侧变了还是PLC侧变了。
7. 通信不通时的排查链路
这套顺序是我这些年养成的习惯,从物理层往上走,一层层排除,比东一榔头西一棒子有效率得多。
物理层:先确认A/B有没有接反、SG接没接、终端电阻装了没有、线缆有没有断