# Modbus RTU 从站测试操作指南 ## 1. 目的和范围 本指南将《测试报告.doc》中的测试项转换为现场可执行步骤,适用于 XDM-60T4-E(STM32F407)上的 Modbus RTU 从站工程。测试范围包括: - 标准功能码:`0x01`、`0x03`、`0x05`、`0x06`、`0x0F`、`0x10`。 - 私有功能码:`0x41`、`0x42`、`0x43`。 - GPIO 输出、通信状态、RTU 帧间隔、UART 错误恢复、RTC 掉电保持。 - TouchWin 的扩展寄存器显示清除功能。 本工程的普通线圈和保持寄存器地址范围均为 `0x0000..0x270F`;从站地址为 `1`。私有功能码仅适用于本项目,不能作为标准 Modbus 功能码使用。 ## 2. 测试前准备 ### 2.1 测试设备 | 设备 | 用途 | | --- | --- | | XDM-60T4-E | 被测 Modbus RTU 从站 | | TG765-MT / TouchWin | HMI 及私有功能码联调 | | Modbus Poll | 标准功能码读写与回读 | | USB-COM/RS-485 转换器 | 主站与从站通信 | | ST-LINK、IAR Embedded Workbench | 下载、观察变量和调试 | | Sscom 或可构造原始帧的串口工具 | CRC、短帧、站号和异常帧测试 | | ModbusTimingTester_3Baud | 测量 T1.5/T3.5 和连续通信时序 | ### 2.2 接线与参数 1. 下载当前项目固件,重新上电或复位 XDM-60T4-E。 2. 按板卡接线图连接 USART1 的通信链路;确认 RS-485 A/B 极性、公共地和终端电阻。 3. 将主站配置为:`115200 bit/s`、`8E1`(8 位数据、偶校验、1 位停止)、从站地址 `1`。 4. Modbus Poll 使用零基地址。若软件显示 `40001`、`00001` 风格地址,先确认其地址换算设置,避免把显示地址当作协议报文地址。 5. Q1-Q7 输出测试前断开危险负载,或改接测试灯。板级输出为低有效,逻辑线圈置 `1` 时对应 GPIO 输出低电平。 ### 2.3 通用记录规则 每条测试记录均应保存:测试时间、固件版本、主站工具及版本、请求帧、响应帧、响应时间、实际现象和判定结果。原始 RTU 帧中的 CRC 按低字节在前发送。 对于错误用例,先记录待验证的数据值,再发送错误帧,最后重新读取该数据值;只有数据未被改变才能判定数据安全测试通过。 ## 3. 已知初始数据和 HMI 映射 ### 3.1 上电后的初始数据 首次启动且没有有效 RTC 备份时,确认以下数据: | 数据对象 | 预期值 | | --- | --- | | 线圈 `0..2` | `1` | | 保持寄存器 `10..15` | `0x0000`、`0x0001`、`0x0010`、`0x0011`、`0x0100`、`0x0110` | | 链路状态线圈 `100` | 无有效通信时为 `0` | | 扩展寄存器 `0x00010000` | 默认 `0x1000`,或有效 RTC 备份值 | 线圈 `1..7` 映射到 Q1-Q7。线圈 `100` 仅用于表示通信状态,不是普通输出线圈。 ### 3.2 TouchWin 私有功能码映射 | 功能 | TouchWin PSW | | --- | --- | | 状态码 / 实际接收长度 | `PSW319` / `PSW320` | | `0x41` 读取结果 | `PSW350` | | `0x42` 写入值 | `PSW351` | | `0x43` 起始基准地址 / 数量 | `PSW370` / `PSW371` | | `0x43` 结果区 | `PSW372..PSW494` | 状态码 `0` 表示成功;`1` 为输入错误;`2` 为发送失败;`3` 为接收失败;`4` 为 CRC 错误;`5` 为响应错误;大于等于 `0x8000` 表示异常响应,低字节为 Modbus 异常码。 ## 4. 功能测试 ### 4.1 初始化与默认数据 **目的:**确认程序完成从站初始化、数据区初始化、GPIO 同步和通信状态初始化。 1. 复位被测设备,复位后不要立刻发送任何 Modbus 请求。 2. 使用 IAR Live Watch,或用 `0x01`/`0x03` 读取第 3.1 节列出的线圈和寄存器。 3. 读取线圈 `100`,确认通信状态未被提前置位。 4. 通过 `0x41` 读取扩展寄存器 `0x00010000`。 **通过条件:**所有初始数据与第 3.1 节一致;从站能响应地址为 `1` 的合法请求;线圈 `100` 在首次有效通信前为 `0`。 ### 4.2 `0x01` 读取线圈 1. 读取线圈 `0`、线圈 `1..7`、线圈 `100`,记录各位值。 2. 分别测试数量 `1`、`8`、跨字节数量以及最大数量 `2000`。 3. 读取起始地址 `0x0000`、`0x270F`,再测试数量为 `0`、数量大于 `2000`、末地址超过 `0x270F` 的请求。 **通过条件:** - 合法响应的功能码为 `0x01`,位图低位先对应较小地址,字节数为 `ceil(数量/8)`。 - 线圈 `1..7` 的读取结果与 Q1-Q7 的逻辑状态一致。 - 数量非法返回异常码 `0x03`;地址非法返回异常码 `0x02`;异常请求不得改变任何线圈。 ### 4.3 `0x03` 读取保持寄存器 1. 读取寄存器 `10..15`,确认初始数据。 2. 分别读取数量 `1`、连续多个及最大数量 `125`。 3. 测试起始地址 `0x0000`、`0x270F`、数量为 `0`、数量大于 `125` 及越界地址。 **通过条件:**合法响应的寄存器数据为高字节在前;数量非法返回 `0x03`,地址非法返回 `0x02`,错误请求不修改寄存器。 ### 4.4 `0x05` 和 `0x06` 单点写入 1. 对线圈 `1` 发送 `0x05`,依次写入 `0xFF00` 和 `0x0000`;每次写入后用 `0x01` 读回并观察 Q1。 2. 对保持寄存器 `10` 发送 `0x06`,依次写入 `0x0000`、`0x1234`、`0xFFFF`;每次写入后用 `0x03` 读回。 3. 向 `0x05` 发送非 `0xFF00`/`0x0000` 的值,向两个功能码发送越界地址。 **通过条件:** - `0x05`、`0x06` 成功响应回显完整请求内容。 - 写入值可被后续读取正确读回;线圈 `1` 的逻辑状态正确驱动 Q1。 - 非法线圈值返回 `0x03`,越界地址返回 `0x02`,原数据不变。 ### 4.5 `0x0F` 和 `0x10` 批量写入 1. 使用 `0x0F` 写入 1 个线圈、8 个线圈、跨字节数量和最大数量 `1968`;读回确认每个线圈。 2. 使用 `0x10` 写入 1 个、多个和最大数量 `123` 个保持寄存器;用 `0x03` 读回确认。 3. 分别构造数量为 `0`、数量超限、字节数不匹配、实际帧长度不匹配和末地址越界的请求。 **通过条件:** - `0x0F` 响应回显起始地址和线圈数量;字节数必须为 `ceil(线圈数量/8)`。 - `0x10` 响应回显起始地址和寄存器数量;字节数必须为 `寄存器数量 x 2`。 - 所有非法请求返回 `0x02` 或 `0x03`,且不得出现部分写入。 ### 4.6 私有功能码 `0x41`、`0x42` 和 `0x43` #### 4.6.1 `0x41` 读取扩展寄存器 1. 在 TouchWin 执行 `ReadExtendedRegister`,或发送如下请求数据并补充 CRC:`01 41 00 01 00 00 00 01`。 2. 记录响应长度和返回的 16 位数据。 3. 分别发送错误扩展地址、数量不为 `1` 和帧长度不为 10 字节的请求。 **通过条件:**仅地址 `0x00010000` 且数量为 `1` 时成功;正常响应总长度为 7 字节,数据字节数为 2;错误地址返回 `0x02`,错误数量或帧长度返回 `0x03`。 #### 4.6.2 `0x42` 写入扩展寄存器 1. 在 `PSW351` 依次填写 `0x0000`、`0xBEEF` 和 `0xFFFF`,执行 `WriteExtendedRegister`;或发送 `01 42 00 01 00 00 <值高字节> <值低字节> CRC_L CRC_H`。 2. 检查响应是否回显 32 位地址和写入值。 3. 立即使用 `0x41` 读取,确认返回值与写入值一致。 4. 使用错误扩展地址测试异常响应。 **通过条件:**正常响应总长度为 10 字节且与请求的地址、数值一致;错误地址返回 `0x02`;仅成功的 `0x42` 写入触发 RTC 备份。 #### 4.6.3 本地清除显示 1. 使用 `0x42` 向扩展寄存器写入非零值,例如 `0xBEEF`。 2. 用 `0x41` 读取,确认 `PSW350` 显示 `0xBEEF`。 3. 执行 TouchWin 的 `ClearExtendedResult`,确认 `PSW350` 变为 `0`。 4. 再次执行 `0x41` 读取。 **通过条件:**清除操作只修改 TouchWin 本地 `PSW350`,不发送 Modbus 帧、不修改 PLC 扩展寄存器、不触发 RTC 保存;第 4 步仍应读回 `0xBEEF`。 #### 4.6.4 `0x43` 奇数地址读取 1. 先用 `0x06` 或 `0x10` 写入可识别数据:`D1=0x1234`、`D3=0xABCD`、`D5=0x0001`;必要时也为 `D11`、`D13`、`D15` 写入不同值。 2. 设置 `PSW370=0`、`PSW371=3`,执行 `ReadOddCountRegisters`。 3. 确认 `PSW372..PSW374` 分别显示 D1、D3、D5 的值。 4. 设置 `PSW370=10`、`PSW371=3`,确认依次返回 D11、D13、D15。 5. 设置数量为 `1` 和 `123`;再设置为 `0`、`2`、`122`、`124`,并测试最后一个奇数地址越界。 **通过条件:** - 从不小于起始地址的第一个奇数地址开始,每次地址加 2。 - 数量必须是 `1..123` 的奇数;数量非法返回 `0x03`。 - 最后一个待读奇数地址超过 `0x270F` 时返回 `0x02`。 - 数量 `123` 时响应长度为 251 字节,数据字节数为 246。 ### 4.7 GPIO 和通信状态 1. 用 `0x05` 或 `0x0F` 逐一置位、复位线圈 `1..7`,观察 Q1-Q7 或测试灯。 2. 发送一帧已寻址的正常请求,读取线圈 `100` 并观察通信指示灯。 3. 停止发送已寻址的有效请求,直到超过配置的 `3000` 个系统节拍,再读取线圈 `100`。 4. 连续发送 CRC 错误帧、非本机站号帧和广播读请求,确认通信状态不被错误刷新。 5. 发送一帧产生 Modbus 异常响应的已寻址请求,确认其能够刷新通信状态。 **通过条件:**线圈 `1..7` 与 Q1-Q7 一一对应;逻辑 `1` 输出低电平;有效已寻址的正常响应、广播写和异常响应会使线圈 `100=1` 并点亮指示灯;通信超时后线圈 `100=0` 且指示灯熄灭。 ### 4.8 广播、异常帧和非本机站号 1. 对 `0x05`、`0x06`、`0x0F`、`0x10` 使用站号 `0` 发送合法广播写请求,随后读取目标数据。 2. 使用站号 `0` 发送读请求及私有功能码请求。 3. 分别发送错误 CRC、短帧、超过 256 字节的长帧、站号 `2` 的请求、未支持功能码、非法地址和非法数量请求。 4. 每个错误请求前后都读取相关线圈、保持寄存器和扩展寄存器。 **通过条件:**合法广播写修改数据但不产生响应;广播读和私有功能码广播被忽略;错误 CRC、短帧、长帧和非本机站号不得修改数据;非广播的非法功能码、地址和值分别返回异常码 `0x01`、`0x02`、`0x03`。 ### 4.9 错误码与异常响应 **目的:**验证从站对可识别的非法请求返回正确 Modbus 异常码,并验证 TouchWin 能正确显示异常结果。 1. 使用 Sscom 或能发送原始 RTU 帧的工具,对站号 `1` 发送下表中的请求。每一帧均应使用正确 CRC。 2. 记录响应的站号、异常功能码、异常码和 CRC;异常功能码必须等于原功能码与 `0x80` 按位或的结果。 3. 使用 TouchWin 发送 `0x41`、`0x42`、`0x43` 的错误请求,确认 `PSW319` 大于等于 `0x8000`,且其低字节等于下表的异常码。 | 编号 | 请求场景 | 预期异常功能码 | 预期异常码 | 说明 | | --- | --- | --- | --- | --- | | EC-01 | 发送未支持的功能码,例如 `0x7F` | `0xFF` | `0x01` | 非法功能码 | | EC-02 | `0x01`/`0x03` 读取数量为 `0` 或超过上限 | 原功能码 `\| 0x80` | `0x03` | 非法数据值 | | EC-03 | 标准读写的起始地址或末地址超出 `0x270F` | 原功能码 `\| 0x80` | `0x02` | 非法数据地址 | | EC-04 | `0x05` 写入值不是 `0xFF00` 或 `0x0000` | `0x85` | `0x03` | 非法线圈写入值 | | EC-05 | `0x0F`/`0x10` 的数量、字节数或帧长度不匹配 | `0x8F`/`0x90` | `0x03` | 非法数量或数据长度 | | EC-06 | `0x41` 的地址不是 `0x00010000` | `0xC1` | `0x02` | 非法扩展地址 | | EC-07 | `0x41` 的数量不是 `1` | `0xC1` | `0x03` | 非法扩展寄存器数量 | | EC-08 | `0x42` 的地址不是 `0x00010000` | `0xC2` | `0x02` | 非法扩展地址 | | EC-09 | `0x43` 的数量为 `0`、偶数或大于 `123` | `0xC3` | `0x03` | 非法奇数地址读取数量 | | EC-10 | `0x43` 的最后一个待读地址超过 `0x270F` | `0xC3` | `0x02` | 奇数地址读取越界 | **无响应场景:**错误 CRC、非本机站号、广播读请求和私有功能码广播请求都不返回 Modbus 异常帧。此时应记录为“无响应”,并确认目标数据没有变化,不能误判为异常码返回失败。 **通过条件:**每个可识别的非法单播请求均返回 5 字节异常响应,响应站号为 `1`,异常功能码和异常码与表格一致,CRC 正确;每个无响应场景均不修改线圈、普通保持寄存器或扩展寄存器。 ## 5. 性能和稳定性测试 ### 5.1 RTU 帧间隔和连续通信 1. 使用串口工具或逻辑分析仪构造三类报文:正常字节间隔、小于 T1.5 的连续字节间隔、超过 T1.5 的字节间隔。 2. 在一帧结束后保持至少 T3.5 的静默时间,再发送下一帧。 3. 循环发送 `0x01`、`0x03`、`0x05`、`0x10` 的混合请求。建议连续运行不少于 10 分钟,并记录总帧数和异常次数。 **通过条件:**正常帧完整处理;字节间隔超时的帧被丢弃且不执行写入;达到 T3.5 后仅处理一次完整帧;连续运行不出现粘帧、重复处理、接收停止或任务阻塞。 ### 5.2 UART 错误恢复 1. 在接收一帧请求中途拔插通信线、制造串口溢出,或使用工具中断接收。 2. 读取目标数据,确认异常帧未执行。 3. 恢复通信线后发送一帧合法 `0x03` 或 `0x06` 请求。 4. 重复上述过程至少 3 次。 **通过条件:**异常帧不参与协议解析且不修改数据;接收恢复后可稳定处理下一帧有效请求;恢复动作不依赖系统复位。 ## 6. 掉电保持测试 1. 确认 PLC 的 VBAT 供电条件满足 RTC 备份要求。 2. 使用 `0x42` 将扩展寄存器写为 `0xBEEF`。 3. 使用标准写功能码修改普通保持寄存器,例如 D0/D31,以及线圈,例如 0/15。 4. 对 PLC 断电后重新上电。 5. 用 `0x41` 读取扩展寄存器,用 `0x01`/`0x03` 读取普通线圈和保持寄存器。 **通过条件:**地址 `0x00010000` 恢复为 `0xBEEF`;普通保持寄存器和线圈不应被 RTC 备份数据覆盖;若 VBAT 未接入或备份记录的魔数、版本、CRC 无效,则扩展寄存器应使用默认值而不是损坏值。 ## 7. 测试完成判定 所有测试项均应记录为“通过、失败或阻塞”。满足以下条件时可判定本轮测试通过: - 所有标准和私有功能码的正常用例通过。 - 所有异常用例均返回预期结果,且未修改不应修改的数据。 - Q1-Q7、线圈 `100` 和通信指示灯行为正确。 - 所有可识别的错误请求均返回正确异常功能码和异常码;无响应场景未产生数据修改。 - 连续通信、RTU 帧间隔和 UART 错误恢复未出现卡死或错误写入。 - RTC 掉电保持只恢复扩展寄存器。 失败项应记录:复现步骤、请求/响应原始帧、固件版本、影响范围、修复提交和回归测试结果。