
FX3U 这代小型 PLC 在串口通讯上其实挺能打本体加一块 485 扩展板就能同时跟称重仪表、变频器、扫码器、温控表这类第三方设备打交道。但很多人第一次上手时会被两个词绕晕一个是ADPRW 指令一个是RS-485 无协议通讯。前者是 MODBUS 串行通讯的专用指令后者是靠自己拼字节帧的通用做法两者根本不是一回事却经常被塞进同一个标题里。这篇就把这两条路线拆开讲透包括硬件怎么接、D8120 那些位怎么算、RS 指令的收发时序怎么控、CRC16 在梯形图里怎么写、丢包了从哪儿查。适合刚接手 FX3U 串口项目的朋友也适合被接收完成标志不来数据总是错一位折腾过的老手回顾一遍。1. 先澄清概念ADPRW 和 RS 指令是两条不同的路1.1 一个被标题带偏的常见误解网上搜ADPRW 指令通讯案例出来的文章一大半在讲 RS 指令拼帧反过来搜FX3U 485 无协议通讯又有人拿 ADPRW 的例程贴上去。这不是抄袭是真的有不少人把两者当成了同一套东西。ADPRW 是 FX3U 在 MODBUS 串行通讯模式下使用的专用指令它要求硬件必须是带 MB 后缀的通讯适配器如 FX3U-485ADP-MB并且通道的通讯格式必须设定成 MODBUS RTU 模式。而RS 指令是无协议通讯的核心帧头帧尾、地址码、功能码、校验码全部由你自己用 MOV 指令一个字节一个字节填进去硬件上普通 485 扩展板就够用。这两条路的差别用一句话概括ADPRW 是点菜你告诉它要读哪个从站的哪个寄存器、读几个剩下的组帧、发CRC、等应答、校验它全包了RS 指令是自己做菜米面油盐全得自己备。前者省事但受限后者麻烦但为所欲为。搞清楚这个区别后面的选型才有意义。我见过最典型的翻车现场是有人拿着一块 FX3U-485BD 扩展板往程序里敲 ADPRW编译能过下载能跑就是一直通讯不上抓破头查了两天接线最后发现硬件压根不支持这条指令。485BD 和 485ADP-MB 是两个不同定位的板子前者只能走无协议和专用协议后者才带 MODBUS 功能。这一点在选型阶段就必须确认别等柜子做好了才发现要换板。1.2 两条路线的对比与选型依据先把两者的关键差异摆出来选型的时候对着看就行。对比项RS 指令无协议ADPRW 指令MODBUS硬件要求FX3U-485BD / 485ADP 均可必须 FX3U-485ADP-MB 等 MB 型号协议支持任意私有协议、任意标准协议仅 MODBUS RTU组帧工作量全部自己写含 CRC 校验指令内部完成帧格式灵活度完全自由受 MODBUS 帧结构约束占用扫描时间可控收发分离指令执行期间阻塞典型适用对象变频器、仪表、扫码枪的私有协议标准 MODBUS 从站设备调试难度高需要抓包看字节低报错码直接给出来这里面有一个容易被忽略的硬约束同一路通讯通道上无协议和 MODBUS 模式不能共存。因为通讯格式是由 D8120 这一个寄存器决定的你把它设成无协议ADPRW 就用不了设成 MODBUS 模式RS 指令就失效。而 FX3U 本体通常只能挂一块通讯适配器也就是只有一路通道。所以现实中的场景往往是这样的现场有五台设备四台是标准 MODBUS一台是厂家自定义协议那对不起整条通道只能走无协议那四台 MODBUS 设备也得自己拼帧去读。反过来说如果整条线上全是标准 MODBUS 从站那就别折腾了直接上 MB 板走 ADPRW省下来的调试时间够你喝好几杯咖啡。这个判断要放在项目开始前做而不是程序写了一半再改。1.3 本文案例的场景设定为了让后面的代码有落脚点我设定一个尽量贴近真实的场景某包装产线上一台 FX3U 作为主站通过 485 总线挂两台设备。一号设备是一台称重仪表标准 MODBUS RTU 从站站号 1重量值放在保持寄存器 0000H16 位有符号数单位克二号设备是一台通用变频器厂家私有 ASCII 协议站号 2用STX 站号 命令字 数据 ETX 校验的帧结构。这个组合看起来是自找麻烦但它恰恰是现场最常见的形态——标准设备和非标设备混在一条总线上。因为通道只能选一种模式最终的方案是整条总线走无协议用 RS 指令给两台设备分别拼帧。而 ADPRW 那条路我会在第 5 章单独用一个纯 MODBUS 场景演示方便你对比两种写法的差异。两条路的程序我都会给关键片段你按自己现场的实际情况取用。2. 硬件层485 接线这几个细节决定成败2.1 FX3U 做 485 主站需要准备什么FX3U 本体自带的那个口是 RS-422 编程口不能直接当 485 用。要做 485 通讯必须加扩展。有两条路一是FX3U-485BD通讯扩展板直接卡在主机左侧的扩展槽里成本低走无协议和专用协议二是FX3U-CNV-BD 转接板 FX3U-485ADP-MB 适配器的组合成本高一些但支持 MODBUS。还有一种FX3U-485ADP不带 MB 的版本功能和 485BD 类似只是安装形态不同适合需要装在 DIN 导轨或者柜门上的场合。选哪块板直接决定了你后面能不能用 ADPRW。如果项目里明确有 MODBUS 设备且没有私有协议设备那一开始就上 MB 版本的适配器别为了省几百块钱给自己挖坑。反过来如果现场有变频器、老式仪表这类私有协议的设备那 485BD 就够了反正都得自己拼帧。还有一个细节FX3U 的通讯扩展板装在左侧扩展槽位占用的通道号跟槽位有关。第一块适配器一般对应通道 1相关的特殊继电器和特殊寄存器都是 8xxx 段通道 1。如果后面再加第二块就对应通道 2编号会变。写程序前先确认清楚你的板子挂在哪个通道把特殊软元件编号对一遍这是新手最容易搞错的地方——程序里全写的通道 1 的地址结果板子插在通道 2 上怎么调都不通。2.2 端子对应关系与终端电阻485 是差分信号两根线A 和 B有的设备标为 D / D-或者 485 / 485-或者 RDA/RDB、SDA/SDB。FX3U-485BD 上的端子是 SDA、SDB、RDA、RDB 四个内部 SDA 和 RDA 是短接的、SDB 和 RDB 是短接的所以两线制接线时把 SDA 和 RDA 短接后接对方 ADSDB 和 RDB 短接后接对方 BD-两线半双工就够了。四线全双工现在用得很少除非对方设备强制要求。接线的时候有三个点必须做到位做不到位后面全是玄学故障。第一是终端电阻。485 总线在物理两端各需要一个 120Ω 的终端电阻用来吸收信号反射。总线长度超过几十米、或者波特率上到 19200 以上时这个电阻不加就会出现偶发丢包、随机误码。FX3U-485BD 板上有内置的终端电阻通过一个小拨片或者短接端子选择是否接入总线两端各接一个就行中间的设备不要接接多了负载太重通信距离反而缩短。第二是屏蔽层接地。485 线必须用双绞屏蔽线屏蔽层单端接地通常接在控制柜的接地排上另一端悬空。两端都接地会形成地环路反而把干扰引进来这个坑我踩过表现为白天正常、晚上设备一开就疯狂丢包。第三是共地问题。485 是差分传输理论上不需要共地但实际现场如果两台设备各自接不同的地网地电位差可能超过收发器的共模范围一般是 -7V 到 12V直接把通讯口打坏。稳妥的做法是用一根细线把两端的信号地GND / SG连起来或者在总线两端加隔离模块。尤其是跨柜、跨楼层、跨车间的通讯隔离模块别省一个隔离模块的钱远低于一次停机排查的成本。2.3 布线走向与常见干扰源485 线和动力线必须分开走。这话谁都会说但现场真正做到的不到一半。变频器输出线、伺服动力线、接触器线圈回路这些都是强干扰源485 线跟它们捆在一个线槽里走十几米通讯质量必然稀烂。如果实在没法分开那就交叉走别平行走交叉角度尽量接近 90 度耦合面积最小。还有一个容易被忽视的点是分支线长度。485 总线应该是手拉手的菊花链结构从站设备挂在主干上每个分支越短越好一般不超过 1 米。有的现场图省事从主柜拉一根线到端子排再从端子排往四面八方发散形成星形拓扑这种接法在低速短距离下可能侥幸能通一旦距离拉长或者加设备就原形毕露。拓扑这东西是物理层的根基改不了就得推倒重来。我个人的排查习惯是通讯不稳的时候先拿万用表量 A、B 之间的直流电压静态时应该在 0.2V 到 1V 之间取决于偏置电阻如果量出来是 0V 或者接近 5V说明总线被拉死或者有设备在持续占用。再量 A 对地、B 对地的电压看有没有对地短路或者漏电。这些基础测量花不了五分钟比在程序里瞎猜强得多。3. 无协议通讯的核心软元件与参数推导3.1 D8120 每一位到底代表什么无协议通讯的所有参数都压缩在 D8120 这一个 16 位寄存器里。上电第一次扫描时把它写好整条通道的工作方式就定下来了。很多人是靠背9600 波特率设 H81这种口诀过日子的一旦对方设备要求变了参数就抓瞎。其实把位定义看明白自己就能算出来。位段含义取值说明b0数据长度0 7 位1 8 位b2 b1校验方式00 无校验01 奇校验11 偶校验b3停止位0 1 位1 2 位b7~b4波特率0111 48001000 96001001 192001010 384001011 576001100 115200b11~b8通讯协议0000 无协议1100 MODBUS RTUb12~b15接口类型、和校验、协议格式等无协议时一般保持默认按实际接口类型置位举个实际的算法。称重仪表的通讯参数是 9600 波特率、8 位数据、无校验、1 位停止位、无协议逐位算下来b0 18 位数据b2b1 00无校验b3 01 位停止位b7~b4 10009600b11~b8 0000无协议。把这些位拼起来就是一个十六进制数 0081H。所以MOV H81 D8120这一句背后其实是这么一笔账。再换一组19200 波特率、8 位数据、偶校验、1 位停止位。b0 1b2b1 11偶校验二进制 0110 的 b11、b21b3 0b7~b4 1001。拼起来是 0097H。会算这个你就不需要到处问别人D8120 该设多少了对方设备说明书上写的 8E1、19200翻译过来就是 H97。有一点必须强调D8120 属于上电初始化参数改完之后最稳妥的做法是断电再上电让参数真正生效。有的程序里在运行中反复写 D8120指望切换通讯参数结果行为诡异通讯时好时坏。别这么干通道参数一条通道一套定下来就别动。3.2 RS 指令的四个操作数与执行机制RS 指令的格式是RS S m D n四个操作数分别对应发送数据的起始软元件 S、发送点数 m、接收数据的起始软元件 D、接收最大点数 n。比如RS D100 K8 D200 K16意思是发送缓冲区从 D100 开始共 8 个字节接收缓冲区从 D200 开始最多能装 16 个字节。这里有个关键设定会决定你数据放在哪M8161。M8161 OFF 时是 16 位模式每个字低 8 位和高 8 位各存一个字节M8161 ON 时是 8 位模式每个字只存一个字节高 8 位被忽略。绝大多数 485 通讯场景都应该用 8 位模式因为拼帧的时候一个字节对一个 MOV 指令地址连续好算不会出现这个字节在高位那个字节在低位的错位问题。新手 90% 的数据错位故障根源就是 M8161 忘了置位或者理解反了。RS 指令本身必须由常 ON 触点M8000驱动只要通讯在跑这条指令就得一直有效。它本身不发送也不接收只是把通道挂起来待命。真正的动作由两个特殊继电器触发M8122 是发送请求你在程序里把它置 ON系统就开始把发送缓冲区的数据往总线上吐吐完了自动把 M8122 复位不需要你手动清M8123 是接收完成标志当系统判定一帧接收结束会把它置 ON你处理完数据之后必须自己写RST M8123否则下一帧进来了它还是 ON你就永远收不到第二帧了。这个收到数据忘记复位的低级错误我见过不止一位同行犯。还有一个RS指令不能忽略的规则同一条通道上只能出现一条 RS 指令。哪怕你有十台设备要问也是共用这一条 RS 指令靠切换发送缓冲区的内容和点数来轮流问。有人图省事在程序里写了两条 RS 指令一条给仪表一条给变频器结果通讯直接罢工。这不是玄学是通道资源独占。3.3 M8122、M8123 与 D8129 之间的时间账发送、接收、超时这三件事在时间上是有先后关系的理清楚才能在出问题的时候快速定位。发送侧比较简单置位 M8122数据开始往总线上走走完 M8122 自动复位。发送耗时可以粗略估算9600 波特率下一个字符起始位 1 数据位 8 停止位 1 10 位大约 1.04 毫秒发 7 个字节就是 7 毫秒出头。这个时间远小于典型的扫描周期余量一般不用操心。接收侧就微妙了。D8129 是接收超时时间单位是 10 毫秒。它的作用不是发送后多久没收到就算失败而是接收开始之后如果连续这么久没有新的字节进来就认为这一帧结束了。系统在这个时刻把 M8123 置 ON通知你去解析数据同时把接收缓冲区的长度截断在最后一个字节。这个区别非常关键。不少人的误解是D8129 设成 50就是等对方 500 毫秒等不到就报超时然后死活等不来 M8123。实际上如果对方一个字节都没回M8123 根本不会动等一辈子也没用。真正判断对方没应答的是你自己写的一个定时器发送完成后启动一个 200 毫秒的定时器时间到了 M8123 还是 OFF那就判定这次通讯失败走重发或者报错流程。那 D8129 该设多少有一个经验公式取 3 到 5 个字符时间的两三倍。9600 波特率下一个字符约 1.04 毫秒3.5 个字符就是 3.6 毫秒为了保险设成 50 毫秒D8129 K5比较常见。设太小遇到对方回复的字节之间有微小间隔就会被提前截断数据只收到一半设太大每次接收都要拖着等轮询周期被拉长。19200 及以上波特率可以设得更小K2 或 K3 都够。4. 从零写一段能跑的无协议通讯程序4.1 程序整体框架与状态机思路如果只是发一串数据出去那不是通讯那叫广播。真正的通讯程序必须有完整的闭环组帧、发送、等待、判断、解析、异常处理。我用一套简单的状态机来组织比一堆乱七八糟的中间继电器清爽得多。状态机的核心就是一个字寄存器比如 D400里面存当前步骤号状态值含义进入下一状态的条件0空闲等待轮询定时器定时器到跳 1010组帧拼称重仪表请求帧组帧完成跳 2020发送置位 M8122发送完成跳 3030等待应答收到 M8123 上升沿跳 40超时跳 9040解析应答取数据校验通过跳 50失败跳 9050切换到变频器组帧跳 6060发送变频器命令发送完成跳 7070等待变频器应答收到跳 80超时跳 9080解析变频器应答跳回 0完成一轮90错误处理计数 重试重试超次数报警跳回 0用状态机的好处是每一时刻 PLC 只干一件事不会出现上一帧还没发完下一帧就顶上去了的冲突。而且出问题的时候看 D400 的值立刻就知道卡在哪一步比看一堆线圈的通断状态高效太多。组帧的过程说白了就是往连续的 D 寄存器里塞字节。因为 M8161 已经置位成了 8 位模式每个 D 存一个字节地址连续就代表字节连续一目了然。这一点比 16 位模式省心太多了16 位模式下你还要算第 3 个字节是 D101 的高 8 位还是低 8 位稍不注意就错位。4.2 组帧与 CRC16 校验的梯形图实现称重仪表是标准 MODBUS RTU 从站读保持寄存器 0000H读 1 个字。请求帧是 8 个字节站号、功能码、寄存器地址高、寄存器地址低、数量高、数量低、CRC 低、CRC 高。前 6 个字节是固定的CRC 需要算。组帧代码; ---- 状态 10组帧 ---- LD D400 K10 MOV H1 D200 ; 字节0从站站号 1 MOV H3 D201 ; 字节1功能码 03读保持寄存器 MOV H0 D202 ; 字节2寄存器地址高字节 MOV H0 D203 ; 字节3寄存器地址低字节 MOV H0 D204 ; 字节4读取数量高字节 MOV H1 D205 ; 字节5读取数量低字节 MOV K6 D510 ; 参与 CRC 计算的字节数 6 CALL P0 ; 调用 CRC 子程序结果写入 D206/D207 MOV K8 D511 ; 本次发送总字节数 8 MOV K20 D400 ; 进入发送状态CRC-16/MODBUS 的参数是初值 FFFFH多项式 A001H反向多项式不反转输入输出无异或输出。算法是逐字节异或后右移 8 次每次右移前判断最低位为 1 则异或 A001H。用梯形图实现如下; ---- 子程序 P0计算 CRC输入 D200 起 D510 个字节输出 D206/D207 ---- LD M8000 MOV KFFFF D540 ; CRC 累加器初值 MOV K0 V ; 变址寄存器清零 FOR D510 ; 外层循环逐字节 WXOR D200V D540 D540 ; CRC 与当前字节异或 FOR K8 ; 内层循环8 次移位 AND D540 H1 D543 ; 取最低位存到 D543 ROR D540 K1 ; 累加器循环右移一位 LD D543 K1 WXOR D540 HA001 D540 ; 最低位为 1 则异或 A001 NEXT INC V ; 指向下一个字节 NEXT MOV D540 D206 ; 低字节在前 MOV D540 D207 AND D207 HFF D207 ; 只保留低 8 位 SWAP D207 ; 高低字节交换后取高字节 AND D207 HFF D207这里有个实操上的小陷阱FOR/NEXT 循环内部不要写跳转指令包括 CJ、CALL 到别的子程序里再跳回来容易出现行为不可预期的情况。另外 FOR 的循环次数操作数必须是字软元件用常数 K 也可以但嵌套层数别超过 5 层。上面这段双层循环外层 6 次、内层 8 次总共 48 次移位每次十来条指令算下来不到 600 条指令即使按 FX3U 的指令速度也就零点几毫秒对轮询周期完全没有影响。有人会说CRC 应该用查表法256 个表项一次查完更快。对高速场合确实该用查表但 485 这种最多也就几十毫秒一次的轮询移位法完全够代码短、改起来方便、不占 D 寄存器空间我一般优先用移位法。4.3 发送、接收与数据解析的时间线控制发送逻辑要干净利落。状态 20 里只要还没发出去就置位 M8122发出去之后立刻转状态; ---- 状态 20发送 ---- LD D400 K20 ANI M8122 ; 等上一次发送彻底结束 SET M8122 ; 请求发送 MOV K30 D400 ; 立即进入等待应答状态 OUT T10 K200 ; 应答超时定时器200ms定时器要跟着状态一起启动这样发送后多久没回应算超时就有了明确的判据。200 毫秒是我比较常用的值一般仪表的响应时间在几十毫秒以内200 毫秒足够宽容又不至于拖慢整个轮询。如果对方是那种启动慢的变频器可以放宽到 500 毫秒。接收侧靠 M8123 的上升沿触发解析; ---- 状态 30 及之后接收处理 ---- LDP M8123 MOV K40 D400 ; 收到数据进入解析 RST M8123 ; 关键立刻复位接收完成标志 ; ---- 状态 40解析 ---- LD D400 K40 MOV D200 K100 ; 临时存放后面用来比较 LD D200 K1 ; 检查站号是否正确 LD D201 K3 ; 检查功能码是否为 03 AND D201 H80 K0 ; 功能码最高位为1说明是从站异常应答解析的重点是三件事站号对不对、功能码对不对、CRC 对不对。三样全过才把数据取出来用。少一样就当成无效帧丢弃然后走错误处理流程。很多人写通讯程序只检查了数据长度就急着取值结果干扰串进来的垃圾数据被当成真实重量送进配方那才是真正的生产事故。数据本身是两个字高字节在前。MODBUS 是大端序所以合成一个 16 位整数要重量 D203 * 256 D204在梯形图里就是MOV D203 D300; SWAP D300; WOR D204 D300 D300或者直接用MOV D203 D300; MUL D300 K256 D300; ADD D300 D204 D300。如果读的是 32 位数据比如某些流量计的总量那就涉及两个字的高低字顺序问题不同厂家高低字顺序可能相反接上之后发现数值离谱先把 32 位数据的高低字对调试一下八成就是这个问题。再说异常应答的处理。如果从站收到帧但拒绝了比如寄存器地址不存在它会返回一个功能码最高位置 1 的应答帧后面跟一个错误代码。所以在判断功能码的时候不能只判断是不是 03还要判断是不是 8383 就是 03 的异常应答说明从站明确告诉你这个请求我不认。这时候与其盲目重发不如把错误码记下来去查对应的手册含义效率高得多。4.4 超时重发与错误计数的落地写法状态 90 是兜底逻辑谁的锅都往这儿甩发送超时、解析失败、CRC 校验错统统进入这里。; ---- 状态 90错误处理 ---- LD D400 K90 INC D600 ; 该台设备的错误计数 1 LD D600 K3 ; 连续错 3 次以内 MOV K10 D400 ; 重新组