43 KiB
43 KiB
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 | 无(单向透传) |
具体场景:
-
场景A - 纯指令流
- UART3接收:
$DI*2F\r\n - 识别为指令 → CmdParser解析 → 执行DI查询 → 响应返回UART3
- UART3接收:
-
场景B - 纯透传数据
- UART3接收:
Hello World\r\n(无$起始符) - 识别为透传 → 直接转发至UART1(RF433发送)
- UART3接收:
-
场景C - 混合交织数据
- UART3接收:
$DI*2F\r\nDATA_TO_FWD\r\n - 前半段识别为指令,执行后
- 后半段无
$起始,识别为透传,转发至UART1
- UART3接收:
-
场景D - 透传数据中出现
$字符- UART3接收:
Some Data $10 more - 由于前面不是
$开头,前半部分透传 - 后续若出现完整指令格式则按指令处理
- UART3接收:
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数据处理流程如下:
// 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(); // 处理已解析完成的指令
}
问题分析:
- 无区分机制:所有UART3数据统一喂给
CmdParser,无法处理透传数据 - 解析器污染:非指令数据进入解析器会触发错误计数
- 缺乏透发转发:即使识别出透传数据,也没有转发至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状态超时),才判定为透传
- 这样可以处理
$开头但格式错误的数据
已缓存错误数据的处理:
// 场景: 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正在发送历史数据
透传数据包节点设计:
#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限制)
方案:背压反馈 + 缓冲区管理
// 透传引擎发送逻辑
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 伪代码实现
/**
* 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 输入/输出接口
// uart3_protocol_discriminator.h
#ifndef __UART3_PROTOCOL_DISCRIMINATOR_H
#define __UART3_PROTOCOL_DISCRIMINATOR_H
#include <stdint.h>
#include <stdbool.h>
// 路由结果枚举
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 - 透传数据写入独立缓冲区,由透传引擎处理
// 修改后的 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 模块接口设计
// uart3_passthrough.h
#ifndef __UART3_PASSTHROUGH_H
#define __UART3_PASSTHROUGH_H
#include <stdint.h>
#include <stdbool.h>
// 透传引擎配置
#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 核心数据结构
// 透传数据节点
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 发送任务工作流程
/**
* @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
// 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 透传引擎发送接口
// 复用现有的 MultiUART_Send(PORT_UART1, data, len)
// 透传引擎将数据写入UART1的发送缓冲区
// 由MultiUART_TxCpltCallback驱动后续发送
附录A:关键数据结构定义
// 协议识别器上下文
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:配置参数
// 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