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

944 lines
40 KiB
Markdown
Raw Permalink Normal View History

# 多通信接口统一指令处理系统开发计划
**项目名称**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 | 架构师 | 初始版本创建 |
***
**文档结束**