1079 lines
43 KiB
Markdown
1079 lines
43 KiB
Markdown
|
|
# 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 <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
|
|||
|
|
```
|