罗克韦尔 Micro850 PLC 与仪表通过 Modbus RTU 协议通信时,若在单次扫描周期内对同一从站地址、同一寄存器地址混用功能码 03(读保持寄存器)和功能码 06(写单个保持寄存器),将导致通信异常:常见表现为 PLC 报错 Modbus Error Code 0x01(非法功能)、0x02(非法数据地址)或通信超时,仪表无响应,甚至后续所有 Modbus 请求全部挂起。
根本原因并非硬件冲突,而是 Micro850 内置 Modbus 主站模块的协议栈状态机设计限制:它不支持在同一通信会话上下文(即同一从站 ID + 同一串口通道)中,在未完成前一个事务(transaction)确认前,发起类型冲突的新请求。功能码 03 与 06 属于语义互斥操作——读与写不可原子合并;而 Micro850 的 Modbus 主站固件(固件版本 ≤ 4.0)将每个请求视为独立会话,但底层串口驱动采用单缓冲+阻塞式发送等待机制,若上位逻辑在一次 PLC 扫描中连续调用 MBUS_MSG 指令块且目标相同,第二条指令会因串口忙或应答未返回而被丢弃或触发校验失败。
以下为完整、可立即落地的逻辑修正方案,覆盖配置、编程、测试全流程。
一、明确通信拓扑与参数约束
Micro850 作为 Modbus 主站,通过 RS-485 端口(COM1 或 COM2)连接智能仪表(如 Yokogawa UT351、Honeywell UDC3500、或国产导轨式温控表),仪表为从站,地址设为 1~247 范围内唯一值。
必须统一且严格匹配的参数:
| 参数项 | 必须值 | 说明 |
|---|---|---|
| 波特率 | 9600、19200 或 38400 |
全网一致;推荐 19200(兼顾速度与抗干扰) |
| 数据位 | 8 |
不可选 7 或 9 |
| 停止位 | 1 |
Micro850 不支持 2 停止位 |
| 校验方式 | None、Even 或 Odd |
必须与仪表物理设置完全相同;多数仪表默认 None,若启用校验则必须同步 |
| 从站地址 | 1~247(十进制) |
PLC 程序中 Slave Address 输入框填该值,非十六进制 |
| 寄存器地址偏移 | 40001 格式 → 实际地址减 40001 |
例如读取仪表显示值寄存器 40010,PLC 中填 9(即 40010 − 40001 = 9) |
⚠️ 注意:Micro850 的
MBUS_MSG指令中,“起始寄存器地址”字段输入的是从 0 开始的偏移量,而非4xxxx协议地址。混淆此点是 70% 通信失败的主因。
二、禁止混用的典型错误逻辑(需删除)
以下梯形图(LAD)或结构化文本(ST)片段在工程中高频出现,但必须彻底禁用:
// ❌ 错误示例:同一扫描周期内对从站 1、地址 9 混用 FC03 和 FC06
MBUS_MSG_1(
Enable := TRUE,
Mode := 0, // 0=RTU Master
Port := 1, // COM1
SlaveAddress := 1,
FunctionCode := 3, // FC03: 读
StartAddress := 9, // 对应 40010
NumOfPoints := 1,
DataPtr := ADR(ReadData[0])
);
MBUS_MSG_2(
Enable := TRUE,
Mode := 0,
Port := 1,
SlaveAddress := 1, // ← 相同从站
FunctionCode := 6, // ← FC06: 写(冲突!)
StartAddress := 10, // 对应 40011
DataValue := WriteValue,
DataPtr := ADR(WriteData)
);
问题本质:两个 MBUS_MSG 实例共享同一物理端口 Port := 1 且 SlaveAddress := 1,Micro850 固件无法区分它们是“并发需求”还是“逻辑错误”,直接拒绝第二条指令,或使第一条指令的响应帧解析失败。
三、正确通信逻辑的三层隔离原则
必须遵循:时间隔离、地址隔离、指令隔离。
1. 时间隔离:强制错开执行时机
使用 TON 定时器或扫描计数器,确保对同一从站的读/写操作至少间隔 2 个 PLC 扫描周期(默认扫描周期约 10 ms,故 ≥ 20 ms)。
推荐做法(ST 语言):
// ✅ 正确:分时调度,读写分离
VAR
ReadTimer: TON;
WriteTimer: TON;
ReadCycle: INT := 0; // 读操作周期计数器
WriteCycle: INT := 0; // 写操作周期计数器
END_VAR
// 每 500ms 执行一次读操作(周期可控)
ReadTimer(IN := TRUE, PT := T#500MS);
IF ReadTimer.Q THEN
ReadCycle := ReadCycle + 1;
IF ReadCycle >= 1 THEN // 首次就触发
MBUS_MSG_Read(
Enable := TRUE,
Mode := 0,
Port := 1,
SlaveAddress := 1,
FunctionCode := 3,
StartAddress := 9, // 40010
NumOfPoints := 1,
DataPtr := ADR(ReadData[0])
);
ReadCycle := 0;
END_IF;
ReadTimer(IN := FALSE); // 复位定时器
END_IF;
// 写操作延后 510ms 触发(错开 10ms 以上)
WriteTimer(IN := TRUE, PT := T#510MS);
IF WriteTimer.Q THEN
WriteCycle := WriteCycle + 1;
IF WriteCycle >= 1 THEN
MBUS_MSG_Write(
Enable := TRUE,
Mode := 0,
Port := 1,
SlaveAddress := 1,
FunctionCode := 6,
StartAddress := 10, // 40011
DataValue := WriteValue,
DataPtr := ADR(WriteData)
);
WriteCycle := 0;
END_IF;
WriteTimer(IN := FALSE);
END_IF;
✅ 效果:读指令在
t=0ms发出,响应在t≈15ms返回;写指令在t=510ms发出,彻底避开读事务窗口。
2. 地址隔离:读写绝对不同寄存器
即使分时,也严禁读写同一寄存器地址。例如:
- 读取
40010(过程值 PV)→ 写入40010(试图修改 PV)是无效且危险的。 - 正确配对应为:
- 读
40010(PV) → 写40011(设定值 SV) - 读
40020(运行状态) → 写40021(启停命令)
- 读
仪表手册中明确标注“只读”或“读写”属性的寄存器,必须严格遵守。强行写只读寄存器将触发 0x02 错误。
3. 指令隔离:单从站单指令块绑定
为每个从站地址创建专用的 MBUS_MSG 实例,并绑定唯一功能码:
| 实例名 | 功能码 | 用途 | 绑定从站 | 是否复用 |
|---|---|---|---|---|
MBUS_R_1 |
3 | 专读从站 1 | 1 | ❌ 禁止用于写 |
MBUS_W_1 |
6 | 专写从站 1 | 1 | ❌ 禁止用于读 |
MBUS_R_2 |
3 | 专读从站 2 | 2 | — |
在程序中,永远只使能其中一个实例,且同一时刻仅有一个 Enable := TRUE。
四、关键配置步骤(Logix Designer v34+)
-
硬件配置
- 在 Controller Properties → Communication → Serial Ports 中,为 COM1 设置:
Protocol: Modbus RTU Master
Baud Rate: 19200
Data Bits: 8,Stop Bits: 1,Parity: None
Timeout: 200 ms(不可低于 150 ms,避免误判超时)
- 在 Controller Properties → Communication → Serial Ports 中,为 COM1 设置:
-
添加 MBUS_MSG 指令
- 在 MainRoutine 中插入
MBUS_MSG指令(非MBUS_MSG_EX,后者不兼容 Micro850)。 - 右键指令 → Properties → 设置
Mode = 0(RTU Master),其余参数按前述填写。
- 在 MainRoutine 中插入
-
数据区定义
- 创建 DINT 类型数组
ReadData[10]存储读回值(每个寄存器占 1 个 DINT); - 创建 DINT 变量
WriteData存储待写值; DataPtr字段必须填ADR(ReadData[0]),不可填ReadData(缺少取地址符)。
- 创建 DINT 类型数组
-
错误监控
- 将
MBUS_MSG的Error输出位连接至报警标签; ErrorID值查表:0x01→ 检查FunctionCode是否超出3/4/6/16范围;0x02→ 核对StartAddress+NumOfPoints是否超出仪表支持范围(如仪表仅开放40001~40050);0x04→ 从站无响应,检查接线、终端电阻(RS-485 必须在首尾加 120Ω 电阻)、从站地址开关。
- 将
五、现场验证 checklist(逐项打钩)
- [ ] RS-485 A/B 线极性正确(Micro850 的 COM1-A 接仪表 A,COM1-B 接仪表 B);
- [ ] 仪表供电独立,不与 PLC 共地引入噪声;
- [ ] Micro850 固件升级至
v4.1或更高(修复了早期版本对FC06响应帧长度判断缺陷); - [ ] 使用串口调试助手(如 QSerialTerm)单独向仪表发
01 03 00 09 00 01 C5 CD(读 40010),确认仪表返回01 03 02 00 C8 B9 3D(值 200); - [ ] 在 PLC 在线监控中,观察
MBUS_MSG的Done位是否稳定置位,Error位是否始终为FALSE; - [ ] 修改
WriteValue后,等待 ≥ 500ms,再检查仪表实际设定值是否更新。
六、进阶:多从站轮询的防冲突模板(ST)
当需管理 5 台仪表(地址 1~5)时,用状态机替代多个定时器:
VAR
PollState: INT := 0; // 0=空闲, 1=读1, 2=写1, 3=读2, 4=写2...
PollTimer: TON;
END_VAR
PollTimer(IN := TRUE, PT := T#20MS); // 每20ms切换一次状态
IF PollTimer.Q THEN
CASE PollState OF
0: BEGIN PollState := 1; END;
1: BEGIN // 读从站1
MBUS_MSG_Read1(Enable := TRUE, ...);
PollState := 2;
END;
2: BEGIN // 写从站1
MBUS_MSG_Write1(Enable := TRUE, ...);
PollState := 3;
END;
3: BEGIN // 读从站2
MBUS_MSG_Read2(Enable := TRUE, ...);
PollState := 4;
END;
// ...依此类推
ELSE PollState := 0;
END_CASE;
PollTimer(IN := FALSE);
END_IF;
此结构确保:
- 每个从站的读/写严格串行;
- 全局轮询周期 =
20ms × 状态数,可精确控制总通信负载; - 任意从站故障不影响其他站通信。
七、公式级通信时序保障(关键推导)
为确保 99.9% 通信成功率,最小安全轮询间隔 $T_{\text{min}}$(单位:ms)需满足:
$$ T_{\text{min}} = \frac{8 \times (N + 1) \times 1000}{\text{BaudRate}} + T_{\text{proc}} + T_{\text{guard}} $$
其中:
- $N$ = 单帧字节数(FC03 读 1 寄存器:从站地址 1B + 功能码 1B + 起始地址 2B + 点数 2B + CRC 2B = 8B;响应帧:1B+1B+2B+2B+2B=8B);
- $T_{\text{proc}}$ = PLC 处理+串口驱动延迟,实测 Micro850 约
3 ms; - $T_{\text{guard}}$ = 线路噪声余量,取
5 ms;
代入 BaudRate = 19200:
$$
T_{\text{min}} = \frac{8 \times 9 \times 1000}{19200} + 3 + 5 \approx 3.75 + 8 = 11.75\ \text{ms}
$$
故任意两次操作间隔 ≥ 12 ms 即可物理层面可靠。前述 20 ms 定时器已充分冗余。
八、常见故障速查表
| 现象 | 最可能原因 | 快速验证动作 |
|---|---|---|
ErrorID = 0x01 |
FunctionCode 设为 0 或 5 |
改为 3 或 6,重下装 |
ErrorID = 0x02 |
StartAddress 超出仪表范围 |
查仪表手册,将 40080 改为 40001 偏移 79 |
Done 不置位,Error = FALSE |
从站未上电或地址开关错 | 用万用表测仪表 RS-485 A/B 间电压(应有 ±1V) |
| 通信时好时坏 | 缺少 120Ω 终端电阻或共模干扰 | 在 COM1 两端各并 120Ω 电阻,屏蔽线单端接地 |
| 写操作成功但仪表值不更新 | 仪表处于手动模式(MAN) | 发送 FC06 写 40000(假设为自动/手动切换寄存器)设为 1 |

暂无评论,快来抢沙发吧!