Files
433_STM32/docs/多通信接口统一指令处理系统开发计划.md

944 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 多通信接口统一指令处理系统开发计划
**项目名称**E32-433TBH-SC 多接口统一指令处理扩展
**版本**V1.0
**制定日期**2026-03-27
**适用范围**STM32F103嵌入式系统
***
## 1. 项目概述
### 1.1 背景与目标
**现有系统状况**
- UART2作为调试专用接口已实现ASCII指令`$CMD,param*CS`格式)解析
- 指令覆盖继电器控制RL、数字输入查询DI、回显测试ECHO
- 四路DI状态变化通过UART2自动上报`$DI_EVENT`格式)
- RF433模块通过UART1通信485模块预留给UART3
**目标**在不破坏现有UART2调试接口功能的前提下将UART1RF433和UART3485纳入统一指令处理体系使这三个接口能够
1. **接收相同格式指令**并执行相同的业务操作
2. **向指令来源接口返回响应**而非统一从UART2返回
3. **所有收发数据在UART2上打印详细调试日志**
### 1.2 范围与边界
| 纳入范围 | 排除范围 |
| --------------------------- | ------------------- |
| UART1RF433、UART3485指令接收 | 现有RF433 TX/RX应用逻辑修改 |
| 多路响应路由机制 | 物理层驱动修改 |
| 全链路调试日志系统 | 协议层protocol.h重构 |
| 共享资源并发保护 | <br /> |
### 1.3 预期交付物
| 交付物 | 说明 |
| ------------------------- | ------------------- |
| `multi_uart_router.[c/h]` | 多UART统一路由核心模块 |
| `cmd_router.[c/h]` | 指令路由与响应分发模块 |
| `debug_log.[c/h]` | 增强型调试日志系统 |
| 修改后的 `cmd_parser.c` | 支持多实例解析器(可选)或响应路由接口 |
| 修改后的 `main.c` | 中断回调和任务调度整合 |
| 测试用例与验证方案 | <br /> |
***
## 2. 系统架构设计
### 2.1 当前架构分析
```
┌─────────────────────────────────────────────────────────────────────┐
│ main.c │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ UART2_Rx │ │ UART1_Rx │ │ UART3_Rx │ │
│ │ Interrupt │ │ (RF433) │ │ (RS485) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CmdParser (仅UART2专用) │ │
│ │ CmdParser_FeedByte() + Task() │ │
│ └─────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │Relay_Ctrl │ │ IO_Monitor │ │ (其他) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ UART2_Print │ ◄── 所有响应固定从UART2输出 │
│ │ (Ring Buffer) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
**关键问题**
1. `cmd_parser.c``send_response_ok()``send_response_err()`**硬编码**使用`UART2_Print_String()`
2. `IO_Monitor``send_di_event()`同样**硬编码**使用`UART2_Print_String()`
3. UART1和UART3的接收中断**未接入指令解析体系**
4. 缺乏统一的**响应路由**机制
### 2.2 目标架构设计
#### 2.2.1 整体数据流图
```
UART2_Print (调试日志)
│ DEBUG_LOG()
┌─────────────────────────────────────┴─────────────────────────────────────┐
│ [UART2 - 调试专用通道] │
│ 调试命令输入 ──► Rx Interrupt ──► CmdParser ──► 执行 ──► 响应UART2 │
└───────────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────────┐
│ [UART1 - RF433无线模块] │
│ RF433数据 ──► Rx Interrupt ──► ┬── FeedByte() ──► 解析 ──► 执行 ──► 响应UART1
│ │ ▲ │
│ │ │ │
│ ┌──────────────────────────────┴────────────────────┴────────────────┐ │
│ │ Multi-UART Command Router (新增核心) │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │UART1_Recv │ │UART2_Recv │ │UART3_Recv │ │ │
│ │ │RingBuffer │ │RingBuffer │ │RingBuffer │ │ │
│ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ └───────────────┼───────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Unified Parser │ ◄── 共享解析状态机 │ │
│ │ │ (CmdParser) │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────┼──────────────┐ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │Relay_Ctrl │ │IO_Monitor│ │ (Future) │ │ │
│ │ └─────┬─────┘ └─────┬─────┘ └───────────┘ │ │
│ │ │ │ │ │
│ │ └──────────────┼─────────────────────────────────────┐ │ │
│ │ │ Response Router │ │ │
│ │ ▼ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │ │
│ │ │ port_table[] = { │ │ │ │
│ │ │ {UART1, &huart1, "RF433"}, │ │ │ │
│ │ │ {UART2, &huart2, "DEBUG"}, // 保持原样 │ │ │ │
│ │ │ {UART3, &huart3, "RS485"} │ │ │ │
│ │ │ } │ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │ │
└────┴──────────────────────────────────────────────────────────────┴──────┘
┌───────────────────────────────────────────────────────────────────────────┐
│ [UART3 - RS485有线模块] │
│ 485数据 ──► Rx Interrupt ──► ──► FeedByte() ──► 解析 ──► 执行 ──► 响应UART3
└───────────────────────────────────────────────────────────────────────────┘
```
#### 2.2.2 模块划分与职责
| 模块名 | 职责 | 文件位置 |
| --------------------- | --------------------------- | ----------------------------- |
| **Multi-UART Router** | 三个UART接收通道管理、环形缓冲区、响应路由表 | `multi_uart_router.[c/h]`(新增) |
| **Cmd Router** | 指令解析器封装、响应目标指定、响应构造与发送 | `cmd_router.[c/h]`(新增) |
| **Debug Log** | 统一日志接口,支持来源标签、十六进制 dump、时间戳 | `debug_log.[c/h]`(新增) |
| **CmdParser** | 状态机解析(修改:移除硬编码响应,改为回调方式) | 修改现有 |
| **UART2\_Print** | 底层环形缓冲区发送(复用) | 现有 |
### 2.3 关键设计决策
#### 2.3.1 指令格式一致性策略
**决策**:保持现有`$CMD,param1,param2*CS`格式不变三个UART共用同一解析器。
**理由**
- 现有`cmd_parser.c`已实现完整的状态机解析,去抖、超时、校验和验证
- 复用解析器避免代码膨胀STM32F103资源有限64KB Flash20KB RAM
- 统一格式降低后续维护复杂度
#### 2.3.2 响应路由机制
**方案**:引入**端口上下文表port\_context\_table**
```c
typedef struct {
UART_HandleTypeDef *huart; // 指向具体UART句柄
const char *port_name; // 端口名称标签,用于日志
ring_buffer_t *rx_ring; // 接收环形缓冲区
uint8_t rx_tmp; // 单字节接收暂存
} uart_port_context_t;
```
**路由原理**
- 指令解析完成后,业务函数返回**响应数据**和**来源端口句柄**
- `Response_Send()`根据句柄查找对应UART发送
- UART2保持原有直接调用方式**不经过路由层**(调试专用)
#### 2.3.3 日志记录策略
**分层日志级别**
| 级别 | 宏定义 | 触发条件 | 示例输出 |
| ----- | ------------- | -------------- | -------------------------------------------------- |
| ERROR | `LOG_ERROR()` | 校验失败、超时、参数错误 | `[UART1] ERR: CS mismatch recv=0xA1 calc=0x3C` |
| WARN | `LOG_WARN()` | 缓冲区边缘、异常状态 | `[UART3] WARN: RX buffer 90% full` |
| INFO | `LOG_INFO()` | 指令接收、响应发送 | `[UART1] RX: $RL,1*2F``[UART1] TX: $OK,RL,1*00` |
| DEBUG | `LOG_DEBUG()` | 十六进制 dump、详细状态 | `[UART1] DUMP: [A1 B2 C3 D4]` |
**日志格式模板**
```
[TIMESTAMP][PORT] <LEVEL>: <message>
示例:
[012345][UART1] INFO: CMD=RL P1=1 P2=空
[012346][UART1] INFO: Relay -> ON
[012347][UART1] TX: $OK,RL,1*00
```
***
## 3. 详细设计
### 3.1 多UART数据接收层
#### 3.1.1 UART1/UART3中断+DMA/环形缓冲区设计
**推荐方案:中断+环形缓冲区**对比DMA方案
| 方案 | 优点 | 缺点 | 适用场景 |
| ------------ | ---------- | ------------ | --------- |
| **中断+环形缓冲区** | 实现简单,代码量小 | 中断频繁(约每字节一次) | 数据量小、指令简短 |
| DMA+环形缓冲区 | 中断少CPU效率高 | 实现复杂,内存占用大 | 高速大数据流 |
**选定**:中断+环形缓冲区现有UART2已验证可行
**环形缓冲区设计**
```c
#define UART_RX_BUFFER_SIZE 128 // 每个端口接收缓冲区大小
typedef struct {
uint8_t buffer[UART_RX_BUFFER_SIZE];
volatile uint16_t head; // 写入位置(中断写入)
volatile uint16_t tail; // 读取位置(主循环读取)
volatile uint16_t count; // 有效数据计数
} uart_rx_ring_t;
```
**关键约束**
- `head``tail`使用`volatile`防止编译器优化
- 缓冲区大小需为2的幂次便于模运算优化本项目128=2^7
- 中断中**只负责**将数据压入缓冲区,**禁止**复杂逻辑
#### 3.1.2 数据接收状态机
```
┌─────────────────────────────────────────┐
│ UART ISR (每字节) │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ 写入 rx_ring.buffer[head] │
│ head = (head + 1) % RX_BUFFER_SIZE │
│ count++ │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ count >= RX_BUFFER_SIZE ? │
│ overflow_count++ (丢弃) │
│ head 回绕覆盖旧数据 │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ 启动下次接收: HAL_UART_Receive_IT() │
└─────────────────────────────────────────┘
```
**防溢出策略**
- 新数据覆盖最旧数据Lossy Ring Buffer
- 溢出时记录`overflow_count`,供诊断使用
### 3.2 统一指令路由模块
#### 3.2.1 模块接口设计(关键函数原型)
```c
/**
* @brief 指令路由模块初始化
* @note 初始化所有UART端口的接收缓冲区和解析器
* @param 无
* @retval 无
*/
void CmdRouter_Init(void);
/**
* @brief 向指定端口的解析器喂入数据
* @note 由UART中断回调调用线程安全
* @param port_id: 端口ID (PORT_UART1/PORT_UART2/PORT_UART3)
* @param byte: 接收到的字节
* @param current_tick: 系统时间戳
* @retval 无
*/
void CmdRouter_FeedByte(port_id_t port_id, uint8_t byte, uint32_t current_tick);
/**
* @brief 指令路由任务(主循环调用)
* @note 轮询所有端口的解析器状态,执行已就绪的指令
* @param 无
* @retval 无
*/
void CmdRouter_Task(void);
/**
* @brief 发送响应到指定端口
* @note 根据port_id查找对应UART句柄发送响应数据
* @param port_id: 目标端口ID
* @param data: 响应数据缓冲区
* @param len: 数据长度
* @retval 无
*/
void CmdRouter_SendResponse(port_id_t port_id, const uint8_t *data, uint16_t len);
/**
* @brief 发送格式化响应
* @note 支持printf风格格式化自动计算校验和
* @param port_id: 目标端口ID
* @param fmt: 格式化字符串
* @retval 无
*/
void CmdRouter_SendResponseFmt(port_id_t port_id, const char *fmt, ...);
```
#### 3.2.2 指令包结构定义
```c
/** 端口ID枚举 */
typedef enum {
PORT_UART1 = 0, /**< RF433模块 */
PORT_UART2 = 1, /**< 调试串口 */
PORT_UART3 = 2, /**< RS485模块 */
PORT_COUNT
} port_id_t;
/**
* @brief 带源端口信息的指令帧结构
* @note 解析完成后携带来源端口信息,用于响应路由
*/
typedef struct {
cmd_frame_t frame; /**< 原始指令帧数据 */
port_id_t source_port; /**< 指令来源端口 */
uint32_t recv_tick; /**< 接收完成时间戳 */
} routed_cmd_frame_t;
/**
* @brief 端口上下文结构
* @note 管理每个UART端口的接收缓冲区和解析状态
*/
typedef struct {
UART_HandleTypeDef *huart; /**< UART句柄指针 */
const char *name; /**< 端口名称(用于日志) */
uart_rx_ring_t rx_ring; /**< 接收环形缓冲区 */
parser_context_t parser; /**< 解析器上下文 */
uint8_t rx_tmp; /**< 中断接收暂存 */
uint32_t rx_count; /**< 累计接收字节数 */
uint32_t error_count; /**< 解析错误计数 */
} uart_port_context_t;
```
#### 3.2.3 与现有`CmdParser`的集成方式
**方案:扩展而非修改**
1. **新增`CmdRouter`层**:封装现有`CmdParser`,添加端口追踪能力
2. **保持`CmdParser`独立**:不修改现有`cmd_parser.c`,通过回调钩子实现集成
**集成接口设计**
```c
/**
* @brief 注册指令处理回调
* @note 当指令解析完成时调用,传入来源端口信息
* @param callback: 回调函数指针
* @retval 无
*/
void CmdParser_RegisterCallback(void (*callback)(const routed_cmd_frame_t *frame));
```
**替代方案(可选)**
如果项目允许适度修改`cmd_parser.c`,推荐直接将`send_response_ok/err()`改为通过回调输出:
```c
// cmd_parser.h 新增
typedef void (*response_callback_t)(port_id_t port_id, const char *response, uint16_t len);
void CmdParser_SetResponseCallback(response_callback_t callback);
```
### 3.3 多路响应处理模块
#### 3.3.1 响应路由表设计
```c
/**
* @brief 响应路由表
* @note 静态表根据port_id索引查找对应UART句柄
*/
static UART_HandleTypeDef* const g_port_uart_map[PORT_COUNT] = {
[PORT_UART1] = &huart1, // RF433
[PORT_UART2] = &huart2, // DEBUG
[PORT_UART3] = &huart3, // RS485
};
/**
* @brief 端口名称表(用于日志输出)
*/
static const char* const g_port_name_map[PORT_COUNT] = {
[PORT_UART1] = "UART1",
[PORT_UART2] = "UART2",
[PORT_UART3] = "UART3",
};
```
#### 3.3.2 响应发送队列管理
**设计原则**
- 复用现有`UART2_Print`的环形缓冲区机制
- 为UART1和UART3**各创建一个发送环形缓冲区**
- 发送流程:业务层构造响应 → 写入目标端口缓冲区 → UART Tx ISR驱动发送
```c
/** 发送环形缓冲区与uart2_print结构兼容 */
typedef struct {
uint8_t buffer[UART_TX_BUFFER_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
volatile uint16_t count;
volatile bool is_sending;
} uart_tx_ring_t;
/** 端口发送上下文 */
typedef struct {
uart_tx_ring_t tx_ring;
UART_HandleTypeDef *huart;
volatile uint16_t overflow_count;
} uart_tx_context_t;
```
**关键API**
```c
/**
* @brief 初始化指定端口的发送缓冲区
* @param port_id: 端口ID
* @retval 无
*/
void MultiUART_TxInit(port_id_t port_id);
/**
* @brief 发送数据到指定端口(非阻塞)
* @param port_id: 端口ID
* @param data: 数据缓冲区
* @param len: 数据长度
* @retval 无
*/
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len);
/**
* @brief 指定端口发送完成回调供HAL_UART_TxCpltCallback调用
* @param port_id: 端口ID
* @retval 无
*/
void MultiUART_TxCpltCallback(port_id_t port_id);
```
### 3.4 增强型调试日志系统
#### 3.4.1 日志格式标准(具体示例)
**基础格式**
```
[HHHHHH][PORT] LEVEL: message\r\n
│ │ │ │
│ │ │ └── 具体日志内容
│ │ └───────── 日志级别 (ERROR/WARN/INFO/DEBUG)
│ └─────────────── 来源端口 (UART1/UART2/UART3/ALL)
└─────────────────────── 系统运行时间(十六进制,毫秒)
```
**示例输出**
```
[0001F4][UART1] INFO: RX len=12 "$RL,1*2F\r\n"
[0001F5][UART1] INFO: CMD=RL P1=1 valid=true
[0001F6][UART1] INFO: Relay -> ON
[0001F7][UART1] TX: "$OK,RL,1*00\r\n"
[0001F8][UART3] INFO: RX len=15 "$DI,2*5A\r\n"
[0001F9][UART3] INFO: CMD=DI P1=2 valid=true
[0001FA][UART3] INFO: DI2 = HIGH
[0001FB][UART3] TX: "$OK,DI,2,1*2B\r\n"
[00020A][UART1] ERROR: CS mismatch recv=0xA1 calc=0x3C
[00020B][UART1] TX: "$ERR,CS*XX\r\n"
```
**十六进制Dump格式DEBUG级别**
```
[0001FC][UART1] DEBUG: HEX dump (16 bytes)
[0001FC] 24 52 4C 2C 31 2A 32 46 0D 0A FF FF FF FF FF FF FF
$ R L , 1 * 2 F CR LF
```
#### 3.4.2 日志分级与过滤机制
**编译期分级**(通过`DEBUG_LOG_LEVEL`宏):
```c
#define DEBUG_LEVEL_NONE 0 // 全部禁用
#define DEBUG_LEVEL_ERROR 1 // 仅错误
#define DEBUG_LEVEL_WARN 2 // 错误+警告
#define DEBUG_LEVEL_INFO 3 // 错误+警告+信息
#define DEBUG_LEVEL_DEBUG 4 // 全部
#ifndef DEBUG_LOG_LEVEL
#define DEBUG_LOG_LEVEL DEBUG_LEVEL_INFO
#endif
// 日志宏定义
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_ERROR
#define LOG_ERROR(fmt, ...) UART2_Print_Printf("[%06X][%s] ERROR: " fmt "\r\n", \
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
#else
#define LOG_ERROR(fmt, ...)
#endif
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_WARN
#define LOG_WARN(fmt, ...) UART2_Print_Printf("[%06X][%s] WARN: " fmt "\r\n", \
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
#else
#define LOG_WARN(fmt, ...)
#endif
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_INFO
#define LOG_INFO(fmt, ...) UART2_Print_Printf("[%06X][%s] INFO: " fmt "\r\n", \
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
#else
#define LOG_INFO(fmt, ...)
#endif
#if DEBUG_LOG_LEVEL >= DEBUG_LEVEL_DEBUG
#define LOG_DEBUG(fmt, ...) UART2_Print_Printf("[%06X][%s] DEBUG: " fmt "\r\n", \
(unsigned int)HAL_GetTick(), port_name, ##__VA_ARGS__)
#define LOG_HEXDUMP(data, len) Print_HexDump(port_name, data, len)
#else
#define LOG_DEBUG(fmt, ...)
#define LOG_HEXDUMP(data, len)
#endif
```
#### 3.4.3 性能影响评估
| 指标 | 估算值 | 说明 |
| --------- | ------------ | ------------------- |
| Flash占用增量 | +4KB \~ +6KB | 路由层+日志系统 |
| RAM占用增量 | +1KB \~ +2KB | 环形缓冲区RX+TX各两个UART |
| CPU开销 | <5% | 日志仅在指令收发时产生中断驱动 |
| UART2带宽占用 | 峰值约30% | 假设每帧响应产生约50字节日志 |
**优化措施**
- 使用`__attribute__((section(".flash.text")))`将日志函数放入Flash
- 环形缓冲区大小根据实际数据量调整避免过大
- 日志输出异步化不阻塞主流程
### 3.5 资源保护与并发控制
#### 3.5.1 共享资源访问冲突解决方案
**共享资源识别**
| 共享资源 | 访问方 | 潜在冲突 |
| --------------------------- | -------- | ------- |
| `Relay_SetState()` | 多指令入口 | 同时控制继电器 |
| `IO_Monitor_GetState()` | 多指令入口 | 读操作可并发 |
| `IO_Monitor_GetAllStates()` | 多指令入口 | 读操作可并发 |
| 环形缓冲区TX/RX | 中断 + 主循环 | 需要临界区保护 |
**解决方案:优先级继承 + 临界区**
```c
/**
* @brief 继电器控制(线程安全版本)
* @note 内部使用临界区保护,防止并发访问
* @param port_id: 来源端口(用于日志)
* @param state: 目标状态
* @retval 无
*/
void Relay_SetState_Safe(port_id_t port_id, bool state)
{
uint32_t irq_flags;
__disable_irq(); // 进入临界区
irq_flags = __get_PRIMASK(); // 保存中断状态
// 继电器操作
Relay_SetState(state);
// 日志输出(允许中断嵌套,日志本身有保护)
LOG_INFO("Relay -> %s (from %s)", state ? "ON" : "OFF",
g_port_name_map[port_id]);
__set_PRIMASK(irq_flags); // 恢复中断状态
__enable_irq(); // 退出临界区
}
```
**中断优先级配置建议**
```c
// 在SystemClock_Config()之后调用
void Configure_UART_Priorities(void)
{
// STM32F103中断优先级数字越小优先级越高
// 建议配置:
// - UART1 (RF433): 优先级=5子优先级=0较高优先级
// - UART2 (DEBUG): 优先级=6子优先级=0中优先级
// - UART3 (RS485): 优先级=5子优先级=1与UART1同级别
// - Systick: 优先级=15最低保证系统心跳
HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
HAL_NVIC_SetPriority(USART2_IRQn, 6, 0);
HAL_NVIC_SetPriority(USART3_IRQn, 5, 1);
}
```
***
## 4. 实施路线图
### 阶段1架构验证与框架搭建第1-2周
**目标**验证环形缓冲区机制和路由架构的可行性
| 任务 | 交付物 | 验收标准 |
| --------------------------------- | ---- | ------------- |
| 1.1 创建`multi_uart_router.c/h`基础框架 | 源码文件 | 编译通过无语法错误 |
| 1.2 实现UART1/UART3接收环形缓冲区 | 单元测试 | 收发数据一致无丢失 |
| 1.3 实现端口上下文表和初始化函数 | 函数实现 | 三个端口均可正确初始化 |
| 1.4 实现`CmdRouter_FeedByte()`接口 | 函数实现 | 字节可正确路由到对应解析器 |
**关键里程碑代码片段**
```c
// multi_uart_router.h
#ifndef __MULTI_UART_ROUTER_H
#define __MULTI_UART_ROUTER_H
#include "usart.h"
#include "cmd_parser.h"
typedef enum {
PORT_UART1 = 0,
PORT_UART2 = 1,
PORT_UART3 = 2,
PORT_COUNT
} port_id_t;
void MultiUART_Init(void);
void MultiUART_FeedByte(port_id_t port_id, uint8_t byte, uint32_t tick);
void MultiUART_Task(void);
void MultiUART_Send(port_id_t port_id, const uint8_t *data, uint16_t len);
void MultiUART_SendString(port_id_t port_id, const char *str);
#endif
```
### 阶段2UART1/RF433接口实现第3-4周
**目标**UART1能够接收指令并返回响应
| 任务 | 交付物 | 验收标准 |
| ------------------------ | ------------------ | ---------------------------- |
| 2.1 修改`main.c`中UART1中断回调 | 修改后main.c | 中断正确调用`MultiUART_FeedByte()` |
| 2.2 实现UART1 TX环形缓冲区发送 | `MultiUART_Send()` | 响应正确发送到UART1 |
| 2.3 实现响应路由功能 | 路由表 | UART1指令响应返回UART1 |
| 2.4 全链路日志打印 | 日志输出 | UART2显示完整收发流程 |
**关键修改**
```c
// main.c 修改 - UART1中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
MultiUART_FeedByte(PORT_UART1, rf433_uart_rx_tmp, HAL_GetTick());
HAL_UART_Receive_IT(&huart1, &rf433_uart_rx_tmp, 1);
}
// ... 其他端口保持原样 ...
}
```
### 阶段3UART3/485接口实现第5-6周
**目标**UART3能够接收指令并返回响应
| 任务 | 交付物 | 验收标准 |
| ------------------- | ------ | ------------ |
| 3.1 实现UART3接收中断处理 | 中断处理 | 指令正确喂入解析器 |
| 3.2 实现UART3 TX发送功能 | 发送函数 | 响应正确发送到UART3 |
| 3.3 RS485方向控制如果需要 | 方向控制逻辑 | 半双工切换正确 |
| 3.4 多端口并发测试 | 测试报告 | 两端口同时收发正常 |
### 阶段4集成测试与优化第7-8周
**目标**系统稳定性和性能优化
| 任务 | 交付物 | 验收标准 |
| ------------- | ------ | --------------- |
| 4.1 多接口并发指令测试 | 测试报告 | 无竞争条件无数据丢失 |
| 4.2 大数据量压力测试 | 压力测试报告 | 1000帧连续收发无错误 |
| 4.3 异常情况处理测试 | 异常测试报告 | 校验失败超时等处理正确 |
| 4.4 性能优化与内存调整 | 优化报告 | RAM<70%CPU<80% |
### 阶段5文档完善与交付第9周
| 任务 | 交付物 |
| ----------- | --------------------- |
| 5.1 API接口文档 | cmd\_router\_api.md |
| 5.2 修改说明文档 | migration\_guide.md |
| 5.3 测试验证报告 | validation\_report.md |
| 5.4 最终代码归档 | 完整源码包 |
***
## 5. 测试验证计划
### 5.1 单元测试策略
**测试框架**使用`unity`或自定义最小测试框架嵌入式友好
| 模块 | 测试用例 | 验证点 |
| ----- | ------------------------ | --------------- |
| 环形缓冲区 | `test_ring_push_pop()` | 数据先进先出无损坏 |
| 环形缓冲区 | `test_ring_overflow()` | 溢出时旧数据被覆盖 |
| 环形缓冲区 | `test_ring_concurrent()` | 中断+主循环并发安全 |
| 路由表 | `test_port_lookup()` | 端口ID正确映射到UART句柄 |
| 响应构造 | `test_response_format()` | 校验和计算正确 |
### 5.2 集成测试用例
#### 5.2.1 多接口并发指令测试
**测试场景**同时从UART1和UART3发送继电器控制指令
**测试步骤**
1. UART1发送`$RL,1*2F`期望Relay开启
2. UART3发送`$RL,0*2E`期望Relay关闭
3. 间隔50ms交替发送10轮
**验收标准**
- 每次操作后继电器状态与最后一条指令一致
- 两个端口各自收到正确的响应帧
- UART2日志完整记录所有收发过程
#### 5.2.2 大数据量压力测试
**测试场景**连续快速发送1000帧指令
**测试步骤**
1. UART1以10ms间隔连续发送DI查询指令
2. 记录丢包率错误率
3. 监测RAM占用峰值
**验收标准**
- 丢包率 < 0.1%
- 无内存溢出
- 平均响应时间 < 20ms
#### 5.2.3 异常情况处理测试
| 测试场景 | 输入 | 期望行为 |
| ---------- | --------------------- | ------------------- |
| 校验和错误 | `$RL,1*A1`实际应为2F | 返回`$ERR,CS*XX`日志记录 |
| 未知命令 | `$ABC*00` | 返回`$ERR,CMD*XX` |
| 参数越界 | `$DI,5*XX`通道1-4 | 返回`$ERR,PARAM*XX` |
| 帧超时 | 发送`$RL`后等待2秒 | 解析器重置日志输出timeout |
| 端口同时发送相同指令 | UART1和UART3同时发`$ECHO` | 各自收到独立响应 |
### 5.3 验收标准
| 指标 | 目标值 | 测量方法 |
| --------- | ---------- | ----------- |
| 指令处理成功率 | 99.5% | 1000帧测试统计 |
| 响应延迟 | 50msP99 | 示波器或时间戳统计 |
| Flash占用增量 | 8KB | 编译后.map文件分析 |
| RAM占用增量 | 2KB | 运行时内存分析 |
| CPU空闲率 | 70% | Systick空闲计数 |
***
## 6. 风险评估与应对
### 6.1 技术风险
| 风险 | 概率 | 影响 | 应对措施 |
| --------------------- | -- | -- | ----------------------------------------- |
| **数据丢失**高频率指令导致缓冲区溢出 | | | 1. 动态调整缓冲区大小2. 实现流量控制XOFF/XON3. 丢帧时日志记录 |
| **响应延迟**复杂指令阻塞发送 | | | 1. 指令处理异步化2. 预计算校验和3. DMA加速发送 |
| **竞争条件**多端口同时访问继电器 | | | 1. 临界区保护2. 命令队列化3. 最小切换间隔保护已有 |
| **内存碎片**频繁分配/释放 | | | 1. 全部使用静态缓冲区2. 避免动态malloc |
### 6.2 资源风险
| 风险 | 概率 | 影响 | 应对措施 |
| ---------------------- | -- | -- | --------------------------------------------------------------------------- |
| **Flash不足**代码量超过64KB | | | 1. 启用-O2优化2. 精简日志字符串3. 评估裁剪非必要功能 |
| **RAM不足**缓冲区+解析器上下文超支 | | | 1. 缓冲区实际需求计算 RX: 3×128=384B TX: 2×256=512B 解析器: 3×128B=384B 总计约1.3KB余量充足 |
| **中断风暴**高频率字节接收导致系统假死 | | | 1. 配置合适的中断优先级2. 使用DMA分担CPU负载 |
### 6.3 应对措施优先级
```
P0立即处理:
├─ 临界区保护实现(防止竞争条件)
└─ 环形缓冲区溢出处理(日志+计数)
P1本阶段完成:
├─ 中断优先级配置
└─ 内存使用量测量验证
P2集成测试阶段:
├─ 压力测试暴露潜在问题
└─ 性能优化调参
```
***
## 7. 附录
### 附录A关键数据结构定义
```c
/**
* @brief 接收环形缓冲区
*/
typedef struct {
uint8_t buffer[UART_RX_BUFFER_SIZE];
volatile uint16_t head; /**< 写入索引 */
volatile uint16_t tail; /**< 读取索引 */
volatile uint16_t count; /**< 有效数据计数 */
} uart_rx_ring_t;
/**
* @brief 发送环形缓冲区
*/
typedef struct {
uint8_t buffer[UART_TX_BUFFER_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
volatile uint16_t count;
volatile bool is_sending;
volatile uint16_t overflow_count;
} uart_tx_ring_t;
/**
* @brief 端口上下文
*/
typedef struct {
UART_HandleTypeDef *huart;
const char *name;
uart_rx_ring_t rx_ring;
uart_tx_ring_t tx_ring;
parser_context_t parser;
uint8_t rx_tmp;
uint32_t rx_count;
uint32_t error_count;
} uart_port_context_t;
/**
* @brief 带路由信息的指令帧
*/
typedef struct {
cmd_frame_t frame;
port_id_t source_port;
uint32_t recv_tick;
} routed_cmd_frame_t;
```
### 附录B接口API文档
#### B.1 CmdRouter模块
| 函数 | 原型 | 说明 |
| --------------------------- | -------------------------------------------------------------------------------- | ------------ |
| `CmdRouter_Init` | `void CmdRouter_Init(void)` | 初始化所有端口上下文 |
| `CmdRouter_FeedByte` | `void CmdRouter_FeedByte(port_id_t port, uint8_t byte, uint32_t tick)` | 向指定端口喂入数据 |
| `CmdRouter_Task` | `void CmdRouter_Task(void)` | 主循环调用处理就绪指令 |
| `CmdRouter_SendResponse` | `void CmdRouter_SendResponse(port_id_t port, const uint8_t *data, uint16_t len)` | 发送原始响应 |
| `CmdRouter_SendResponseFmt` | `void CmdRouter_SendResponseFmt(port_id_t port, const char *fmt, ...)` | 发送格式化响应 |
#### B.2 DebugLog模块
| | 说明 |
| ------------------------ | ----------- |
| `LOG_ERROR(fmt, ...)` | 错误级别日志 |
| `LOG_WARN(fmt, ...)` | 警告级别日志 |
| `LOG_INFO(fmt, ...)` | 信息级别日志 |
| `LOG_DEBUG(fmt, ...)` | 调试级别日志 |
| `LOG_HEXDUMP(data, len)` | 十六进制内存 dump |
#### B.3 回调接口
```c
/**
* @brief 指令执行完成回调由CmdRouter内部调用
* @param frame: 带路由信息的指令帧
* @retval 无
*/
void App_OnCommandReceived(const routed_cmd_frame_t *frame);
```
### 附录C响应格式规范
**成功响应**`$OK[,content]*CS\r\n`
- 示例`$OK,RL,1*00\r\n`继电器控制成功
- 示例`$OK,DI,1101*2B\r\n`四路DI状态
**错误响应**`$ERR,err_code*CS\r\n`
- err\_code: PARAM参数错误)、CS校验和错误)、CMD未知命令)、TIMEOUT超时
- 示例`$ERR,CS*XX\r\n`
**事件上报**`$DI_EVENT,channel,state*CS\r\n`
- 示例`$DI_EVENT,1,1*A5\r\n`通道1变为高电平
***
## 文档版本历史
| 版本 | 日期 | 作者 | 变更说明 |
| --- | ---------- | --- | ------ |
| 1.0 | 2026-03-27 | 架构师 | 初始版本创建 |
***
**文档结束**