Files
433_STM32/docs/UART3智能数据路由与透传功能开发计划.md
zhongxuanzhen 0eea5c1424 3.27_433:实验并验证485发送数据透传至RF433模块,并在外部设备成功接收
- 新增协议识别器状态机,实现指令与透传数据的自动识别
2026-03-27 19:58:20 +08:00

1079 lines
43 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UART3智能数据路由与透传功能开发计划
**项目名称**E32-433TBH-SC UART3智能数据路由与透传扩展
**版本**V1.0
**制定日期**2026-03-27
**适用范围**STM32F103嵌入式系统
**基于系统**:多通信接口统一指令处理系统 V1.0
---
## 1. 需求与目标
### 1.1 功能需求详述
**背景**:当前系统已通过`multi_uart_router``cmd_router`模块实现了三端口UART1/UART2/UART3ASCII指令的统一解析与响应路由。
**新增功能目标**使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`(无`$`起始符)
- 识别为透传 → 直接转发至UART1RF433发送
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@9600bps1字节 ≈ 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 <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`
- 透传数据写入独立缓冲区,由透传引擎处理
```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 <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 核心数据结构
```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
```