| @@ -0,0 +1,141 @@ | |||
| # Modbus RTU最大长度ADU测试 | |||
| 串口助手请选择HEX发送。下列报文已经完整展开,可以直接复制。 | |||
| 测试条件: | |||
| - 从站地址为`0x01` | |||
| - 先执行0x0F写线圈,再执行0x01读线圈 | |||
| - 先执行0x10写保持寄存器,再执行0x03读保持寄存器 | |||
| - 0x10和0x03使用D6000起始地址,避免覆盖掉电保持区和通信历史区 | |||
| - 文档中的全零读取响应以相关线圈和保持寄存器初始值均为0为前提 | |||
| - 0x0F一次最多写1968个线圈,不能单次清零全部2000个待读线圈;测试0x01前还应确认线圈1968~1999为0 | |||
| - 0x10一次最多写123个寄存器,不能单次清零全部125个待读寄存器;测试0x03前还应确认D6123~D6124为0 | |||
| ## 0x0F写入1968个线圈 | |||
| 含义: | |||
| - `01`:从站地址 | |||
| - `0F`:写多个线圈功能码 | |||
| - `00 00`:起始线圈地址0 | |||
| - `07 B0`:写入数量1968 | |||
| - `F6`:后续线圈数据长度246字节 | |||
| - 连续246个`00`:将线圈0~1967全部写为OFF | |||
| - `A6 FE`:CRC低字节、CRC高字节 | |||
| - 测试目的:用255字节合法请求帧检查DMA接收缓冲区边界和最大线圈写入数量 | |||
| 请求帧长度:255字节 | |||
| 请求帧: | |||
| ```text | |||
| 01 0F 00 00 07 B0 F6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A6 FE | |||
| ``` | |||
| 预期响应帧长度:8字节 | |||
| 预期响应帧: | |||
| ```text | |||
| 01 0F 00 00 07 B0 56 4F | |||
| ``` | |||
| ## 0x01读取2000个线圈 | |||
| 含义: | |||
| - `01`:从站地址 | |||
| - `01`:读线圈功能码 | |||
| - `00 00`:起始线圈地址0 | |||
| - `07 D0`:读取数量2000 | |||
| - `3F A6`:CRC低字节、CRC高字节 | |||
| - 响应中的`FA`表示后续包含250字节线圈状态,因为`2000 ÷ 8 = 250` | |||
| - 响应中的250个`00`表示线圈0~1999全部为OFF | |||
| - `F5 AF`:全零响应对应的CRC低字节、CRC高字节 | |||
| - 测试目的:检查最大线圈读取数量以及255字节响应帧的DMA发送 | |||
| 请求帧长度:8字节 | |||
| 请求帧: | |||
| ```text | |||
| 01 01 00 00 07 D0 3F A6 | |||
| ``` | |||
| 预期响应帧长度:255字节 | |||
| 预期响应帧: | |||
| ```text | |||
| 01 01 FA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F5 AF | |||
| ``` | |||
| ## 0x10写入123个保持寄存器 | |||
| 含义: | |||
| - `01`:从站地址 | |||
| - `10`:写多个保持寄存器功能码 | |||
| - `17 70`:起始地址6000,即D6000 | |||
| - `00 7B`:写入数量123 | |||
| - `F6`:后续寄存器数据长度246字节 | |||
| - 连续246个`00`:将D6000~D6122全部写为0 | |||
| - `76 06`:CRC低字节、CRC高字节 | |||
| - 正常响应回显起始地址`17 70`和写入数量`00 7B` | |||
| - 测试目的:用255字节合法请求帧检查DMA接收缓冲区边界和最大寄存器写入数量 | |||
| 请求帧长度:255字节 | |||
| 请求帧: | |||
| ```text | |||
| 01 10 17 70 00 7B F6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 76 06 | |||
| ``` | |||
| 预期响应帧长度:8字节 | |||
| 预期响应帧: | |||
| ```text | |||
| 01 10 17 70 00 7B 84 45 | |||
| ``` | |||
| ## 0x03读取125个保持寄存器 | |||
| 含义: | |||
| - `01`:从站地址 | |||
| - `03`:读保持寄存器功能码 | |||
| - `17 70`:起始地址6000,即D6000 | |||
| - `00 7D`:读取数量125 | |||
| - `81 84`:CRC低字节、CRC高字节 | |||
| - 响应中的`FA`表示后续包含250字节寄存器数据,因为`125 × 2 = 250` | |||
| - 响应中的250个`00`表示D6000~D6124全部为0 | |||
| - `08 E8`:全零响应对应的CRC低字节、CRC高字节 | |||
| - 测试目的:检查最大保持寄存器读取数量以及255字节响应帧的DMA发送 | |||
| 请求帧长度:8字节 | |||
| 请求帧: | |||
| ```text | |||
| 01 03 17 70 00 7D 81 84 | |||
| ``` | |||
| 预期响应帧长度:255字节 | |||
| 预期响应帧: | |||
| ```text | |||
| 01 03 FA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 E8 | |||
| ``` | |||
| ## 检查项 | |||
| - 0x0F和0x10请求的`ModbusRxFrameLength`应为255 | |||
| - 0x01和0x03正常响应的`responseLength`应为255 | |||
| - `droppedFrameCount`不应增加 | |||
| - `uartErrorCount`不应增加 | |||
| - 不应发生HardFault、DMA越界或接收停止 | |||
| - 完成255字节帧后,下一帧仍应正常通信 | |||
| @@ -9,10 +9,10 @@ extern "C" | |||
| #endif | |||
| /** | |||
| * @brief Modbus 数据模型的最大地址。 | |||
| * @brief Modbus 数据模型的最大地址 | |||
| * | |||
| * 任务要求规定线圈和保持寄存器地址范围均为 0~0x270F, | |||
| * 因此每种数据共有 10000 项。 | |||
| * 因此每种数据共有 10000 项 | |||
| */ | |||
| #define MODBUS_MAP_LAST_ADDRESS (0x270FU)//保持寄存器或线圈地址 | |||
| #define MODBUS_MAP_ITEM_COUNT (0x2710U)//保持寄存器或线圈数量 | |||
| @@ -24,9 +24,9 @@ extern "C" | |||
| #define MODBUS_CONNECTION_TIMEOUT_MS (1000U) | |||
| /** | |||
| * @brief 与 TouchWin 触摸屏联调使用的演示地址。 | |||
| * @brief 与 TouchWin 触摸屏联调使用的演示地址 | |||
| * | |||
| * 可以直接访问以下地址。 | |||
| * 可以直接访问以下地址 | |||
| */ | |||
| #define HMI_REG_DEVICE_ID (0U) | |||
| #define HMI_REG_UPTIME_SECONDS (1U) | |||
| @@ -55,20 +55,20 @@ static volatile MODBUS_BACKUP_DATA *backupData =(volatile MODBUS_BACKUP_DATA *)B | |||
| /** | |||
| * @brief Modbus 从站通信统计信息。 | |||
| * @brief Modbus 从站通信统计信息 | |||
| */ | |||
| typedef struct | |||
| { | |||
| uint32_t rxEventCount; ///< 串口接收事件总数。 | |||
| uint32_t validFrameCount; ///< CRC 和从站地址均有效的帧数。 | |||
| uint32_t txFrameCount; ///< 成功启动 DMA 发送的帧数。 | |||
| uint32_t crcErrorCount; ///< CRC 校验错误帧数。 | |||
| uint32_t ignoredAddressCount; ///< 因从站地址不匹配而忽略的帧数。 | |||
| uint32_t illegalFunctionCount; ///< 非法功能码计数。 | |||
| uint32_t illegalAddressCount; ///< 非法数据地址计数。 | |||
| uint32_t illegalValueCount; ///< 非法数据值计数。 | |||
| uint32_t droppedFrameCount; ///< 接收槽占用或长度错误导致的丢帧数。 | |||
| uint32_t uartErrorCount; ///< HAL 串口错误计数。 | |||
| uint32_t rxEventCount; ///< 串口接收事件总数 | |||
| uint32_t validFrameCount; ///< CRC 和从站地址均有效的帧数 | |||
| uint32_t txFrameCount; ///< 成功启动 DMA 发送的帧数 | |||
| uint32_t crcErrorCount; ///< CRC 校验错误帧数 | |||
| uint32_t ignoredAddressCount; ///< 因从站地址不匹配而忽略的帧数 | |||
| uint32_t illegalFunctionCount; ///< 非法功能码计数 | |||
| uint32_t illegalAddressCount; ///< 非法数据地址计数 | |||
| uint32_t illegalValueCount; ///< 非法数据值计数 | |||
| uint32_t droppedFrameCount; ///< 接收槽占用或长度错误导致的丢帧数 | |||
| uint32_t uartErrorCount; ///< HAL 串口错误计数 | |||
| } MODBUS_SLAVE_STATS; | |||
| typedef struct | |||
| @@ -82,80 +82,80 @@ typedef struct | |||
| uint8_t response[256]; | |||
| } MODBUS_HISTORY_ITEM; | |||
| /** @brief Modbus 从站通信统计数据。 */ | |||
| /** @brief Modbus 从站通信统计数据 */ | |||
| extern volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics; | |||
| /** | |||
| * @brief 初始化 Modbus 从站并启动 USART DMA 空闲接收。 | |||
| * @param[in] huart 已由 HAL 初始化完成的串口句柄。 | |||
| * @param[in] slaveAddress 从站地址,有效范围为 1~247。 | |||
| * @retval HAL_OK 初始化成功且 DMA 接收已启动。 | |||
| * @retval HAL_ERROR 输入参数无效。 | |||
| * @retval HAL_BUSY 串口或 DMA 当前忙。 | |||
| * @brief 初始化 Modbus 从站并启动 USART DMA 空闲接收 | |||
| * @param[in] huart 已由 HAL 初始化完成的串口句柄 | |||
| * @param[in] slaveAddress 从站地址,有效范围为 1~247 | |||
| * @retval HAL_OK 初始化成功且 DMA 接收已启动 | |||
| * @retval HAL_ERROR 输入参数无效 | |||
| * @retval HAL_BUSY 串口或 DMA 当前忙 | |||
| */ | |||
| HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart, uint8_t slaveAddress); | |||
| /** | |||
| * @brief 校验并处理一帧待处理请求。 | |||
| * @brief 校验并处理一帧待处理请求 | |||
| * | |||
| * 本函数应在 uC/OS-II 任务中周期调用。中断只负责接收数据, | |||
| * CRC 校验、协议解析和响应发送均在任务上下文中执行。 | |||
| * 本函数应在 uC/OS-II 任务中周期调用中断只负责接收数据, | |||
| * CRC 校验、协议解析和响应发送均在任务上下文中执行 | |||
| */ | |||
| void ModbusSlavePoll(void); | |||
| /** | |||
| * @brief 处理 HAL 串口 DMA 接收事件。 | |||
| * @param[in] huart 产生接收事件的串口句柄。 | |||
| * @param[in] size 本次接收到的字节数。 | |||
| * @brief 处理 HAL 串口 DMA 接收事件 | |||
| * @param[in] huart 产生接收事件的串口句柄 | |||
| * @param[in] size 本次接收到的字节数 | |||
| */ | |||
| void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size); | |||
| /** | |||
| * @brief 处理 HAL 串口 DMA 发送完成事件。 | |||
| * @param[in] huart 完成发送的串口句柄。 | |||
| * @brief 处理 HAL 串口 DMA 发送完成事件 | |||
| * @param[in] huart 完成发送的串口句柄 | |||
| */ | |||
| void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart); | |||
| /** | |||
| * @brief 处理 HAL 串口错误事件并恢复接收。 | |||
| * @param[in] huart 发生错误的串口句柄。 | |||
| * @brief 处理 HAL 串口错误事件并恢复接收 | |||
| * @param[in] huart 发生错误的串口句柄 | |||
| */ | |||
| void ModbusSlaveOnUartError(UART_HandleTypeDef *huart); | |||
| /** | |||
| * @brief 写入一个保持寄存器。 | |||
| * @param[in] address 保持寄存器地址。 | |||
| * @param[in] value 待写入的 16 位数据。 | |||
| * @brief 写入一个保持寄存器 | |||
| * @param[in] address 保持寄存器地址 | |||
| * @param[in] value 待写入的 16 位数据 | |||
| */ | |||
| uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value); | |||
| /** | |||
| * @brief 读取一个保持寄存器。 | |||
| * @param[in] address 保持寄存器地址。 | |||
| * @param[out] value 用于保存读取结果的指针。 | |||
| * @brief 读取一个保持寄存器 | |||
| * @param[in] address 保持寄存器地址 | |||
| * @param[out] value 用于保存读取结果的指针 | |||
| */ | |||
| uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value); | |||
| /** | |||
| * @brief 设置一个线圈的状态。 | |||
| * @param[in] address 线圈地址。 | |||
| * @param[in] state 0 表示复位,非 0 表示置位。 | |||
| * @brief 设置一个线圈的状态 | |||
| * @param[in] address 线圈地址 | |||
| * @param[in] state 0 表示复位,非 0 表示置位 | |||
| */ | |||
| uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state); | |||
| /** | |||
| * @brief 读取一个线圈的状态。 | |||
| * @param[in] address 线圈地址。 | |||
| * @param[out] state 用于保存线圈状态的指针,结果为 0 或 1。 | |||
| * @brief 读取一个线圈的状态 | |||
| * @param[in] address 线圈地址 | |||
| * @param[out] state 用于保存线圈状态的指针,结果为 0 或 1 | |||
| */ | |||
| uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state); | |||
| /** | |||
| * @brief 判断指定时间内是否收到过有效请求。 | |||
| * @param[in] timeoutMs 连接超时时间,单位为毫秒。 | |||
| * @retval 1 从站在指定时间内收到过有效请求。 | |||
| * @retval 0 尚未收到有效请求或连接已经超时。 | |||
| * @brief 判断指定时间内是否收到过有效请求 | |||
| * @param[in] timeoutMs 连接超时时间,单位为毫秒 | |||
| * @retval 1 从站在指定时间内收到过有效请求 | |||
| * @retval 0 尚未收到有效请求或连接已经超时 | |||
| */ | |||
| uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs); | |||
| void ModbusRetainedRegistersLoad(void); | |||
| @@ -110,9 +110,9 @@ static uint32_t ModbusGetU24Be(const uint8_t *data) | |||
| } | |||
| /** | |||
| * @brief 检查连续数据地址范围是否合法。 | |||
| * @param[in] start 起始地址。 | |||
| * @param[in] quantity 数据项数量。 | |||
| * @brief 检查连续数据地址范围是否合法 | |||
| * @param[in] start 起始地址 | |||
| * @param[in] quantity 数据项数量 | |||
| */ | |||
| static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity) | |||
| { | |||
| @@ -126,9 +126,9 @@ static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity) | |||
| } | |||
| /** | |||
| * @brief 读取已经确认地址合法的线圈。 | |||
| * @param[in] address 线圈地址。 | |||
| * @return 线圈状态,取值为 0 或 1。 | |||
| * @brief 读取已经确认地址合法的线圈 | |||
| * @param[in] address 线圈地址 | |||
| * @return 线圈状态,取值为 0 或 1 | |||
| */ | |||
| static uint8_t ModbusCoilGetUnchecked(uint16_t address) | |||
| { | |||
| @@ -138,9 +138,9 @@ static uint8_t ModbusCoilGetUnchecked(uint16_t address) | |||
| } | |||
| /** | |||
| * @brief 设置已经确认地址合法的线圈。 | |||
| * @param[in] address 线圈地址。 | |||
| * @param[in] state 0 表示复位,非 0 表示置位。 | |||
| * @brief 设置已经确认地址合法的线圈 | |||
| * @param[in] address 线圈地址 | |||
| * @param[in] state 0 表示复位,非 0 表示置位 | |||
| */ | |||
| static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state) | |||
| { | |||
| @@ -157,10 +157,10 @@ static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state) | |||
| } | |||
| /** | |||
| * @brief 启动 USART DMA 空闲接收。 | |||
| * @retval HAL_OK DMA 接收启动成功。 | |||
| * @retval HAL_BUSY 串口未配置或发送尚未结束。 | |||
| * @return 其他 HAL 状态表示 DMA 接收启动失败。 | |||
| * @brief 启动 USART DMA 空闲接收 | |||
| * @retval HAL_OK DMA 接收启动成功 | |||
| * @retval HAL_BUSY 串口未配置或发送尚未结束 | |||
| * @return 其他 HAL 状态表示 DMA 接收启动失败 | |||
| */ | |||
| static HAL_StatusTypeDef ModbusStartReceive(void) | |||
| { | |||
| @@ -178,8 +178,8 @@ static HAL_StatusTypeDef ModbusStartReceive(void) | |||
| if ((status == HAL_OK) && (ModbusUart->hdmarx != NULL)) | |||
| { | |||
| /* | |||
| * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用。 | |||
| * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧。 | |||
| * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用 | |||
| * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧 | |||
| */ | |||
| __HAL_DMA_DISABLE_IT(ModbusUart->hdmarx, DMA_IT_HT); | |||
| } | |||
| @@ -190,24 +190,24 @@ static HAL_StatusTypeDef ModbusStartReceive(void) | |||
| /** | |||
| * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节。 | |||
| * @param[in,out] frame 待追加 CRC 的帧缓冲区。 | |||
| * @param[in] payloadLength 不包含 CRC 的有效载荷长度。 | |||
| * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节 | |||
| * @param[in,out] frame 待追加 CRC 的帧缓冲区 | |||
| * @param[in] payloadLength 不包含 CRC 的有效载荷长度 | |||
| */ | |||
| static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength) | |||
| { | |||
| uint16_t crc = ModbusCrc16(frame, payloadLength); | |||
| /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后。 */ | |||
| /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后 */ | |||
| frame[payloadLength] = (uint8_t)(crc & 0x00FFU); | |||
| frame[payloadLength + 1U] = (uint8_t)(crc >> 8U); | |||
| } | |||
| /** | |||
| * @brief 构造 Modbus 异常响应。 | |||
| * @param[in] function 请求功能码。 | |||
| * @param[in] exception 异常码。 | |||
| * @return 异常响应 ADU 长度。 | |||
| * @brief 构造 Modbus 异常响应 | |||
| * @param[in] function 请求功能码 | |||
| * @param[in] exception 异常码 | |||
| * @return 异常响应 ADU 长度 | |||
| */ | |||
| static uint16_t ModbusBuildException(uint8_t function, uint8_t exception) | |||
| { | |||
| @@ -338,11 +338,11 @@ static uint16_t ModbusProcessReadHolding(const uint8_t *request,uint16_t request | |||
| } | |||
| /** | |||
| * @brief 处理写单个保持寄存器功能码 0x06。 | |||
| * @param[in] request RTU 请求帧。 | |||
| * @param[in] requestLength 请求帧长度。 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播。 | |||
| * @return 单播响应长度;广播或无法响应时返回 0。 | |||
| * @brief 处理写单个保持寄存器功能码 0x06 | |||
| * @param[in] request RTU 请求帧 | |||
| * @param[in] requestLength 请求帧长度 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播 | |||
| * @return 单播响应长度;广播或无法响应时返回 0 | |||
| */ | |||
| static uint16_t ModbusProcessWriteSingleRegister( | |||
| const uint8_t *request, | |||
| @@ -376,18 +376,18 @@ static uint16_t ModbusProcessWriteSingleRegister( | |||
| return 0U; | |||
| } | |||
| /* 0x06 的正常响应必须原样回显请求的前 6 个字节。 */ | |||
| /* 0x06 的正常响应必须原样回显请求的前 6 个字节 */ | |||
| (void)memcpy(ModbusTxFrame, request, 6U); | |||
| ModbusAppendCrc(ModbusTxFrame, 6U); | |||
| return 8U; | |||
| } | |||
| /** | |||
| * @brief 处理写单个线圈功能码 0x05。 | |||
| * @param[in] request RTU 请求帧。 | |||
| * @param[in] requestLength 请求帧长度。 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播。 | |||
| * @return 单播响应长度;广播或无法响应时返回 0。 | |||
| * @brief 处理写单个线圈功能码 0x05 | |||
| * @param[in] request RTU 请求帧 | |||
| * @param[in] requestLength 请求帧长度 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播 | |||
| * @return 单播响应长度;广播或无法响应时返回 0 | |||
| */ | |||
| static uint16_t ModbusProcessWriteSingleCoil( | |||
| const uint8_t *request, | |||
| @@ -409,7 +409,7 @@ static uint16_t ModbusProcessWriteSingleCoil( | |||
| /* | |||
| * 0x05 只接受 0xFF00(线圈置位)和 0x0000(线圈复位); | |||
| * 其他数值属于非法数据值。 | |||
| * 其他数值属于非法数据值 | |||
| */ | |||
| if ((value != MODBUS_COIL_VALUE_ON) | |||
| && (value != MODBUS_COIL_VALUE_OFF)) | |||
| @@ -435,18 +435,18 @@ static uint16_t ModbusProcessWriteSingleCoil( | |||
| return 0U; | |||
| } | |||
| /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC。 */ | |||
| /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC */ | |||
| (void)memcpy(ModbusTxFrame, request, 6U); | |||
| ModbusAppendCrc(ModbusTxFrame, 6U); | |||
| return 8U; | |||
| } | |||
| /** | |||
| * @brief 处理写多个线圈功能码 0x0F。 | |||
| * @param[in] request RTU 请求帧。 | |||
| * @param[in] requestLength 请求帧长度。 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播。 | |||
| * @return 单播响应长度;广播或无法响应时返回 0。 | |||
| * @brief 处理写多个线圈功能码 0x0F | |||
| * @param[in] request RTU 请求帧 | |||
| * @param[in] requestLength 请求帧长度 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播 | |||
| * @return 单播响应长度;广播或无法响应时返回 0 | |||
| */ | |||
| static uint16_t ModbusProcessWriteMultipleCoils( | |||
| const uint8_t *request, | |||
| @@ -511,11 +511,11 @@ static uint16_t ModbusProcessWriteMultipleCoils( | |||
| } | |||
| /** | |||
| * @brief 处理写多个保持寄存器功能码 0x10。 | |||
| * @param[in] request RTU 请求帧。 | |||
| * @param[in] requestLength 请求帧长度。 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播。 | |||
| * @return 单播响应长度;广播或无法响应时返回 0。 | |||
| * @brief 处理写多个保持寄存器功能码 0x10 | |||
| * @param[in] request RTU 请求帧 | |||
| * @param[in] requestLength 请求帧长度 | |||
| * @param[in] isBroadcast 非 0 表示当前请求为广播 | |||
| * @return 单播响应长度;广播或无法响应时返回 0 | |||
| */ | |||
| static uint16_t ModbusProcessWriteMultipleRegisters( | |||
| const uint8_t *request, | |||
| @@ -590,7 +590,7 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| uint16_t index; | |||
| uint16_t value; | |||
| /* 请求帧:站号1 + 功能码1 + 地址3 + 数量2 + CRC2。 */ | |||
| /* 请求帧:站号1 + 功能码1 + 地址3 + 数量2 + CRC2 */ | |||
| if (requestLength != 9U) | |||
| { | |||
| ModbusSlaveStatistics.illegalValueCount++; | |||
| @@ -600,11 +600,11 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| MODBUS_EX_ILLEGAL_VALUE); | |||
| } | |||
| /* 提取24位起始地址和16位寄存器数量。 */ | |||
| /* 提取24位起始地址和16位寄存器数量 */ | |||
| start = ModbusGetU24Be(&request[2]); | |||
| quantity = ModbusGetU16Be(&request[5]); | |||
| /* 一次最多读取125个寄存器。 */ | |||
| /* 一次最多读取125个寄存器 */ | |||
| if ((quantity == 0U) || | |||
| (quantity > MODBUS_READ_REGS_MAX)) | |||
| { | |||
| @@ -617,7 +617,7 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| /* | |||
| * 普通SRAM有40000个寄存器,CCMRAM有29999个寄存器, | |||
| * 总地址范围为0~69998。 | |||
| * 总地址范围为0~69998 | |||
| */ | |||
| if ((start >= 69999UL) || ((uint32_t)quantity > (69999UL - start))) | |||
| @@ -629,7 +629,7 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| } | |||
| /* 组成正常响应帧头。 */ | |||
| /* 组成正常响应帧头 */ | |||
| ModbusTxFrame[0] = ModbusSlaveAddress; | |||
| ModbusTxFrame[1] = 0x48U; | |||
| ModbusTxFrame[2] = (uint8_t)(quantity * 2U); | |||
| @@ -640,7 +640,7 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| /* | |||
| * 地址0~39999位于普通SRAM; | |||
| * 地址40000~69998位于CCMRAM。 | |||
| * 地址40000~69998位于CCMRAM | |||
| */ | |||
| if (currentAddress < 40000UL) | |||
| { | |||
| @@ -652,22 +652,22 @@ static uint16_t ModbusProcessReadBigHolding( | |||
| } | |||
| /* 每个寄存器按照高字节、低字节装入响应帧。 */ | |||
| /* 每个寄存器按照高字节、低字节装入响应帧 */ | |||
| ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U); | |||
| ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU); | |||
| } | |||
| /* 添加CRC。 */ | |||
| /* 添加CRC */ | |||
| ModbusAppendCrc( ModbusTxFrame, (uint16_t)(3U + quantity * 2U)); | |||
| return (uint16_t)(5U + quantity * 2U); | |||
| } | |||
| /** | |||
| * @brief 校验并分发一帧 Modbus RTU 请求。 | |||
| * @param[in] request RTU 请求帧。 | |||
| * @param[in] requestLength 请求帧长度。 | |||
| * @return 待发送响应长度;无需响应时返回 0。 | |||
| * @brief 校验并分发一帧 Modbus RTU 请求 | |||
| * @param[in] request RTU 请求帧 | |||
| * @param[in] requestLength 请求帧长度 | |||
| * @return 待发送响应长度;无需响应时返回 0 | |||
| */ | |||
| static uint16_t ModbusProcessRequest(const uint8_t *request,uint16_t requestLength) | |||
| { | |||
| @@ -706,7 +706,7 @@ static uint16_t ModbusProcessRequest(const uint8_t *request,uint16_t requestLeng | |||
| switch (request[1]) | |||
| { | |||
| case 0X01U://读线圈 | |||
| // 广播请求不允许读取,从站不作响应。 | |||
| // 广播请求不允许读取,从站不作响应 | |||
| return (isBroadcast != 0U) ? 0U : | |||
| ModbusProcessReadCoils(request, requestLength); | |||
| @@ -798,7 +798,7 @@ void ModbusSlavePoll(void) | |||
| } | |||
| /* | |||
| * 当前帧;处理完成后再释放帧槽。 | |||
| * 当前帧;处理完成后再释放帧槽 | |||
| */ | |||
| ModbusRxFrameReady = 0U; | |||
| } | |||
| @@ -936,7 +936,7 @@ void ModbusRetainedRegistersLoad(void) | |||
| /* | |||
| * 数据初始化完成后最后写magic,避免初始化中途断电 | |||
| * 却把不完整数据标记成有效。 | |||
| * 却把不完整数据标记成有效 | |||
| */ | |||
| backupData->magic = MODBUS_BACKUP_MAGIC; | |||
| } | |||