# UART3智能数据路由与透传功能开发计划 **项目名称**:E32-433TBH-SC UART3智能数据路由与透传扩展 **版本**:V1.0 **制定日期**:2026-03-27 **适用范围**:STM32F103嵌入式系统 **基于系统**:多通信接口统一指令处理系统 V1.0 --- ## 1. 需求与目标 ### 1.1 功能需求详述 **背景**:当前系统已通过`multi_uart_router`和`cmd_router`模块,实现了三端口(UART1/UART2/UART3)ASCII指令的统一解析与响应路由。 **新增功能目标**:使UART3成为"智能接口",能够根据数据内容自动识别并路由: | 数据类型 | 识别条件 | 处理路径 | 响应目标 | |---------|---------|---------|---------| | **ASCII指令** | 符合`$CMD,param*CS\r\n`格式 | 现有指令解析流程 | 返回UART3 | | **透传数据** | 不符合指令格式 | 透传引擎转发至UART1 | 无(单向透传) | **具体场景**: 1. **场景A - 纯指令流** - UART3接收:`$DI*2F\r\n` - 识别为指令 → CmdParser解析 → 执行DI查询 → 响应返回UART3 2. **场景B - 纯透传数据** - UART3接收:`Hello World\r\n`(无`$`起始符) - 识别为透传 → 直接转发至UART1(RF433发送) 3. **场景C - 混合交织数据** - UART3接收:`$DI*2F\r\nDATA_TO_FWD\r\n` - 前半段识别为指令,执行后 - 后半段无`$`起始,识别为透传,转发至UART1 4. **场景D - 透传数据中出现`$`字符** - UART3接收:`Some Data $10 more` - 由于前面不是`$`开头,前半部分透传 - 后续若出现完整指令格式则按指令处理 ### 1.2 非功能性需求 | 指标 | 目标值 | 说明 | |------|--------|------| | **实时性 - 透传延迟** | ≤10ms | 从UART3接收到UART1开始发送的延迟 | | **实时性 - 指令响应** | ≤50ms | 指令解析到响应发送的总时间 | | **可靠性 - 指令识别准确率** | ≥99.9% | 误判率≤0.1% | | **吞吐量 - 透传能力** | ≥9600 Baud | 匹配UART3物理层速率 | | **资源消耗 - Flash增量** | ≤4KB | 新增代码占用 | | **资源消耗 - RAM增量** | ≤1KB | 缓冲区及状态机占用 | | **CPU占用率** | ≤15% | 峰值情况 | ### 1.3 与现有系统的关系 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 现有系统架构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ │ │ │ UART3中断 │ ◄── RS485接收字节 │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │MultiUART │ │ │ │FeedByte() │ ◄── 写入环形缓冲区 │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │CmdRouter │ │ │ │Task() │ ◄── 轮询读取缓冲区 │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │CmdParser │ │ │ │FeedByte() │ ◄── 所有数据统一解析 │ │ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ▼ 增量设计 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 新增功能架构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ │ │ │ UART3中断 │ │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │MultiUART │ │ │ │FeedByte() │ │ │ └──────┬───────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ UART3协议识别器 (Protocol Discriminator) │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ 状态机: INIT → SCAN → CMD_MODE / PASSTHROUGH │ │ │ │ │ │ 识别结果: 指令 or 透传 │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └────────────────────────┬──────────────────────────────┘ │ │ │ │ │ ┌───────────────┴───────────────┐ │ │ ▼ ▼ │ │ ┌──────────────────┐ ┌──────────────────────┐ │ │ │ CMD_MODE │ │ PASSTHROUGH_MODE │ │ │ │ │ │ │ │ │ │ CmdParser │ │ 透传引擎 │ │ │ │ FeedByte() │ │ Passthrough_Task() │ │ │ │ ↓ │ │ ↓ │ │ │ │ 执行指令 │ │ MultiUART_Send() │ │ │ │ ↓ │ │ (UART1) │ │ │ │ 响应→UART3 │ │ │ │ │ └──────────────────┘ └──────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 2. 系统架构设计 ### 2.1 当前UART3数据处理流程分析 基于代码分析,当前UART3数据处理流程如下: ```c // main.c 中的中断回调(假设已实现) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART3) { MultiUART_FeedByte(PORT_UART3, uart3_rx_byte); HAL_UART_Receive_IT(&huart3, &uart3_rx_byte, 1); } } // cmd_router.c - CmdRouter_Task() void CmdRouter_Task(void) { // ... UART1/UART3处理 ... for (port_id_t port_id = 0; port_id < PORT_COUNT; port_id++) { if (port_id == PORT_UART2) continue; // UART2单独处理 uint8_t byte; while (MultiUART_ReadByte(port_id, &byte) > 0) { CmdParser_FeedByte(byte, HAL_GetTick()); // 统一喂给解析器 } } CmdParser_Task(); // 处理已解析完成的指令 } ``` **问题分析**: 1. **无区分机制**:所有UART3数据统一喂给`CmdParser`,无法处理透传数据 2. **解析器污染**:非指令数据进入解析器会触发错误计数 3. **缺乏透发转发**:即使识别出透传数据,也没有转发至UART1的机制 ### 2.2 目标架构与数据流图 #### 2.2.1 整体数据流图 ``` UART2 (调试日志) ▲ │ DEBUG_LOG() │ ┌─────────────────────────────────┴─────────────────────────────────┐ │ UART3智能数据路由 │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ UART3 ISR (每字节) │ │ │ └──────────────────────────┬────────────────────────────────────┘ │ │ │ byte │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 协议识别器 (Protocol Discriminator) │ │ │ │ │ │ │ │ ┌──────────┐ byte ┌──────────┐ 识别结果 │ │ │ │ │ INIT │ ──────► │ SCAN │ ──────► │ │ │ │ └──────────┘ └────┬─────┘ │ │ │ │ │ │ │ │ │ ┌───────────────────┼───────────────────┐ │ │ │ │ │ │ │ │ │ │ │ 遇到'$' 超时(50ms) 遇到'\n' │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌───────────┐ ┌────────────┐ ┌───────────┐ │ │ │ │ │ CMD_MODE │ │PASSTHROUGH │ │ CMD_MODE │ │ │ │ │ │ (指令解析) │ │ (透传转发) │ │ (已完整帧) │ │ │ │ │ └─────┬─────┘ └──────┬──────┘ └─────┬─────┘ │ │ │ │ │ │ │ │ │ │ └────────┼───────────────────┼───────────────────┼─────────────┘ │ │ │ │ │ │ │ ▼ │ ▼ │ │ ┌──────────────────┐ │ ┌──────────────────┐ │ │ │ CmdParser │ │ │ 缓存已接收字节 │ │ │ │ FeedByte() │ │ │ 等待透传引擎处理 │ │ │ └────────┬────────┘ │ └────────┬─────────┘ │ │ │ │ │ │ │ ▼ │ ▼ │ │ ┌──────────────────┐ │ ┌──────────────────┐ │ │ │ 指令执行 │ │ │ Passthrough_Task │ │ │ │ + 响应→UART3 │ │ │ 转发至UART1 │ │ │ └──────────────────┘ │ └──────────────────┘ │ │ │ │ └───────────────────────────────┼────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ │ UART1 (RF433) │ │ 透明传输至远端 │ └──────────────────────┘ ``` #### 2.2.2 模块划分与职责 | 模块 | 职责 | 文件位置 | |-----|------|---------| | **Protocol Discriminator** | UART3协议识别与模式切换 | `uart3_router.[c/h]`(新增) | | **Passthrough Engine** | 透传数据缓冲与发送 | `uart3_passthrough.[c/h]`(新增) | | **CmdRouter** | 修改:集成协议识别器 | 修改现有 | ### 2.3 关键设计决策 #### 2.3.1 协议识别策略 **方案:基于启发式规则的早期判断** | 策略 | 描述 | 优势 | 劣势 | |------|------|------|------| | **起始符检测** | 以`$`开头视为指令候选 | 简单直接 | 透传数据中包含`$`会误判 | | **字符集分析** | 指令字符集受限(A-Z,0-9,`,*`) | 可区分乱码 | 需要一定积累 | | **超时触发** | 一定时间无`$`则判定为透传 | 防止长数据堆积 | 增加延迟 | | **帧间隔检测** | `\n`后重新判断 | 符合协议特性 | 依赖协议格式 | **选定方案:起始符 + 超时触发的混合模式** ``` 状态机逻辑: 1. 初始状态 INIT 2. 接收到'$' → 进入CMD_MODE,开始指令解析 3. 接收到非'$'字节 → 启动50ms超时计时器,进入SCAN状态 4. SCAN状态下: - 遇到'$' → 清空已接收数据,进入CMD_MODE - 超时(50ms) → 已接收数据判定为透传,进入PASSTHROUGH_MODE - 遇到'\n' → 已接收数据判定为透传,进入PASSTHROUGH_MODE 5. CMD_MODE下遇到完整帧(\r\n) → 执行指令,然后返回SCAN状态 ``` **为什么是50ms?** - UART3@9600bps,1字节 ≈ 1.04ms - 50ms ≈ 48字节数据的传输时间 - 足够接收一个短指令的开销 #### 2.3.2 路由切换机制 **场景分析:指令解析过程中发现格式错误** ``` 接收序列: $ABC,XYZ*12\r\n 正常流程: '$' → CMD_MODE 'A','B','C' → 解析命令 ',' → 解析参数 ... 完整帧 → 执行 异常流程(指令不存在): '$' → CMD_MODE ... 解析完成 → 执行 → 发现CMD未知 → 返回错误响应 → 但数据已完全接收 错误切换场景: 接收序列: $INVALID_DATA\r\n '$' → CMD_MODE ... 解析命令 'INVALID' 遇到 '\r' → 校验和验证失败 → 判定为"错误指令帧" → 不切换为透传,而是返回ERR响应 ``` **决策:指令解析失败不切换为透传** - 解析失败(校验和错误、未知命令等)仍按指令处理,返回错误响应 - 只有在**未开始解析**的情况下(SCAN状态超时),才判定为透传 - 这样可以处理`$`开头但格式错误的数据 **已缓存错误数据的处理**: ```c // 场景: SCAN状态下已接收10字节 "RAW_DATA$" // 此时用户发送的不是完整指令 处理策略: 1. 若 '$' 后续数据在100ms内未形成完整帧 2. 则将 '$' 及其之前的数据作为透传发送 3. '$' 及其后的数据重新进入SCAN状态判断 ``` #### 2.3.3 缓冲区设计 **方案:独立环形缓冲区** | 方案 | 优势 | 劣势 | 决策 | |------|------|------|------| | 复用UART3 RX环形缓冲区 | 节省内存 | 逻辑复杂,需要读写位置管理 | ❌ | | 独立透传缓冲区 | 逻辑清晰,隔离性好 | 额外内存开销 | ✅ | **缓冲区容量计算**: ``` 场景: UART3@9600bps 连续接收大数据 最大传输速率: 960 bytes/sec (9600/10) 透传目标: UART1@9600bps 发送 若UART1发送速度 < UART3接收速度,需要缓冲 设计考虑: - 环形缓冲区大小: 256字节 (2x UART3_RING_SIZE) - 理由: 透传数据通常是连续数据流,需要足够缓冲应对速度差 - 最坏情况: UART3全速接收,UART1正在发送历史数据 ``` **透传数据包节点设计**: ```c #define PASSTHROUGH_NODE_SIZE 128 // 每个透传节点大小 #define PASSTHROUGH_NODE_COUNT 4 // 节点数量 typedef struct { uint8_t data[PASSTHROUGH_NODE_SIZE]; uint16_t length; // 有效数据长度 uint16_t offset; // 已发送偏移 bool in_use; // 是否被占用 } passthrough_node_t; typedef struct { passthrough_node_t nodes[PASSTHROUGH_NODE_COUNT]; uint8_t write_index; // 写入位置 uint8_t read_index; // 读取位置 volatile uint16_t total_pending; // 待发送总字节数 } passthrough_queue_t; ``` #### 2.3.4 发送流控策略 **问题**:UART3接收速率(9600Baud) > UART1发送速率(可能受RF433限制) **方案:背压反馈 + 缓冲区管理** ```c // 透传引擎发送逻辑 void Passthrough_Task(void) { // 检查UART1是否可发送 if (!UART1_IsTxReady()) { return; // UART1忙,等待下次调度 } // 检查是否有待发送数据 if (g_passthrough_queue.total_pending == 0) { return; } // 获取下一个待发送节点 passthrough_node_t *node = &g_passthrough_queue.nodes[g_passthrough_queue.read_index]; if (node->offset < node->length) { // 发送一个字节 uint8_t byte = node->data[node->offset++]; MultiUART_Send(PORT_UART1, &byte, 1); } // 检查节点是否发送完成 if (node->offset >= node->length) { node->in_use = false; g_passthrough_queue.read_index = (g_passthrough_queue.read_index + 1) % PASSTHROUGH_NODE_COUNT; g_passthrough_queue.total_pending -= node->length; } } ``` **背压策略**: - 当透传缓冲区接近满时(`total_pending > 200`),记录警告日志 - 当缓冲区满时,新数据覆盖最旧数据(Lossy机制) - 始终保证透传通道可用,不阻塞UART3中断 --- ## 3. 模块详细设计 ### 3.1 协议识别器模块 #### 3.1.1 状态机设计 ``` ┌─────────────────────────────────────────────────┐ │ │ │ 状态转换图 │ │ │ └─────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ INIT │ 初始状态 │ │ └──────┬──────┘ │ │ │ │ │ byte != '$' │ byte == '$' │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ ├────────────────────────►│ SCAN │◄──────┐ │ │ └──────┬──────┘ │ │ │ │ │ │ │ byte == '$' │ │ │ │ (新起始符) │ 超时/'\n' │ '\r' │ │ │ │ │ │ ▼ │ │ │ ┌─────────────┐ │ │ │ │ CMD_MODE │───────┘ │ │ └──────┬──────┘ '\n' │ │ │ │ │ │ 帧解析完成 │ 解析失败 │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────┐ │ │ │ │ EXEC │───────┘ │ │ │ 执行指令 │ (返回SCAN) │ │ └─────────────┘ │ │ │ │ PASSTHROUGH_MODE: │ │ ┌─────────────┐ │ │ │ PASSTHROUGH│ ←── (SCAN超时/'\n') ─────────────────────────┐ │ │ └──────┬──────┘ │ │ │ │ │ │ │ │ 所有缓存透传数据 │ │ │ │ 发送完成 │ │ │ │ │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────┘ 状态说明: - INIT: 系统启动/复位状态,等待第一个字节 - SCAN: 扫描模式,等待指令起始符'$'或透传判定 - CMD_MODE: 指令解析模式,数据喂给CmdParser - EXEC: 指令执行模式 - PASSTHROUGH: 透传模式,数据转发至UART1 转换条件: 1. INIT → SCAN: 收到任何字节(非'$') 2. SCAN → CMD_MODE: 收到'$' 3. SCAN → PASSTHROUGH: 超时(50ms) 且有数据 或 收到'\n' 4. CMD_MODE → EXEC: 收到'\n' 且帧解析完成 5. CMD_MODE → SCAN: 收到'\r' 6. EXEC → SCAN: 指令执行完成 7. PASSTHROUGH → SCAN: 透传数据全部发送完成 ``` #### 3.1.2 伪代码实现 ```c /** * UART3协议识别器上下文 */ typedef struct { enum { STATE_INIT = 0, STATE_SCAN, STATE_CMD_MODE, STATE_EXEC, STATE_PASSTHROUGH } state; uint32_t last_byte_tick; // 上次收到字节的时间 uint32_t scan_start_tick; // SCAN状态开始时间 uint8_t scan_buffer[128]; // SCAN状态缓存 uint16_t scan_length; // SCAN缓存长度 bool pending_cmd; // CMD_MODE是否有待处理帧 uint32_t passthrough_bytes; // 透传字节计数 } uart3_protocol_context_t; // 超时阈值 #define SCAN_TIMEOUT_MS 50 // SCAN状态超时时间 #define CMD_COMPLETE_TIMEOUT_MS 200 // 指令帧完整接收超时 /** * @brief 协议识别器字节输入 * @note 由UART3中断调用,判断字节应该走指令路径还是透传路径 * @param byte: 接收字节 * @param current_tick: 当前系统时间 * @retval uint8_t: 识别结果 (ROUTE_CMD / ROUTE_PASSTHROUGH / ROUTE_NONE) */ uint8_t UART3_ProtocolDiscriminator(uint8_t byte, uint32_t current_tick) { uart3_protocol_context_t *ctx = &g_uart3_proto_ctx; switch (ctx->state) { case STATE_INIT: ctx->last_byte_tick = current_tick; if (byte == '$') { ctx->state = STATE_CMD_MODE; ctx->scan_length = 0; return ROUTE_CMD; // '$'交给CmdParser } else { // 非'$',开始SCAN ctx->state = STATE_SCAN; ctx->scan_start_tick = current_tick; ctx->scan_buffer[0] = byte; ctx->scan_length = 1; return ROUTE_NONE; // 暂时缓存 } case STATE_SCAN: ctx->last_byte_tick = current_tick; if (byte == '$') { // 发现新指令起始符 if (ctx->scan_length > 0) { // 之前的数据判定为透传 ctx->state = STATE_PASSTHROUGH; return ROUTE_PASSTHROUGH; // 触发透传 } else { // 空扫描,直接进入CMD_MODE ctx->state = STATE_CMD_MODE; return ROUTE_CMD; } } else if (byte == '\n') { // 帧结束,判定为透传 ctx->state = STATE_PASSTHROUGH; return ROUTE_PASSTHROUGH; } else { // 其他字符,加入SCAN缓存 if (ctx->scan_length < sizeof(ctx->scan_buffer)) { ctx->scan_buffer[ctx->scan_length++] = byte; } return ROUTE_NONE; } case STATE_CMD_MODE: ctx->last_byte_tick = current_tick; // 所有数据都交给CmdParser return ROUTE_CMD; case STATE_PASSTHROUGH: // 透传模式下,新数据继续加入透传缓存 // 注意:此时透传引擎应该正在发送已缓存数据 return ROUTE_PASSTHROUGH; default: ctx->state = STATE_INIT; return ROUTE_NONE; } } /** * @brief 协议识别器状态检查(定时调用) * @note 检查SCAN状态是否超时 * @retval bool: true=触发了透传,false=继续等待 */ bool UART3_ProtocolDiscriminator_CheckTimeout(uint32_t current_tick) { uart3_protocol_context_t *ctx = &g_uart3_proto_ctx; if (ctx->state == STATE_SCAN && ctx->scan_length > 0) { if (current_tick - ctx->scan_start_tick >= SCAN_TIMEOUT_MS) { ctx->state = STATE_PASSTHROUGH; return true; } } return false; } /** * @brief 获取透传模式下的缓存数据 * @param buffer: 输出缓冲区 * @param length: 数据长度 * @retval bool: true=有数据,false=无数据 */ bool UART3_GetPassthroughData(uint8_t *buffer, uint16_t *length) { uart3_protocol_context_t *ctx = &g_uart3_proto_ctx; if (ctx->state == STATE_PASSTHROUGH && ctx->scan_length > 0) { memcpy(buffer, ctx->scan_buffer, ctx->scan_length); *length = ctx->scan_length; ctx->scan_length = 0; ctx->state = STATE_INIT; // 重置状态 return true; } return false; } ``` #### 3.1.3 输入/输出接口 ```c // uart3_protocol_discriminator.h #ifndef __UART3_PROTOCOL_DISCRIMINATOR_H #define __UART3_PROTOCOL_DISCRIMINATOR_H #include #include // 路由结果枚举 typedef enum { ROUTE_NONE = 0, // 暂时缓存,不路由 ROUTE_CMD, // 路由至指令解析器 ROUTE_PASSTHROUGH // 路由至透传引擎 } route_result_t; // 初始化 void UART3_Protocol_Init(void); // 字节输入 route_result_t UART3_Protocol_FeedByte(uint8_t byte, uint32_t current_tick); // 超时检查(定时调用) bool UART3_Protocol_CheckTimeout(uint32_t current_tick); // 获取透传数据 bool UART3_Protocol_GetPassthroughData(uint8_t *buffer, uint16_t *length); // 获取当前状态(调试用) uint8_t UART3_Protocol_GetState(void); #endif ``` #### 3.1.4 与CmdParser的交互关系 **关键设计**: - 协议识别器位于`CmdRouter_Task`之前,先判断数据类型 - 只有识别为CMD的数据才喂给`CmdParser` - 透传数据写入独立缓冲区,由透传引擎处理 ```c // 修改后的 CmdRouter_Task() 逻辑 void CmdRouter_Task_UART3_Enhanced(void) { uint32_t current_tick = HAL_GetTick(); // 1. 读取UART3接收缓冲区 uint8_t byte; while (MultiUART_ReadByte(PORT_UART3, &byte) > 0) { // 2. 协议识别 route_result_t route = UART3_Protocol_FeedByte(byte, current_tick); switch (route) { case ROUTE_CMD: // 指令路径:喂给CmdParser CmdParser_FeedByte(byte, current_tick); LOG_DEBUG("UART3 CMD: 0x%02X", byte); break; case ROUTE_PASSTHROUGH: // 透传路径:写入透传缓冲区 Passthrough_PushByte(byte); LOG_DEBUG("UART3 PTX: 0x%02X", byte); break; case ROUTE_NONE: // 暂时缓存,不处理 break; } } // 3. 检查SCAN超时 if (UART3_Protocol_CheckTimeout(current_tick)) { // 超时触发透传 uint8_t buffer[128]; uint16_t length; if (UART3_Protocol_GetPassthroughData(buffer, &length)) { Passthrough_PushBuffer(buffer, length); LOG_INFO("UART3 PASSTHROUGH triggered: %d bytes", length); } } // 4. 透传引擎任务 Passthrough_Task(); // 5. 指令解析任务 if (CmdParser_HasCompleteFrame(NULL)) { // 指令解析完成,等待执行 } CmdParser_Task(); } ``` ### 3.2 透传引擎模块 #### 3.2.1 模块接口设计 ```c // uart3_passthrough.h #ifndef __UART3_PASSTHROUGH_H #define __UART3_PASSTHROUGH_H #include #include // 透传引擎配置 #define PASSTHROUGH_TX_BUFFER_SIZE 256 // 发送缓冲区大小 #define PASSTHROUGH_MAX_QUEUE_SIZE 4 // 最大队列节点数 #define PASSTHROUGH_NODE_SIZE 128 // 每个节点大小 // 透传统计结构 typedef struct { uint32_t total_bytes_sent; // 累计发送字节数 uint32_t total_packets; // 累计发送数据包数 uint32_t overflow_count; // 缓冲区溢出次数 uint32_t busy_count; // UART1忙等待次数 } passthrough_stats_t; /** * @brief 透传引擎初始化 */ void Passthrough_Init(void); /** * @brief 压入单字节到透传缓冲区 * @param byte: 待发送字节 * @retval bool: true=成功,false=缓冲区满 */ bool Passthrough_PushByte(uint8_t byte); /** * @brief 压入数据块到透传缓冲区 * @param data: 数据缓冲区 * @param length: 数据长度 * @retval uint16_t: 实际写入的字节数 */ uint16_t Passthrough_PushBuffer(const uint8_t *data, uint16_t length); /** * @brief 透传引擎任务(应周期性调用) * @note 每次调用尝试发送一个字节 */ void Passthrough_Task(void); /** * @brief 检查UART1是否就绪 * @retval bool: true=可发送,false=忙 */ bool Passthrough_CanSend(void); /** * @brief 获取透传统计信息 * @param stats: 统计结构指针 */ void Passthrough_GetStats(passthrough_stats_t *stats); /** * @brief 重置统计信息 */ void Passthrough_ResetStats(void); #endif ``` #### 3.2.2 核心数据结构 ```c // 透传数据节点 typedef struct { uint8_t data[PASSTHROUGH_NODE_SIZE]; uint16_t length; // 有效数据长度 uint16_t offset; // 已发送偏移 bool valid; // 节点是否有效 } passthrough_node_t; // 透传队列 typedef struct { passthrough_node_t nodes[PASSTHROUGH_MAX_QUEUE_SIZE]; uint8_t write_index; // 写入位置 uint8_t read_index; // 读取位置 volatile uint16_t pending_count; // 待发送字节总数 uint8_t current_node; // 当前处理节点 } passthrough_queue_t; // 透传引擎上下文 typedef struct { passthrough_queue_t queue; passthrough_stats_t stats; bool initialized; } passthrough_context_t; static passthrough_context_t g_passthrough_ctx; ``` #### 3.2.3 发送任务工作流程 ```c /** * @brief 透传引擎任务 * @note 在主循环中周期性调用,每次尝试发送一个字节 * 配合UART1 TX完成中断实现连续发送 */ void Passthrough_Task(void) { passthrough_context_t *ctx = &g_passthrough_ctx; // 检查是否有待发送数据 if (ctx->queue.pending_count == 0) { return; } // 检查UART1是否可发送 if (!Passthrough_CanSend()) { ctx->stats.busy_count++; return; } // 获取当前处理节点 passthrough_node_t *node = &ctx->queue.nodes[ctx->queue.read_index]; // 检查当前节点是否有效或有剩余数据 if (!node->valid || node->offset >= node->length) { // 节点数据已发送完毕,移动到下一个 node->valid = false; ctx->queue.read_index = (ctx->queue.read_index + 1) % PASSTHROUGH_MAX_QUEUE_SIZE; ctx->queue.current_node++; return; } // 发送一个字节 uint8_t byte = node->data[node->offset++]; MultiUART_Send(PORT_UART1, &byte, 1); ctx->queue.pending_count--; ctx->stats.total_bytes_sent++; } /** * @brief UART1发送完成回调 * @note 应在HAL_UART_TxCpltCallback中调用 */ void Passthrough_OnTxComplete(void) { // 触发下一次发送 Passthrough_Task(); } /** * @brief 压入数据块 */ uint16_t Passthrough_PushBuffer(const uint8_t *data, uint16_t length) { passthrough_context_t *ctx = &g_passthrough_ctx; uint16_t written = 0; for (uint16_t i = 0; i < length; i++) { // 寻找下一个可用节点 uint8_t next_index = (ctx->queue.write_index + 1) % PASSTHROUGH_MAX_QUEUE_SIZE; if (next_index == ctx->queue.read_index && ctx->queue.pending_count > 0) { // 队列满,溢出处理 ctx->stats.overflow_count++; break; } passthrough_node_t *node = &ctx->queue.nodes[ctx->queue.write_index]; // 如果当前节点已满,创建新节点 if (!node->valid || node->length >= PASSTHROUGH_NODE_SIZE) { if (node->valid) { ctx->queue.write_index = next_index; node = &ctx->queue.nodes[ctx->queue.write_index]; } node->length = 0; node->offset = 0; node->valid = true; } node->data[node->length++] = data[i]; ctx->queue.pending_count++; written++; } ctx->stats.total_packets++; return written; } bool Passthrough_CanSend(void) { // 检查UART1 TX是否忙 return (MultiUART_GetTxAvailable(PORT_UART1) > 0) && (g_passthrough_ctx.queue.pending_count > 0); } ``` ### 3.3 与现有模块的集成 #### 3.3.1 修改CmdRouter_Task ```c // cmd_router.c - 新增UART3专用处理 // UART3是否启用智能路由模式 #ifndef UART3_SMART_ROUTING_ENABLED #define UART3_SMART_ROUTING_ENABLED 1 #endif void CmdRouter_Task(void) { uint32_t current_tick = HAL_GetTick(); // 1. 处理UART1 (RF433) // ... 保持现有逻辑 ... // 2. 处理UART3 - 智能路由模式 #if UART3_SMART_ROUTING_ENABLED if (UART3_SMART_ROUTING_ENABLED) { UART3_SmartRouter_Task(current_tick); } #else // 原有逻辑:所有数据喂给CmdParser uint8_t byte; while (MultiUART_ReadByte(PORT_UART3, &byte) > 0) { CmdParser_SetSourcePort(PORT_UART3); CmdParser_FeedByte(byte, current_tick); } #endif // 3. 处理UART2 (调试串口) // ... 保持现有逻辑 ... // 4. 执行已解析的指令 CmdParser_Task(); } /** * @brief UART3智能路由任务 */ static void UART3_SmartRouter_Task(uint32_t current_tick) { uint8_t byte; // 读取所有待处理的字节 while (MultiUART_ReadByte(PORT_UART3, &byte) > 0) { // 协议识别 route_result_t route = UART3_Protocol_FeedByte(byte, current_tick); switch (route) { case ROUTE_CMD: // 指令路径 CmdParser_SetSourcePort(PORT_UART3); CmdParser_FeedByte(byte, current_tick); LOG_DEBUG("[UART3] CMD byte: 0x%02X", byte); break; case ROUTE_PASSTHROUGH: // 透传路径 Passthrough_PushByte(byte); LOG_DEBUG("[UART3] PTX byte: 0x%02X", byte); break; case ROUTE_NONE: // 缓存中,不处理 break; } } // 检查SCAN超时 if (UART3_Protocol_CheckTimeout(current_tick)) { uint8_t buffer[128]; uint16_t length; if (UART3_Protocol_GetPassthroughData(buffer, &length)) { Passthrough_PushBuffer(buffer, length); LOG_INFO("[UART3] PASSTHROUGH: %d bytes queued", length); } } // 执行透传引擎任务 Passthrough_Task(); } ``` #### 3.3.2 透传引擎发送接口 ```c // 复用现有的 MultiUART_Send(PORT_UART1, data, len) // 透传引擎将数据写入UART1的发送缓冲区 // 由MultiUART_TxCpltCallback驱动后续发送 ``` ## 附录A:关键数据结构定义 ```c // 协议识别器上下文 typedef struct { uint8_t state; // 当前状态 uint32_t last_byte_tick; // 上次收到字节时间 uint32_t scan_start_tick; // SCAN状态开始时间 uint8_t scan_buffer[128]; // SCAN状态缓存 uint16_t scan_length; // SCAN缓存长度 uint32_t cmd_frame_count; // 指令帧计数 uint32_t passthrough_bytes; // 透传字节计数 uint32_t mode_switch_count; // 模式切换次数 } uart3_protocol_context_t; // 透传节点 typedef struct { uint8_t data[128]; uint16_t length; uint16_t offset; bool valid; } passthrough_node_t; // 透传队列 typedef struct { passthrough_node_t nodes[4]; uint8_t write_index; uint8_t read_index; volatile uint16_t pending_count; } passthrough_queue_t; // 透传引擎上下文 typedef struct { passthrough_queue_t queue; uint32_t total_bytes_sent; uint32_t total_packets; uint32_t overflow_count; uint32_t busy_count; bool initialized; } passthrough_context_t; ``` ## 附录D:配置参数 ```c // uart3_smart_router_config.h #ifndef __UART3_SMART_ROUTER_CONFIG_H #define __UART3_SMART_ROUTER_CONFIG_H // 协议识别器配置 #define SCAN_TIMEOUT_MS 50 // SCAN状态超时时间 #define SCAN_BUFFER_SIZE 128 // SCAN缓存大小 #define CMD_COMPLETE_TIMEOUT_MS 200 // 指令帧完整接收超时 // 透传引擎配置 #define PASSTHROUGH_NODE_SIZE 128 // 每个透传节点大小 #define PASSTHROUGH_MAX_NODES 4 // 最大节点数 #define PASSTHROUGH_TOTAL_BUFFER (PASSTHROUGH_NODE_SIZE * PASSTHROUGH_MAX_NODES) // 512 bytes // 功能开关 #define UART3_SMART_ROUTING_ENABLED 1 // 1=启用智能路由 0=原有逻辑 #define PASSTHROUGH_LOG_ENABLED 1 // 1=启用透传日志 // 调试配置 #define DEBUG_PROTOCOL_DISCRIMINATOR 1 // 协议识别器调试 #define DEBUG_PASSTHROUGH_ENGINE 1 // 透传引擎调试 #endif ```