| @@ -24,6 +24,7 @@ | |||
| /* USER CODE BEGIN Includes */ | |||
| #include "ucos_ii.h" | |||
| #include "modbus_rtu_slave.h" | |||
| #include "stdio.h" | |||
| /* USER CODE END Includes */ | |||
| /* Private typedef -----------------------------------------------------------*/ | |||
| @@ -110,6 +111,9 @@ static void AppTaskStart(void *p_arg) | |||
| 6, | |||
| (uint16_t)ModbusSlaveStatistics.illegalValueCount); | |||
| HistorySaveToRegisters(); | |||
| @@ -122,6 +126,7 @@ static void AppTaskStart(void *p_arg) | |||
| */ | |||
| ModbusSlavePoll(); | |||
| ModbusRetainedRegistersPoll(); | |||
| // printf("123"); | |||
| OSTimeDly(1U); | |||
| } | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| # Modbus异常码实例 | |||
| ## 非法功能码 | |||
| - 请求:`01 02 00 00 00 01 B9 CA` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:`01 82 01 81 60` | |||
| - 返回帧长度:5字节 | |||
| - 解释:从站未实现功能码`0x02`,返回功能码`0x82`和异常码`0x01`,表示非法功能。 | |||
| ## 03功能码读取非法地址 | |||
| - 请求:`01 03 27 10 00 01 8F 7B` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:`01 83 02 C0 F1` | |||
| - 返回帧长度:5字节 | |||
| - 解释:起始地址`0x2710`等于10000,超出标准保持寄存器有效地址`0~9999`,返回异常码`0x02`,表示非法数据地址。 | |||
| ## 03功能码读取数量为0 | |||
| - 请求:`01 03 00 00 00 00 45 CA` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:`01 83 03 01 31` | |||
| - 返回帧长度:5字节 | |||
| - 解释:读取寄存器数量为0,不符合功能码要求,返回异常码`0x03`,表示非法数据值。 | |||
| ## 05功能码写入非法线圈值 | |||
| - 请求:`01 05 00 00 12 34 C0 BD` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:`01 85 03 02 91` | |||
| - 返回帧长度:5字节 | |||
| - 解释:05功能码只允许使用`FF 00`表示ON或`00 00`表示OFF,写入值`12 34`非法,返回异常码`0x03`。 | |||
| ## 06功能码写入非法地址 | |||
| - 请求:`01 06 27 10 00 01 43 7B` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:`01 86 02 C3 A1` | |||
| - 返回帧长度:5字节 | |||
| - 解释:写入地址`0x2710`等于10000,超出标准保持寄存器有效地址`0~9999`,返回异常码`0x02`。 | |||
| ## 0F功能码字节数错误 | |||
| - 请求:`01 0F 00 00 00 09 01 01 6E 95` | |||
| - 请求帧长度:10字节 | |||
| - 返回帧:`01 8F 03 04 31` | |||
| - 返回帧长度:5字节 | |||
| - 解释:请求写入9个线圈时应携带2个数据字节,但帧中的字节数字段为1,返回异常码`0x03`。 | |||
| ## 10功能码写入数量为0 | |||
| - 请求:`01 10 00 00 00 00 00 09 50` | |||
| - 请求帧长度:9字节 | |||
| - 返回帧:`01 90 03 0C 01` | |||
| - 返回帧长度:5字节 | |||
| - 解释:写入寄存器数量为0,不符合功能码要求,返回异常码`0x03`。 | |||
| ## 48功能码读取非法大地址 | |||
| - 请求:`01 48 01 11 6F 00 01 CD 69` | |||
| - 请求帧长度:9字节 | |||
| - 返回帧:`01 C8 02 F6 01` | |||
| - 返回帧长度:5字节 | |||
| - 解释:起始地址`0x01116F`等于69999,超出扩展保持寄存器有效地址`0~69998`,返回异常码`0x02`。 | |||
| ## 48功能码读取数量为0 | |||
| - 请求:`01 48 00 00 00 00 00 04 48` | |||
| - 请求帧长度:9字节 | |||
| - 返回帧:`01 C8 03 37 C1` | |||
| - 返回帧长度:5字节 | |||
| - 解释:读取寄存器数量为0,不符合功能码要求,返回异常码`0x03`。 | |||
| ## 从站地址不匹配 | |||
| - 请求:`02 03 00 00 00 01 84 39` | |||
| - 请求帧长度:8字节 | |||
| - 返回帧:无 | |||
| - 返回帧长度:0字节 | |||
| - 解释:当前从站地址为`0x01`,请求帧的目标从站地址为`0x02`,因此当前从站直接忽略该帧,不返回正常响应或异常响应。 | |||
| @@ -71,6 +71,17 @@ typedef struct | |||
| uint32_t uartErrorCount; ///< HAL 串口错误计数。 | |||
| } MODBUS_SLAVE_STATS; | |||
| typedef struct | |||
| { | |||
| uint32_t tick; | |||
| uint16_t requestLength; | |||
| uint16_t responseLength; | |||
| uint8_t request[256]; | |||
| uint8_t response[256]; | |||
| } MODBUS_HISTORY_ITEM; | |||
| /** @brief Modbus 从站通信统计数据。 */ | |||
| extern volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics; | |||
| @@ -82,8 +93,8 @@ extern volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics; | |||
| * @retval HAL_ERROR 输入参数无效。 | |||
| * @retval HAL_BUSY 串口或 DMA 当前忙。 | |||
| */ | |||
| HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart, | |||
| uint8_t slaveAddress); | |||
| HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart, uint8_t slaveAddress); | |||
| /** | |||
| * @brief 校验并处理一帧待处理请求。 | |||
| @@ -149,6 +160,13 @@ uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state); | |||
| uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs); | |||
| void ModbusRetainedRegistersLoad(void); | |||
| void ModbusRetainedRegistersPoll(void); | |||
| void HistorySaveToRegisters(void); | |||
| static void ModbusHistorySave( | |||
| uint32_t tick, | |||
| const uint8_t *request, | |||
| uint16_t requestLength, | |||
| const uint8_t *response, | |||
| uint16_t responseLength); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| @@ -1,5 +1,5 @@ | |||
| #include "modbus_rtu_slave.h" | |||
| #include "ucos_ii.h" | |||
| #include <string.h> | |||
| #define MODBUS_RTU_ADU_SIZE_MAX (256U)//Modbus RTU 最大 ADU 长度,单位为字节 | |||
| @@ -24,6 +24,8 @@ | |||
| static UART_HandleTypeDef *ModbusUart; | |||
| static uint8_t ModbusSlaveAddress; | |||
| static MODBUS_HISTORY_ITEM ModbusHistory[16]; | |||
| static uint16_t ModbusHistoryWriteIndex; | |||
| /** | |||
| * DMA 缓冲区只由 DMA 写入;帧缓冲区在接收回调中完成一次快照, | |||
| * 随后由任务解析,避免 DMA 重启后覆盖尚未处理的数据 | |||
| @@ -752,7 +754,12 @@ void ModbusSlavePoll(void) | |||
| responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength); | |||
| ModbusHistorySave(OSTimeGet() / 1000,ModbusRxFrame, | |||
| ModbusRxFrameLength, | |||
| ModbusTxFrame, | |||
| responseLength); | |||
| if (responseLength > 0U) | |||
| { | |||
| @@ -941,3 +948,72 @@ void ModbusRetainedRegistersPoll(void) | |||
| } | |||
| static void ModbusHistorySave( | |||
| uint32_t tick, | |||
| const uint8_t *request, | |||
| uint16_t requestLength, | |||
| const uint8_t *response, | |||
| uint16_t responseLength) | |||
| { | |||
| MODBUS_HISTORY_ITEM *item; | |||
| if(requestLength > 256U) | |||
| { | |||
| requestLength = 256U; | |||
| } | |||
| if(responseLength > 256U) | |||
| { | |||
| responseLength = 256U; | |||
| } | |||
| item = &ModbusHistory[ModbusHistoryWriteIndex]; | |||
| item->tick = tick; | |||
| item->requestLength = requestLength; | |||
| item->responseLength = responseLength; | |||
| (void)memcpy( | |||
| item->request, | |||
| request, | |||
| requestLength); | |||
| if(responseLength > 0U) | |||
| { | |||
| (void)memcpy( | |||
| item->response, | |||
| response, | |||
| responseLength); | |||
| } | |||
| ModbusHistoryWriteIndex++; | |||
| if(ModbusHistoryWriteIndex >= 16U) | |||
| { | |||
| ModbusHistoryWriteIndex = 0U; | |||
| } | |||
| } | |||
| void HistorySaveToRegisters(void) | |||
| { | |||
| // (void)memcpy( | |||
| // &ModbusHoldingRegisters[1000], | |||
| // ModbusHistory, | |||
| // sizeof(ModbusHistory)); | |||
| uint16_t index; | |||
| if(ModbusHistoryWriteIndex == 0U) | |||
| { | |||
| index = 15U; | |||
| } | |||
| else | |||
| { | |||
| index = ModbusHistoryWriteIndex - 1U; | |||
| } | |||
| (void)memcpy( | |||
| &ModbusHoldingRegisters[ | |||
| 1000U + index * 260U], | |||
| &ModbusHistory[index], | |||
| sizeof(ModbusHistory[index])); | |||
| } | |||