文档版本:2.1(2026-08-03)。本版本按当前
Core/Src、Core/Modbus和 IAR 工程配置核对,描述的是现行固件实现。
本项目运行在 XDM-60T4-E 的 STM32F407 平台上,作为 Modbus RTU 从站,
通过 USART1 接收 TouchWin 或 Modbus Poll 的请求。目标是提供标准线圈/保持
寄存器读写、项目私有功能码、稳定的 RTU 帧边界识别,以及唯一扩展寄存器的
RTC 掉电保持。
本项目的应用代码位于 Core/Src、Core/Modbus 和 Core/OS/Cfg。
Core/Src/main.c 负责启动、任务、UART 接收和 RTU 定时器;Core/Modbus
负责协议解析、响应构造和 RTC 备份;Core/OS/Cfg/app_cfg.h 负责通信时序和任务配置。
Drivers/STM32F4xx_HAL_Driver 与 Core/OS 是 HAL 和 uC/OS-II 依赖,不在本设计中重复解释。
| 角色 | 责任 |
|---|---|
| TouchWin / Modbus Poll | Modbus RTU 主站,发送请求并处理从站响应。 |
| XDM-60T4-E | Modbus RTU 从站,完成帧校验、数据访问、异常响应、GPIO 同步和 RTC 保持。 |
| RTC 备份域 | 只保存私有地址 0x00010000 对应的 16 位扩展寄存器;需要 VBAT 供电。 |
主站 RTU 字节流
-> USART1 接收完成回调
-> TIM5 T1.5 / T3.5 单次定时
-> Modbus 帧就绪信号量
-> Modbus 任务校验并分发功能码
-> 数据区 / GPIO / RTC 备份
-> USART1 异步发送响应
系统入口是 Core/Src/main.c 的 main。它依次完成 HAL、SysTick、系统时钟、
GPIO、USART1 和 TIM5 初始化,然后启动 uC/OS-II。启动任务创建信号量,并创建
Modbus 工作任务和 RTU/链路状态任务;具体函数为 AppTaskStart、AppTaskModbus
和 AppTaskT35。
USART1 使用 115200 bit/s、1 个停止位和偶校验,代码配置为
UART_WORDLENGTH_9B + UART_PARITY_EVEN。在 STM32 HAL 中,这表示 8 位数据加 1 位
校验,主站应配置为 8E1;从站地址为 1。一个字符按 10 bit 计算,因此
APP_MODBUS_BITS_PER_CHARACTER 为 10,字符时间由 app_cfg.h 中的向上取整公式计算。
波特率高于 19200 bit/s 时,RTU 使用固定时序:T1.5 = 750 us、
T3.5 = 1750 us。代码还将接收超时设置为 T1.5 + 一个字符时间,并用第二阶段
补足到 T3.5。TIM5 按 1 MHz 计数,因此超时值可以直接以微秒装载。
HAL_UART_RxCpltCallback 每次只接收一个字节,AppHandleModbusRtuTimer 先处理 T1.5 字符间隔,再确认 T3.5 帧间静默;ModbusFrameSem 交给任务。AppStartUartReception 清空软件帧状态并挂接一个字节的中断接收;AppRecoverUartReception 只在 Modbus 任务上下文中执行 Abort、清错和重挂接。TIM5_IRQHandler 只推进 RTU 状态机,USART1_IRQHandler 只调用 HAL;中断中不解析帧,HAL_UART_ErrorCallback 只停止定时器、标记当前帧无效并设置恢复请求,之后由任务执行恢复。AppStartUartReception;只有 UART 错误或 HAL 状态丢失时才走完整恢复流程,| 数据对象 | 位置 | 用途与保持属性 |
|---|---|---|
holdingRegisters |
Core/Src/main.c |
地址 0x0000..0x270F 的普通保持寄存器,可在 IAR Live Watch 查看;不保持。 |
coilStorage |
Core/Src/main.c |
按位压缩的普通线圈区,可在 IAR Live Watch 查看;不保持。 |
extendedHoldingRegister |
Core/Src/main.c |
私有地址 0x00010000 的唯一扩展值;仅它可被 RTC 保存。 |
协议层不再维护请求帧、响应帧或数据镜像副本,调试时应直接查看上表三个真实数据对象,
避免镜像数据与协议数据不同步。为便于断线、插拔和 UART 恢复测试,main.c 仍保留
ModbusUartLastErrorCode、ModbusUartLastRxState、ModbusUartRecoveryCount、
ModbusUartRecoveryFailureCount、ModbusUartRxArmCount、
ModbusUartRxArmFailureCount、ModbusUartLastRxByte 和
ModbusLastCompleteFrameLength 等运行时诊断变量;这些变量只记录收发恢复过程,
不参与 Modbus 地址映射,也不会作为额外寄存器对外提供。
AppSyncCoilsToGpio 将线圈 1 至 7 写入 Q1 至 Q7。
板级输出为低有效,因此逻辑线圈置 1 时写入 GPIO_PIN_RESET。线圈 0 是链路
数据区中的普通线圈,不参与 Q0/Q1 至 Q7 的输出映射;链路状态线圈 100 和物理
Q0 指示灯由 AppUpdateLinkStatus 根据最近一次有效、已寻址的通信刷新。CRC 错误帧、
非本站号帧和广播读不会刷新在线状态。
协议模块入口是 Core/Modbus/modbus.c 的 ModbusProcessFrame。固定的处理
顺序为:参数校验、帧长度、CRC、站号、功能码、字段和地址范围校验。CRC 错误或
非本机站号的帧不会修改数据区。
ModbusCrc16 计算 RTU CRC16,发送时 CRC 低字节在前。ModbusSlaveInit 初始化
普通数据模型,ModbusConfigureExtendedHoldingRegister 绑定扩展保持寄存器,
ModbusGet/SetCoil 和 ModbusGet/SetHoldingRegister 提供带地址检查的访问接口。
| 功能码 | 行为 | 分发位置 |
|---|---|---|
0x01 |
读取线圈,按 Modbus 位顺序打包响应。 | ModbusBuildReadCoilsResponse |
0x03 |
读取普通保持寄存器,高字节在前。 | ModbusBuildReadRegistersResponse |
0x05 |
写单个线圈,只接受 0xFF00 或 0x0000,成功时回显请求。 |
ModbusProcessWriteSingleCoil |
0x06 |
写单个保持寄存器并回显。 | ModbusProcessWriteSingleRegister |
0x0F |
批量写线圈,校验数量、字节数、帧长度和地址范围。 | ModbusProcessWriteMultipleCoils |
0x10 |
批量写保持寄存器,校验数量、字节数、帧长度和地址范围。 | ModbusProcessWriteMultipleRegisters |
| 功能码 | 行为 | 实现位置 |
|---|---|---|
0x41 |
读取固定 32 位地址 0x00010000,数量必须为 1。 |
ModbusBuildReadExtendedRegistersResponse |
0x42 |
写入 0x00010000 并回显地址和值。协议核心只改 RAM;应用层确认成功后保存 RTC。 |
ModbusBuildWriteExtendedRegisterResponse + AppProcessFrame |
0x43 |
从不小于起始基准地址的第一个奇数地址开始,每隔 2 读取一个值;数量必须为 1..123 的奇数。 |
ModbusBuildReadOddAddressRegistersResponse |
私有功能码只供当前 TouchWin 与本 PLC 工程配套使用,不应声明为标准 Modbus
功能码。触摸屏侧的 PSW372..PSW494 最多显示 123 个 0x43 返回值;一次请求只读取
本帧指定的数量,不会自动读取整个 0x0000..0x270F 区间。
ModbusBackupSave 在应用层确认 0x42
成功后保存扩展值。写入顺序为:先清除魔数、写扩展值、写版本与 CRC、最后写魔数。
这使掉电中断的半写入记录不会被误认为有效数据。
ModbusBackupRestore 只在 RTC 可访问、
魔数匹配、版本匹配和 CRC 正确时恢复。RTC 访问和 LSI 就绪超时控制在
ModbusBackupEnableRtcAccess,CRC 计算由模块内部函数完成。普通寄存器、
线圈和触摸屏本地显示不会通过该模块恢复。
IAR 工程文件为 EWARM/Modbus.ewp,实际源文件清单以 IAR 工程配置为准。
业务代码应至少包含 Core/Src/main.c、Core/Src/stm32f4xx_it.c、
Core/Modbus/modbus.c 和 Core/Modbus/modbus_backup.c,并链接 STM32 HAL、
uC/OS-II 和对应的启动文件。modbus_backup.c 使用 RTC 备份寄存器,因此目标硬件
必须具备有效的 RTC/备份域供电条件。
根目录 test/ 只存放主机测试源码、静态检查脚本和测试支持头文件,不在
EWARM/Modbus.ewp 中引用。交付可运行固件时只需保留 IAR 工程、Core 正式源码、
Drivers、启动文件及必要的工程配置;test/ 可以作为开发归档单独保留或从交付包移除。
| 验证项 | 源码或命令位置 | 目的 |
|---|---|---|
| 协议主机测试 | test/modbus_test.c |
覆盖 CRC、标准/私有功能码、边界和异常响应;不参与目标固件。 |
| RTC 主机测试 | test/modbus_backup_test.c |
证明只恢复扩展寄存器,并校验版本和 CRC 失效场景。 |
| 运行时主机测试 | test/app_runtime_test.c |
验证 SysTick 优先级、UART 恢复重试、错误延后恢复和并发恢复请求。 |
| 运行时静态检查 | test/check_rtu_runtime_invariants.ps1 |
检查 SysTick 的 OS 感知调用和 UART 恢复边界。 |
| 通信计数静态检查 | test/check_no_communication_counters.ps1 |
确认接收、发送和错误计数逻辑未重新引入。 |
| 目标构建 | 使用 IAR 对 EWARM/Modbus.ewp 执行 Modbus 配置构建 |
验证目标工程实际可编译,要求 0 errors、0 warnings。 |
构建产生的 Obj/、.map、.out、.sim、.dep、主机测试 .exe 和
*.stackdump 都是可重建产物,已通过 .gitignore 排除。可下载的
EWARM/Modbus/Exe/Modbus.hex 保留在工程中。