feat(vd960DBN): UART1 ↔ Air780 4G 通道 (协议 §6.1 纯字节流透传)
需求(2026-09-10): 打通 vd960DBN 与 vd960Air(Air780) 两侧串口对接逻辑 ——
DBN 侧 UART1(PB6/PB7) 通道 + UART2↔UART1 双向透传。
协议依据 DLD960_IoT_MQTT协议 §6.1(V1.12 方案C):
上行 Loop 0x7F(UART2) --原样转发--> UART1 --> Air780 解析转 JSON --> MQTT
下行 平台 JSON --> Air780 --转 0x7F 帧--> DBN --0x7F(UART2)--> Loop
DBN 只做字节流透传, 零业务转换; 魔数分流: 0x7F -> UART2, 0x8F -> DBN 本地
实现:
- usart_biz.c +242 行: UART1 通道整段
* uart1_dma_init(): USART1 重映射 PB6(TX)/PB7(RX), 115200 8N1,
RX 走 DMA1_Ch5 循环 512B (UART2 占 Ch6/Ch7, 不冲突)
* uart1_feed_byte()/uart1_frame_ready()/uart1_resync(): 独立帧装配器
* uart1_dispatch_frame(): 按魔数分流
* uart1_dma_poll(): 主循环消费 DMA 缓冲
* uart_srv() 内插入上行转发(484-490): 紧接 lup_process_frame 之后、
InitPkgUart 消费之前, 且只转发 lup_verify_checksum()==0 的完整帧
(位置必须在 BLE 分支改写 pkg[0]=0x8F 之前, 否则会给 Air780 发错魔数帧)
- cmcng.h: 声明 uart1_dma_init/poll + 5 个通道计数
- peripheral_main.c: 主循环加 uart1_dma_poll() (置于 MK_UART_SRV fault marker
之外, 不改变既有 marker 语义)
三个关键决定:
1. 接收走 DMA 而非 RXNE 中断 —— 理由同 UART2 方案A(2026-08-17): PRINT 临界区
关中断 ~7.4ms / BLE 栈回调 / SPI 擦除 45ms 等长阻塞窗口会屏蔽 RXNE 丢字节。
"不丢帧"是 Air780 沿检测(loop_state 变化沿)判车的硬前提。
2. 帧装配器独立于 g_lup_parser —— 后者硬编码专供 UART2/Loop 通道, 两条流混进
同一状态机必然互相残杀; 只复用纯函数 lup_verify_checksum()。
3. 坏帧回找魔数 resync —— 防"单帧损坏 → 后续帧全部失步、链路长时间瞎掉"。
与 printf 共用 USART1 (按用户 2026-09-10 指示: printf 串口不动, 4G 时禁用 debug):
- 开关 UART1_AIR780_EN, 默认 (DEBUG == 0)
- 4G 版本 -DDEBUG=0 编译即自动启用: #if(DEBUG) 为假 -> PRINT 空宏, 且
USART_Printf_Init() 里 USART1 配置分支整段不编译(已核对 USART_Init/USART_Cmd
均在 #if(DEBUG == DEBUG_UART1) 内)
- 调试版本通道为空实现; debug.h/debug.c 一行未改, printf 行为完全不变
验证:
- host 单测: 从 usart_biz.c 原样抽取装配器 + 逐字复刻 loop_uart_proto.c 的
XOR/SUM 校验, gcc -Wall -Wextra -Werror 零警告, 13 组用例全部通过 ——
含"坏帧后紧跟好帧仍送达"、"截断帧后只交付 1 帧完整好帧"、70B 最大帧、
LEN 非法/超长、0x9F(Loop OTA 魔数)忽略、上行闸门只放行校验通过帧
- 待板级验证: 本机无 RISC-V 工具链, 固件编译与实机联调尚未进行
现场核对: 新增 g_uart1_fwd_to_air / _to_loop / _local_cnt / _badchk / _drop
计数, 判据为"g_uart1_fwd_to_air 与 Air780 侧实收帧数相等"
文档: devlog 新增 2026-09-10 条目(含接线说明 PB6/PB7 交叉 + 待办清单)
This commit is contained in:
@@ -154,4 +154,13 @@ void uart_srv(void);
|
||||
void UART2_SendString(uint8_t *buf, uint16_t len);
|
||||
void UART1_SendString(uint8_t *buf, uint16_t len);
|
||||
|
||||
/* ---- 4G 通道: UART1 <-> Air780 (协议 §6.1 纯字节流透传) ---- */
|
||||
void uart1_dma_init(void); /* 初始化 (DEBUG!=0 时为空实现) */
|
||||
void uart1_dma_poll(void); /* 主循环轮询: 消费 DMA 缓冲 -> 分流 */
|
||||
extern uint32_t g_uart1_fwd_to_air; /* 上行转发帧数 (UART2->UART1) */
|
||||
extern uint32_t g_uart1_fwd_to_loop; /* 下行转发帧数 (UART1->UART2, 0x7F) */
|
||||
extern uint32_t g_uart1_local_cnt; /* 下行本地处理帧数 (0x8F) */
|
||||
extern uint32_t g_uart1_badchk; /* 下行校验失败帧数 */
|
||||
extern uint32_t g_uart1_drop; /* 下行 DMA 溢出丢弃次数 */
|
||||
|
||||
#endif /* INCLUDE_CMCNG_H_ */
|
||||
|
||||
@@ -275,6 +275,7 @@ void Main_Circulation(void)
|
||||
FAULT_MARKER(MK_UART_SRV_IN);
|
||||
uart_srv();
|
||||
FAULT_MARKER(MK_UART_SRV_OUT);
|
||||
uart1_dma_poll(); /* 4G 通道下行收帧 (协议 §6.1): 置于 fault marker 之外, 不改变 MK_UART_SRV 语义 */
|
||||
ota_poll(); /* OTA 刷写状态机 (V1.08): 非阻塞 tick 驱动, 先消费 UART2 ACK 再推进 */
|
||||
snap_flush(); /* 快照落盘: 无条件挂主循环 (原在 iot_enable 分支, TCP 模式不落盘) */
|
||||
FAULT_MARKER(MK_POLL_BLE_IN);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "cmcng.h"
|
||||
#include "debug.h" /* DEBUG 宏: 4G 版本以 -DDEBUG=0 编译, 此时 printf 不占用 USART1 */
|
||||
#include "loop_uart_proto.h"
|
||||
#include <string.h>
|
||||
#include "dbn_ble_srv.h"
|
||||
@@ -113,6 +114,238 @@ void uart2_dma_poll(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*==================== UART1 <-> Air780 4G 通道 (协议 §6.1) ====================
|
||||
* 定位: 4G 通道中 vd960DBN 只做"字节流透传", 零业务转换 ——
|
||||
* 上行: Loop 0x7F 帧(UART2) --校验通过后原样转发--> UART1 --> Air780 解析转 JSON
|
||||
* 下行: Air780 帧(UART1) --魔数分流--> 0x7F: 转发 UART2(给 vd960Loop)
|
||||
* 0x8F: DBN 本地处理
|
||||
*
|
||||
* 硬约束:
|
||||
* 1) 只转发 lup_verify_checksum() 通过的完整帧。Air780 侧靠 loop_state 变化沿判车,
|
||||
* 转发一个残缺帧 = 凭空多判一台车。
|
||||
* 2) 接收必须走 DMA (与 UART2 方案A 同理): PRINT 临界区 / BLE 栈回调 / SPI 擦除等
|
||||
* 长阻塞窗口会屏蔽 RXNE 中断 -> 丢字节 -> 校验失败。本通道"不丢帧"是硬要求。
|
||||
* 3) 帧装配器独立于 g_lup_parser: 后者专供 UART2(Loop) 通道, 两条流的字节绝不能
|
||||
* 混进同一个状态机 (混了就是互相残杀)。
|
||||
*
|
||||
* 帧格式 (与 Loop/BLE 协议完全一致):
|
||||
* [7F][Addr][LEN][CMD][Value: LEN-1B][XOR][SUM] 总长 = LEN + 5
|
||||
* 校验覆盖 Addr..Value (不含 magic), 复用 lup_verify_checksum()
|
||||
*
|
||||
* 编译开关:
|
||||
* 调试版 (DEBUG != 0): printf 占用 USART1/PB6, 本通道不参与编译 -> 走空实现
|
||||
* 4G 版 (DEBUG == 0): -DDEBUG=0 编译即自动启用 (PB6/PB7 由本文件接管)
|
||||
* 亦可 -DUART1_AIR780_EN=1/0 强制覆盖
|
||||
*===========================================================================*/
|
||||
#ifndef UART1_AIR780_EN
|
||||
#define UART1_AIR780_EN (DEBUG == 0)
|
||||
#endif
|
||||
|
||||
#define UART1_MAGIC_LOOP LUP_MAGIC /* 0x7F: 转发给 vd960Loop */
|
||||
#define UART1_MAGIC_LOCAL 0x8F /* 0x8F: DBN 本地处理 (BLE 侧同名魔数) */
|
||||
#define UART1_DMA_BUF_LEN 512
|
||||
|
||||
#if (UART1_AIR780_EN)
|
||||
|
||||
static uint8_t uart1_dma_buf[UART1_DMA_BUF_LEN] __attribute__((aligned(4)));
|
||||
static uint16_t uart1_dma_last = 0; /* 主循环消费位置 (DMA 写指针由硬件维护) */
|
||||
|
||||
/* 下行帧装配器 (独立实例, 不与 g_lup_parser 共享) */
|
||||
static uint8_t uart1_rx_frame[LUP_MAX_PKG_LEN];
|
||||
static uint16_t uart1_rx_idx = 0; /* 已收字节数 */
|
||||
static uint16_t uart1_rx_need = 0; /* 本帧总长, 0 = 尚未定长 */
|
||||
|
||||
/* 通道计数: 现场核对"上行转发数 == Air780 实收数", 比翻日志可靠 */
|
||||
uint32_t g_uart1_fwd_to_air = 0; /* 上行: UART2 -> UART1 转发帧数 */
|
||||
uint32_t g_uart1_fwd_to_loop = 0; /* 下行: UART1 -> UART2 转发帧数 */
|
||||
uint32_t g_uart1_local_cnt = 0; /* 下行: 本地处理帧数 (0x8F) */
|
||||
uint32_t g_uart1_badchk = 0; /* 下行: 校验失败帧数 */
|
||||
uint32_t g_uart1_drop = 0; /* 下行: DMA 溢出丢弃次数 */
|
||||
|
||||
/* ---- 下行分流 ---- */
|
||||
static void uart1_dispatch_frame(uint8_t *pkg, uint16_t len)
|
||||
{
|
||||
if (pkg[0] == UART1_MAGIC_LOOP) {
|
||||
/* 0x7F -> 原样转发给 vd960Loop */
|
||||
g_uart1_fwd_to_loop++;
|
||||
UART2_SendString(pkg, len);
|
||||
} else {
|
||||
/* 0x8F -> DBN 本地处理 (预留: 具体行为待协议细化, 先只计数) */
|
||||
g_uart1_local_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 坏帧回找魔数: 把 [1..len) 里第一个魔数起的残余搬到头部继续装配 ----
|
||||
* 防"单帧损坏 -> 后续帧全部失步" */
|
||||
static void uart1_resync(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
for (i = 1; i < len; i++) {
|
||||
if (buf[i] == UART1_MAGIC_LOOP || buf[i] == UART1_MAGIC_LOCAL) {
|
||||
uint16_t k, n = 0;
|
||||
for (k = i; k < len; k++) {
|
||||
uart1_rx_frame[n++] = buf[k];
|
||||
}
|
||||
uart1_rx_idx = n;
|
||||
uart1_rx_need = 0;
|
||||
if (n >= 3) {
|
||||
uint8_t lf = uart1_rx_frame[2];
|
||||
uint16_t need = (uint16_t)lf + 5;
|
||||
if (lf >= 1 && need <= LUP_MAX_PKG_LEN) {
|
||||
uart1_rx_need = need;
|
||||
} else {
|
||||
uart1_rx_idx = 0; /* LEN 非法: 残余作废 */
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* 残余中没有魔数 -> 全部丢弃 */
|
||||
}
|
||||
|
||||
/* ---- 定长收满 -> 校验 -> 分流 ---- */
|
||||
static void uart1_frame_ready(void)
|
||||
{
|
||||
uint16_t len = uart1_rx_idx;
|
||||
uart1_rx_idx = 0;
|
||||
uart1_rx_need = 0;
|
||||
if (lup_verify_checksum(uart1_rx_frame, len) == 0) {
|
||||
uart1_dispatch_frame(uart1_rx_frame, len);
|
||||
} else {
|
||||
g_uart1_badchk++;
|
||||
uart1_resync(uart1_rx_frame, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- UART1 字节流装配 ---- */
|
||||
static void uart1_feed_byte(uint8_t b)
|
||||
{
|
||||
uint8_t guard = 0;
|
||||
|
||||
if (uart1_rx_idx == 0) {
|
||||
if (b == UART1_MAGIC_LOOP || b == UART1_MAGIC_LOCAL) {
|
||||
uart1_rx_frame[0] = b;
|
||||
uart1_rx_idx = 1;
|
||||
}
|
||||
return; /* 非魔数字节: 帧间空闲, 直接丢 */
|
||||
}
|
||||
|
||||
if (uart1_rx_idx >= LUP_MAX_PKG_LEN) { /* 防御: 正常流程到不了 */
|
||||
uart1_rx_idx = 0;
|
||||
uart1_rx_need = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uart1_rx_frame[uart1_rx_idx++] = b;
|
||||
|
||||
if (uart1_rx_idx == 3) { /* [7F][Addr][LEN] 到手 -> 定长 */
|
||||
uint8_t lf = uart1_rx_frame[2];
|
||||
uint16_t need = (uint16_t)lf + 5;
|
||||
if (lf < 1 || need > LUP_MAX_PKG_LEN) {
|
||||
uart1_rx_idx = 0; /* LEN 非法 -> 重新找魔数 */
|
||||
uart1_rx_need = 0;
|
||||
return;
|
||||
}
|
||||
uart1_rx_need = need;
|
||||
}
|
||||
|
||||
/* 校验失败后 resync 可能让残余已够长, 故用 while (guard 防跑飞) */
|
||||
while (uart1_rx_need != 0 && uart1_rx_idx >= uart1_rx_need && guard++ < 8) {
|
||||
uart1_frame_ready();
|
||||
}
|
||||
}
|
||||
|
||||
void uart1_dma_init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||
USART_InitTypeDef USART_InitStructure = {0};
|
||||
DMA_InitTypeDef DMA_InitStructure;
|
||||
|
||||
/* PB6 = USART1_TX / PB7 = USART1_RX (USART1 重映射) */
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
|
||||
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
|
||||
|
||||
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /* Tx */
|
||||
GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* Rx */
|
||||
GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||
|
||||
USART_InitStructure.USART_BaudRate = 115200; /* 与 Air780 侧 uart1 一致 */
|
||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
||||
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
||||
USART_Init(USART1, &USART_InitStructure);
|
||||
USART_Cmd(USART1, ENABLE);
|
||||
|
||||
/* RX 用 DMA1_Ch5 循环缓冲 (UART2 占用 Ch6/Ch7, 通道不冲突) */
|
||||
DMA_DeInit(DMA1_Channel5);
|
||||
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DATAR);
|
||||
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uart1_dma_buf;
|
||||
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
||||
DMA_InitStructure.DMA_BufferSize = UART1_DMA_BUF_LEN;
|
||||
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
||||
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
||||
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
||||
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
||||
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
|
||||
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
|
||||
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
||||
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
|
||||
|
||||
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
|
||||
DMA_Cmd(DMA1_Channel5, ENABLE);
|
||||
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); /* DMA 接管, 逐字节中断不再需要 */
|
||||
|
||||
uart1_dma_last = 0;
|
||||
uart1_rx_idx = 0;
|
||||
uart1_rx_need = 0;
|
||||
}
|
||||
|
||||
/* 主循环每轮调用: 消费 DMA 环形缓冲新字节 -> 装配 -> 分流 */
|
||||
void uart1_dma_poll(void)
|
||||
{
|
||||
uint16_t cur = (uint16_t)(UART1_DMA_BUF_LEN - DMA_GetCurrDataCounter(DMA1_Channel5));
|
||||
uint16_t n = (uint16_t)((cur + UART1_DMA_BUF_LEN - uart1_dma_last) & (UART1_DMA_BUF_LEN - 1));
|
||||
|
||||
if (n == 0) return;
|
||||
|
||||
/* 溢出保护: 未消费超过半缓冲 -> 已被 DMA 覆盖, 复位装配器重同步 */
|
||||
if (n > (UART1_DMA_BUF_LEN / 2)) {
|
||||
g_uart1_drop++;
|
||||
uart1_rx_idx = 0;
|
||||
uart1_rx_need = 0;
|
||||
uart1_dma_last = cur;
|
||||
return;
|
||||
}
|
||||
|
||||
while (n--) {
|
||||
uint8_t b = uart1_dma_buf[uart1_dma_last];
|
||||
uart1_dma_last = (uint16_t)((uart1_dma_last + 1) & (UART1_DMA_BUF_LEN - 1));
|
||||
uart1_feed_byte(b);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !UART1_AIR780_EN: 调试版 printf 占用 USART1 -> 空实现, 保证链接一致 */
|
||||
|
||||
void uart1_dma_init(void) { }
|
||||
void uart1_dma_poll(void) { }
|
||||
uint32_t g_uart1_fwd_to_air = 0;
|
||||
uint32_t g_uart1_fwd_to_loop = 0;
|
||||
uint32_t g_uart1_local_cnt = 0;
|
||||
uint32_t g_uart1_badchk = 0;
|
||||
uint32_t g_uart1_drop = 0;
|
||||
|
||||
#endif /* UART1_AIR780_EN */
|
||||
|
||||
void uart_init(void){
|
||||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||
USART_InitTypeDef USART_InitStructure = {0};
|
||||
@@ -152,6 +385,7 @@ void uart_init(void){
|
||||
USART_Cmd(USART2, ENABLE);
|
||||
|
||||
uart2_dma_init(); /* DMA 循环接收接管 (2026-08-17) */
|
||||
uart1_dma_init(); /* 4G 通道 UART1↔Air780 初始化 (协议 §6.1); DEBUG!=0 时为空实现 */
|
||||
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
|
||||
@@ -247,6 +481,14 @@ void uart_srv(void)
|
||||
// 其他: 校验后匹配挂起命令
|
||||
lup_process_frame(g_pkg_uart_2.pkg, g_pkg_uart_2.offset);
|
||||
|
||||
/* --- 4G 通道上行转发 (协议 §6.1) ---
|
||||
校验通过的完整帧原样送 Air780; 必须置于下方 InitPkgUart 消费之前。
|
||||
只转发 verify 通过的帧: Air780 靠 loop_state 变化沿判车, 残缺帧 = 多判一台车 */
|
||||
if (lup_verify_checksum(g_pkg_uart_2.pkg, g_pkg_uart_2.offset) == 0) {
|
||||
g_uart1_fwd_to_air++;
|
||||
UART1_SendString(g_pkg_uart_2.pkg, g_pkg_uart_2.offset);
|
||||
}
|
||||
|
||||
// --- 传感器上报 (0xC0) 分流 ---
|
||||
// 回调已处理 TCP 推送,此处仅处理 BLE 转发
|
||||
if(cmd == LUP_CMD_SENSOR_REPORT)
|
||||
|
||||
@@ -4,6 +4,80 @@
|
||||
>
|
||||
> 项目定位: DLD960 通信板 — BLE 配网、TCP JSON 协议服务、Loop MCU 串口桥接
|
||||
|
||||
## 2026-09-10 — vd960DBN UART1 ↔ Air780 4G 通道实现(协议 §6.1 纯透传)
|
||||
|
||||
> 用户需求 2026-09-10:把 vd960DBN 与 vd960Air(Air780) 两侧的串口对接逻辑做出来 —— DBN 侧 UART1(PB6/PB7) 通道 + UART2↔UART1 双向透传。
|
||||
> **关闭**:V1.12 条目"vd960DBN 侧开发计划"第 1 条;V1.11 条目第 1、2 条(UART1 通道 / 双向透传)。
|
||||
|
||||
### 实现
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `APP/usart_biz.c` | +242 行:UART1 通道整段(118-347);上行转发插入 `uart_srv()`(484-490) |
|
||||
| `APP/include/cmcng.h` | 声明 `uart1_dma_init/poll` + 5 个通道计数 |
|
||||
| `APP/peripheral_main.c` | 主循环加 `uart1_dma_poll()`(置于 `MK_UART_SRV` fault marker **之外**,不改变既有 marker 语义) |
|
||||
|
||||
**上行**(Loop → Air780):`uart_srv()` 内 `lup_process_frame()` 之后、**`InitPkgUart` 消费之前**插入转发,
|
||||
且**只转发 `lup_verify_checksum()==0` 的完整帧**。插入点必须在 BLE 分支改写 `pkg[0]=0x8F` 之前 ——
|
||||
否则会把已改魔数的帧发给 Air780。
|
||||
|
||||
**下行**(Air780 → DBN):UART1 收帧 → **魔数分流**:`0x7F` → `UART2_SendString()` 给 Loop;`0x8F` → DBN 本地处理(预留,当前仅计数)。
|
||||
|
||||
### 三个关键设计决定(含理由)
|
||||
|
||||
1. **接收走 DMA(DMA1_Ch5 循环 + 512B),不用 RXNE 中断。** 理由与 UART2 方案A(2026-08-17) 完全相同:
|
||||
`PRINT` 临界区关中断 ~7.4ms / BLE 栈回调 / SPI 擦除 45ms 等长阻塞窗口会屏蔽 RXNE → 丢字节 → 校验失败。
|
||||
本通道"不丢帧"是 Air780 沿检测的硬前提。UART2 占 DMA1_Ch6/Ch7,**Ch5 空闲,无冲突**。
|
||||
2. **帧装配器独立于 `g_lup_parser`。** `lup_feed_byte()` 硬编码操作全局 `g_lup_parser`(专供 UART2/Loop 通道),
|
||||
两条字节流混进同一状态机必然互相残杀。故 UART1 用独立装配器,只复用纯函数 `lup_verify_checksum()`。
|
||||
3. **坏帧回找魔数(resync)。** 校验失败时在已收缓冲里从 idx=1 起回找下一个 `0x7F/0x8F`,残余搬到头部继续装配,
|
||||
防"单帧损坏 → 后续帧全部失步、链路长时间瞎掉"。
|
||||
|
||||
### 与 printf 共用 USART1 的处理(按用户指示)
|
||||
|
||||
用户 2026-09-10 指示:**printf 对应串口不动;不会同时使用 Debug 打印和 4G 通信,4G 通信时禁用 debug 即可。**
|
||||
|
||||
- 现状:`debug.c` 的 `USART_Printf_Init()` 把 USART1 重映射到 PB6 做 printf,且 `PRINT` 走 `__disable_irq()`。
|
||||
- 落地:本通道用编译开关 `UART1_AIR780_EN`(默认 `(DEBUG == 0)`)—— **4G 版本以 `-DDEBUG=0` 编译即自动启用**。
|
||||
此时 `#if(DEBUG)` 为假:`PRINT` 是空宏,且 `USART_Printf_Init()` 里 USART1 的配置分支整段不参与编译
|
||||
(已核对 `USART_Init/USART_Cmd` 均在 `#if(DEBUG == DEBUG_UART1)` 内)→ USART1 干净地交给本文件。
|
||||
- 调试版(DEBUG!=0)本通道编译为空实现,**`debug.h`/`debug.c` 一行未改**,printf 行为完全不变。
|
||||
- ⚠ 遗留:`net_srv.c:125` 有一处裸 `printf()`(错误路径),`DEBUG=0` 时仍会调用,建议改为 `PRINT`(列入待办)。
|
||||
|
||||
### 验证
|
||||
|
||||
- **host 单测**(本机无 RISC-V 工具链,无法编译固件):从 `usart_biz.c` **原样抽取**装配器代码 + 逐字复刻
|
||||
`loop_uart_proto.c` 的 XOR/SUM 校验,gcc 编译(`-Wall -Wextra -Werror` **零警告**)跑 13 组用例 **全部通过**:
|
||||
单帧 `0x7F`/`0x8F` 分流、带数据帧逐字节一致、70B 最大帧、校验和破坏不转发、
|
||||
**坏帧后紧跟好帧仍送达**、**截断帧后只交付 1 帧完整好帧**、乱码前缀、`LEN=0` 非法、`LEN` 超长、三帧连发、
|
||||
`0x9F`(Loop OTA 魔数) 忽略、上行闸门只放行 verify 通过的帧。
|
||||
- **待板级验证**:需 MounRiver 工具链编译 + 实机(本机无 riscv 工具链,固件编译尚未跑过)。
|
||||
|
||||
### 现场核对手段(比翻日志可靠)
|
||||
|
||||
新增 5 个全局计数(`cmcng.h` 已声明,可调试器 Watch):
|
||||
`g_uart1_fwd_to_air`(上行转发帧数)/ `g_uart1_fwd_to_loop`(下行 0x7F)/ `g_uart1_local_cnt`(下行 0x8F)/
|
||||
`g_uart1_badchk`(校验失败)/ `g_uart1_drop`(DMA 溢出)。
|
||||
|
||||
**判据:跑一段时间后 `g_uart1_fwd_to_air` 与 Air780 侧实收帧数必须相等。**
|
||||
|
||||
### 接线(供硬件核对)
|
||||
|
||||
- DBN `USART1` 重映射:**PB6 = TX / PB7 = RX**,115200 8N1
|
||||
- 与 Air780 必须**交叉**:`DBN_PB6(TX) → Air780_RX`,`DBN_PB7(RX) ← Air780_TX`
|
||||
- 波特率两侧一致:115200(Air780 侧日志 `Uart_ChangeBR uart1, 115200`)
|
||||
|
||||
### 仍待办
|
||||
|
||||
- [ ] `0x8F` 下行的本地处理行为(当前仅计数;协议未细化具体动作)
|
||||
- [ ] `0x7D` 握手/配置同步帧(V1.11 曾提,未进正式协议,暂预留)
|
||||
- [ ] BLE 设置服务器/topic 时同步下发 Air780 配置(V1.12 第 2 条)
|
||||
- [ ] 通道切换策略(V1.12 第 3 条,待讨论;暂定默认 4G)
|
||||
- [ ] `net_srv.c:125` 裸 `printf` → `PRINT`
|
||||
- [ ] 板级联调:编译烧录 + 帧计数对齐 + 断线重连
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-31 — MQTT 协议 V1.13:initialize extra_info 增加 imei/iccid 可选字段
|
||||
|
||||
> 用户需求 2026-08-31:initialize 指令的 `extra_info` 增加可选字段 `imei` / `iccid`(4G 模块 IMEI / 流量卡 ICCID)。
|
||||
|
||||
Reference in New Issue
Block a user