# 多通信接口统一指令处理系统开发计划 **项目名称**: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调试接口功能的前提下,将UART1(RF433)和UART3(485)纳入统一指令处理体系,使这三个接口能够: 1. **接收相同格式指令**并执行相同的业务操作 2. **向指令来源接口返回响应**(而非统一从UART2返回) 3. **所有收发数据在UART2上打印详细调试日志** ### 1.2 范围与边界 | 纳入范围 | 排除范围 | | --------------------------- | ------------------- | | UART1(RF433)、UART3(485)指令接收 | 现有RF433 TX/RX应用逻辑修改 | | 多路响应路由机制 | 物理层驱动修改 | | 全链路调试日志系统 | 协议层(protocol.h)重构 | | 共享资源并发保护 |
| ### 1.3 预期交付物 | 交付物 | 说明 | | ------------------------- | ------------------- | | `multi_uart_router.[c/h]` | 多UART统一路由核心模块 | | `cmd_router.[c/h]` | 指令路由与响应分发模块 | | `debug_log.[c/h]` | 增强型调试日志系统 | | 修改后的 `cmd_parser.c` | 支持多实例解析器(可选)或响应路由接口 | | 修改后的 `main.c` | 中断回调和任务调度整合 | | 测试用例与验证方案 |
| *** ## 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 Flash,20KB 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] : 示例: [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 ``` ### 阶段2:UART1/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); } // ... 其他端口保持原样 ... } ``` ### 阶段3:UART3/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帧测试统计 | | 响应延迟 | ≤50ms(P99) | 示波器或时间戳统计 | | Flash占用增量 | ≤8KB | 编译后.map文件分析 | | RAM占用增量 | ≤2KB | 运行时内存分析 | | CPU空闲率 | ≥70% | Systick空闲计数 | *** ## 6. 风险评估与应对 ### 6.1 技术风险 | 风险 | 概率 | 影响 | 应对措施 | | --------------------- | -- | -- | ----------------------------------------- | | **数据丢失**:高频率指令导致缓冲区溢出 | 中 | 高 | 1. 动态调整缓冲区大小2. 实现流量控制(XOFF/XON)3. 丢帧时日志记录 | | **响应延迟**:复杂指令阻塞发送 | 中 | 中 | 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 | 架构师 | 初始版本创建 | *** **文档结束**