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

40 KiB
Raw Permalink Blame 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重构
共享资源并发保护

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.csend_response_ok()send_response_err()硬编码使用UART2_Print_String()
  2. IO_Monitorsend_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

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已验证可行

环形缓冲区设计

#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;

关键约束

  • headtail使用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 模块接口设计(关键函数原型)

/**
 * @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 指令包结构定义

/** 端口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,通过回调钩子实现集成

集成接口设计

/**
 * @brief  注册指令处理回调
 * @note   当指令解析完成时调用,传入来源端口信息
 * @param  callback: 回调函数指针
 * @retval 无
 */
void CmdParser_RegisterCallback(void (*callback)(const routed_cmd_frame_t *frame));

替代方案(可选)

如果项目允许适度修改cmd_parser.c,推荐直接将send_response_ok/err()改为通过回调输出:

// 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 响应路由表设计

/**
 * @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驱动发送
/** 发送环形缓冲区与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

/**
 * @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宏):

#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 中断 + 主循环 需要临界区保护

解决方案:优先级继承 + 临界区

/**
 * @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();                    // 退出临界区
}

中断优先级配置建议

// 在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()接口 函数实现 字节可正确路由到对应解析器

关键里程碑代码片段

// 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显示完整收发流程

关键修改

// 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关键数据结构定义

/**
 * @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 回调接口

/**
 * @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 架构师 初始版本创建

文档结束