Browse Source

增加了主站异常码测试窗口,增加了通信历史功能

deepseek
ywh 1 month ago
parent
commit
63abe443c0
5 changed files with 184 additions and 4 deletions
  1. +5
    -0
      Core/Src/main.c
  2. +81
    -0
      Document/my_document/Modbus异常码实例.md
  3. +20
    -2
      Modbus/Inc/modbus_rtu_slave.h
  4. +78
    -2
      Modbus/Src/modbus_rtu_slave.c
  5. BIN
      Touchwin/工程11.txp

+ 5
- 0
Core/Src/main.c View File

@@ -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);
}
}


+ 81
- 0
Document/my_document/Modbus异常码实例.md View File

@@ -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`,因此当前从站直接忽略该帧,不返回正常响应或异常响应。

+ 20
- 2
Modbus/Inc/modbus_rtu_slave.h View File

@@ -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


+ 78
- 2
Modbus/Src/modbus_rtu_slave.c View File

@@ -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]));
}

BIN
Touchwin/工程11.txp View File


Loading…
Cancel
Save