fix(vd960DBN): BLE→Loop OTA 失效修复 — 0x9F 帧透传回 BLE
DMA 改造(fed4335)后 UART2 RX 走 lup_feed_byte 只认 0x7F, Loop bootloader
回的 0x9F 响应帧(pre_ok/addr_ok/data ACK)全被吞; OTA 是停等协议
(0xA7 WITH_BACK 每块必回 ACK), 工具等不到 ACK 升级必然卡死。
- loop_uart_proto: 新增 lup_feed_byte_ota() 0x9F 帧状态机 (SUM 校验无 XOR,
复用 g_lup_parser), 枚举追加 OTA 专用状态
- usart_biz: uart2_dma_poll 按 g_flag_counter_ota.flag 切换 0x9F/0x7F 解析器,
模式切换 reset, OTA 溢出阈值放宽整缓冲, 补收帧 tick 归零;
uart_srv OTA 分支 0x9F 帧透传回 BLE + 清 flag
- 单测 tests/test_lup_ota_parser.c 8 断言全过 (bootloader 真实帧向量)
- 遗留: g_flag_counter_ota.flag 无退出机制, 升级后需断电重启
This commit is contained in:
@@ -212,7 +212,11 @@ typedef enum {
|
|||||||
LUP_FRAME_STATE_HEADER, // 接收 Header (Addr, LEN, CMD)
|
LUP_FRAME_STATE_HEADER, // 接收 Header (Addr, LEN, CMD)
|
||||||
LUP_FRAME_STATE_VALUE, // 接收 Value
|
LUP_FRAME_STATE_VALUE, // 接收 Value
|
||||||
LUP_FRAME_STATE_CHECK, // 接收 Checksum
|
LUP_FRAME_STATE_CHECK, // 接收 Checksum
|
||||||
LUP_FRAME_STATE_COMPLETE // 帧完成
|
LUP_FRAME_STATE_COMPLETE, // 帧完成
|
||||||
|
/* 0x9F OTA 专用状态 (2026-08-19): 帧格式 9F SubL SubH LEN CMD DATA CHECK */
|
||||||
|
LUP_FRAME_STATE_OTA_LEN, // 0x9F: 收 LEN (含 CMD)
|
||||||
|
LUP_FRAME_STATE_OTA_CMD, // 0x9F: 收 CMD
|
||||||
|
LUP_FRAME_STATE_OTA_CHECK // 0x9F: 收 CHECK (仅 SUM, 无 XOR)
|
||||||
} LUP_FrameState;
|
} LUP_FrameState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -228,6 +232,11 @@ extern LUP_FrameParser g_lup_parser;
|
|||||||
/* 喂一个字节给帧解析器,返回 1 表示帧接收完成 */
|
/* 喂一个字节给帧解析器,返回 1 表示帧接收完成 */
|
||||||
int lup_feed_byte(uint8_t byte);
|
int lup_feed_byte(uint8_t byte);
|
||||||
|
|
||||||
|
/* 0x9F OTA 帧解析器 (OTA 模式专用, g_flag_counter_ota.flag=1 时使用)
|
||||||
|
* 帧格式: 9F SubL SubH LEN CMD DATA(LEN-1) CHECK(SUM)
|
||||||
|
* 与 0x7F 共用 g_lup_parser 缓冲/索引, 状态值独立 (LUP_FRAME_STATE_OTA_*) */
|
||||||
|
int lup_feed_byte_ota(uint8_t byte);
|
||||||
|
|
||||||
/* 取完整帧的指针和数据长度 */
|
/* 取完整帧的指针和数据长度 */
|
||||||
const uint8_t *lup_frame_data(void);
|
const uint8_t *lup_frame_data(void);
|
||||||
uint16_t lup_frame_len(void);
|
uint16_t lup_frame_len(void);
|
||||||
|
|||||||
@@ -655,6 +655,110 @@ int lup_feed_byte(uint8_t byte)
|
|||||||
// 不应该到达这里,调用方应在 COMPLETE 后 reset
|
// 不应该到达这里,调用方应在 COMPLETE 后 reset
|
||||||
lup_frame_reset();
|
lup_frame_reset();
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
/* 未知状态 (含 0x9F OTA 专用状态值): 防御性复位, 防状态机跑飞 */
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 0x9F OTA 帧解析器 (OTA 模式专用, 2026-08-19)
|
||||||
|
*
|
||||||
|
* 帧格式 (DLD960LoopBootloader 源码确认):
|
||||||
|
* [9F] [SubL] [SubH] [LEN] [CMD] [DATA: LEN-1] [CHECK: SUM]
|
||||||
|
* - SubL/SubH: 分包序号, 2 字节小端 (低字节在前)
|
||||||
|
* - LEN: 含 CMD 的字节数, 故 DATA = LEN - 1
|
||||||
|
* - CHECK: 仅 SUM 校验 (无 XOR), 从 SubL 累加到 DATA 最后字节
|
||||||
|
* 总帧长 = LEN + 5 (与 0x7F 相同)
|
||||||
|
*
|
||||||
|
* 状态机复用 g_lup_parser (缓冲/索引), 状态值独立 (LUP_FRAME_STATE_OTA_*)
|
||||||
|
* 调用方 (uart2_dma_poll) 在 OTA 模式切换时负责 lup_frame_reset()
|
||||||
|
*===========================================================================*/
|
||||||
|
int lup_feed_byte_ota(uint8_t byte)
|
||||||
|
{
|
||||||
|
LUP_FrameParser *p = &g_lup_parser;
|
||||||
|
|
||||||
|
switch (p->state) {
|
||||||
|
case LUP_FRAME_STATE_IDLE:
|
||||||
|
if (byte == LUP_MAGIC_OTA) {
|
||||||
|
p->buf[0] = byte;
|
||||||
|
p->idx = 1;
|
||||||
|
p->state = LUP_FRAME_STATE_HEADER; /* 复用 HEADER: 收 SubL/SubH */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_HEADER:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->idx == 3) { /* SubL + SubH 收完 */
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_LEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_LEN:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
{
|
||||||
|
uint8_t len_field = p->buf[3];
|
||||||
|
uint16_t data_bytes;
|
||||||
|
if (len_field < 1) {
|
||||||
|
data_bytes = 0; /* LEN<1 非法, 按无数据直接到 CHECK */
|
||||||
|
} else {
|
||||||
|
data_bytes = len_field - 1; /* LEN 含 CMD */
|
||||||
|
}
|
||||||
|
if (data_bytes > LUP_MAX_VALUE_LEN) {
|
||||||
|
lup_frame_reset(); /* 非法长度, 丢弃 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p->value_len = data_bytes;
|
||||||
|
p->value_idx = 0;
|
||||||
|
}
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CMD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_CMD:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->value_len > 0) {
|
||||||
|
p->state = LUP_FRAME_STATE_VALUE; /* 复用 VALUE: 收 DATA */
|
||||||
|
} else {
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CHECK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_VALUE:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
p->value_idx++;
|
||||||
|
if (p->value_idx >= p->value_len) {
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CHECK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_CHECK:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
{
|
||||||
|
/* SUM = SubL+SubH+LEN+CMD+DATA (不含 magic 0x9F, 不含本 CHECK) */
|
||||||
|
uint8_t sum = 0;
|
||||||
|
uint16_t i;
|
||||||
|
for (i = 1; i < (uint16_t)(p->idx - 1); i++) {
|
||||||
|
sum += p->buf[i];
|
||||||
|
}
|
||||||
|
if (sum == p->buf[p->idx - 1]) {
|
||||||
|
p->state = LUP_FRAME_STATE_COMPLETE;
|
||||||
|
return 1; /* 帧完成 */
|
||||||
|
}
|
||||||
|
lup_frame_reset(); /* 校验失败, 丢弃 */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_COMPLETE:
|
||||||
|
// 不应该到达这里,调用方应在 COMPLETE 后 reset
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
|||||||
static uint8_t uart2_dma_buf[UART2_DMA_BUF_LEN] __attribute__((aligned(4)));
|
static uint8_t uart2_dma_buf[UART2_DMA_BUF_LEN] __attribute__((aligned(4)));
|
||||||
static uint16_t uart2_dma_last = 0; /* 主循环消费位置 (DMA 写指针由硬件维护) */
|
static uint16_t uart2_dma_last = 0; /* 主循环消费位置 (DMA 写指针由硬件维护) */
|
||||||
static uint32_t uart2_dma_drop = 0; /* 溢出丢弃计数 (主循环消费不及时) */
|
static uint32_t uart2_dma_drop = 0; /* 溢出丢弃计数 (主循环消费不及时) */
|
||||||
|
static uint8_t uart2_dma_ota_mode = 0xFF; /* 上次 OTA 模式 (0xFF=未初始化, 首轮强制 reset) */
|
||||||
|
|
||||||
void uart2_dma_init(void)
|
void uart2_dma_init(void)
|
||||||
{
|
{
|
||||||
@@ -67,19 +68,33 @@ void uart2_dma_poll(void)
|
|||||||
|
|
||||||
if (n == 0) return;
|
if (n == 0) return;
|
||||||
|
|
||||||
/* 溢出保护: 未消费 > 半缓冲 (256B) → 消费太慢被 DMA 覆盖, 重置解析器丢帧计数 */
|
/* 溢出保护: 未消费 > 半缓冲 (256B) → 消费太慢被 DMA 覆盖, 重置解析器丢帧计数
|
||||||
if (n > (UART2_DMA_BUF_LEN / 2)) {
|
2026-08-19: OTA 模式下 0x9F ACK 帧仅 7B, 停等协议同一时刻 Loop 至多回 1 帧,
|
||||||
uart2_dma_drop++;
|
放宽阈值到整缓冲 — 256B 阈值在 TX 阻塞窗口可能误杀 ACK */
|
||||||
|
{
|
||||||
|
uint16_t drop_thr = g_flag_counter_ota.flag ? UART2_DMA_BUF_LEN : (UART2_DMA_BUF_LEN / 2);
|
||||||
|
if (n > drop_thr) {
|
||||||
|
uart2_dma_drop++;
|
||||||
|
lup_frame_reset();
|
||||||
|
uart2_dma_last = cur;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTA 模式切换 (0x7F ↔ 0x9F) 时重置帧解析器, 防旧模式残留状态污染 */
|
||||||
|
if (g_flag_counter_ota.flag != uart2_dma_ota_mode) {
|
||||||
lup_frame_reset();
|
lup_frame_reset();
|
||||||
uart2_dma_last = cur;
|
uart2_dma_ota_mode = g_flag_counter_ota.flag;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (n--) {
|
while (n--) {
|
||||||
uint8_t b = uart2_dma_buf[uart2_dma_last];
|
uint8_t b = uart2_dma_buf[uart2_dma_last];
|
||||||
uart2_dma_last = (uint16_t)((uart2_dma_last + 1) & (UART2_DMA_BUF_LEN - 1));
|
uart2_dma_last = (uint16_t)((uart2_dma_last + 1) & (UART2_DMA_BUF_LEN - 1));
|
||||||
|
|
||||||
if (lup_feed_byte(b)) {
|
/* OTA 模式走 0x9F 帧解析 (bootloader 的 pre_ok/addr_ok/data ACK),
|
||||||
|
非 OTA 保持 0x7F 协议解析 */
|
||||||
|
int done = g_flag_counter_ota.flag ? lup_feed_byte_ota(b) : lup_feed_byte(b);
|
||||||
|
if (done) {
|
||||||
/* 帧接收完成 → 复制到 g_pkg_uart_2 (与中断版同逻辑, 主循环无竞争) */
|
/* 帧接收完成 → 复制到 g_pkg_uart_2 (与中断版同逻辑, 主循环无竞争) */
|
||||||
const uint8_t *frame = lup_frame_data();
|
const uint8_t *frame = lup_frame_data();
|
||||||
uint16_t frame_len = lup_frame_len();
|
uint16_t frame_len = lup_frame_len();
|
||||||
@@ -90,6 +105,9 @@ void uart2_dma_poll(void)
|
|||||||
g_pkg_uart_2.tick = 0;
|
g_pkg_uart_2.tick = 0;
|
||||||
}
|
}
|
||||||
lup_frame_reset();
|
lup_frame_reset();
|
||||||
|
} else if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) {
|
||||||
|
/* 收帧中 → tick 归零 (对齐中断版行为, 防 TIM3 半帧兜底误判) */
|
||||||
|
g_pkg_uart_2.tick = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,7 +291,14 @@ void uart_srv(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// OTA 模式 — 忽略 Loop MCU 数据
|
/* OTA 模式 (2026-08-19): Loop bootloader 回的 0x9F 帧
|
||||||
|
(back_9F_pre_ok / addr_ok / data ACK) 透传回 BLE 工具,
|
||||||
|
否则停等协议 (0xA7 WITH_BACK) 永远等不到 ACK */
|
||||||
|
if(g_flag_bt_state){
|
||||||
|
g_flag_notify_temp = set_response_tran_to_notify(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &g_notify_buftemp);
|
||||||
|
}
|
||||||
|
/* OTA 模式同样清 flag — 残留帧会在 OTA 结束后被误处理 */
|
||||||
|
InitPkgUart(&g_pkg_uart_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,47 @@
|
|||||||
>
|
>
|
||||||
> 项目定位: DLD960 通信板 — BLE 配网、TCP JSON 协议服务、Loop MCU 串口桥接
|
> 项目定位: DLD960 通信板 — BLE 配网、TCP JSON 协议服务、Loop MCU 串口桥接
|
||||||
|
|
||||||
|
## 2026-08-19 — UART2 RX DMA 下 BLE→Loop OTA 失效修复:0x9F 帧透传回 BLE
|
||||||
|
|
||||||
|
### 背景
|
||||||
|
|
||||||
|
DMA 改造(fed4335)后通过 BLE 给地感(Loop MCU)OTA 升级失效。代码排查 + DLD960LoopBootloader 源码确认根因。
|
||||||
|
|
||||||
|
### 根因
|
||||||
|
|
||||||
|
1. **`lup_feed_byte()` IDLE 态只认 `0x7F`**,Loop bootloader 回的 0x9F 响应帧(pre_ok/addr_ok/data ACK)第一个字节就被丢弃
|
||||||
|
2. **OTA 是停等协议**:`0xA7` (TRAN_PKG_WITH_BACK) 每收一块必须回 `back_9F_data()` ACK,工具等不到 ACK 不发下一块 → DBN 吞掉 ACK = 升级必然卡死
|
||||||
|
3. **`uart_srv` OTA 分支"忽略 Loop MCU 数据"**——即使解析成功也不透传回 BLE
|
||||||
|
4. DMA 版相对中断版丢失"收帧中 tick 归零";溢出保护 256B 阈值在 TX 阻塞窗口可能误杀 ACK
|
||||||
|
|
||||||
|
### 0x9F 帧格式(DLD960LoopBootloader 源码确认)
|
||||||
|
|
||||||
|
| 方向 | 帧格式 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| 下行 | `9F SubL SubH LEN CMD DATA(LEN-1) CHECK` | CHECK = SUM(SubL..DATA),**无 XOR**;LEN 含 CMD |
|
||||||
|
| 上行 | `9F SubL SubH 02 CMD Status CHECK` | 例:pre_ok = `9F 01 00 02 A5 00 A8` |
|
||||||
|
| CMD | `A5` READY / `A6` ADDR_START / `A7` TRAN_WITH_BACK(回ACK) / `A8` TRAN_WITHOUT_BACK(不回) | 总帧长 = LEN+5 与 0x7F 相同 |
|
||||||
|
|
||||||
|
### 变更
|
||||||
|
|
||||||
|
| 文件 | 变更 |
|
||||||
|
|------|------|
|
||||||
|
| `loop_uart_proto.h` | `LUP_FrameState` 追加 OTA 专用状态(OTA_LEN/OTA_CMD/OTA_CHECK);声明 `lup_feed_byte_ota()` |
|
||||||
|
| `loop_uart_proto.c` | 实现 `lup_feed_byte_ota()`:0x9F 帧状态机(SUM 校验,复用 `g_lup_parser` 缓冲);`lup_feed_byte` 加 default 防御性 reset |
|
||||||
|
| `usart_biz.c` | `uart2_dma_poll`:OTA 模式切换时 reset 解析器 + 按 `g_flag_counter_ota.flag` 选 0x9F/0x7F 解析器 + OTA 模式溢出阈值放宽到整缓冲(0x9F ACK 仅 7B,停等协议同刻至多 1 帧)+ 收帧中 tick 归零(对齐中断版);`uart_srv` OTA 分支:0x9F 帧 `set_response_tran_to_notify` 透传回 BLE + `InitPkgUart` 清 flag |
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
|
||||||
|
- gcc 隔离单测 `tests/test_lup_ota_parser.c`:**8 断言全过**(bootloader 真实帧向量 pre_ok/addr_ok/addr_err/data ACK、错误校验拒绝并复位、0x7F 帧隔离、两帧粘包独立解析)
|
||||||
|
- `gcc -fsyntax-only`:`loop_uart_proto.c`/`usart_biz.c` 无新错误(仅 RISC-V interrupt attribute 假阳性 + 原有 unused warning)
|
||||||
|
|
||||||
|
### 遗留
|
||||||
|
|
||||||
|
- **`g_flag_counter_ota.flag` 无退出机制**:升级完成后 DBN 仍卡 OTA 透传态,只能断电重启恢复 0x7F 通信——待定加"OTA 超时/完成帧自动退出"
|
||||||
|
- 待板级:MRS 编译 + 真机 BLE OTA 全流程回归
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-08-18 — log_query 改为 hex 原始字节上报(协议 V1.03/V1.07 修订)
|
## 2026-08-18 — log_query 改为 hex 原始字节上报(协议 V1.03/V1.07 修订)
|
||||||
|
|
||||||
### 背景
|
### 背景
|
||||||
@@ -1370,6 +1411,7 @@ TCP 超时要等 ~2 分钟才触发 `SINT_STAT_TIM_OUT`,
|
|||||||
|
|
||||||
| 版本 | 时间 | 说明 |
|
| 版本 | 时间 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
|
| V4.4 | 2026-08-19 | UART2 DMA 下 BLE→Loop OTA 修复: lup_feed_byte_ota 0x9F 帧解析+透传回 BLE (停等 ACK), DMA poll OTA 模式切换/溢出阈值/tick 归零, 单测 8 例 |
|
||||||
| V4.3 | 2026-08-18 | log_query 改 hex 原始字节上报 (协议 V1.03/V1.07 修订): offlog_evt_to_hex/snap_rec_to_hex, 2 条快照 406B<800B, 快照 count 恢复 2 |
|
| V4.3 | 2026-08-18 | log_query 改 hex 原始字节上报 (协议 V1.03/V1.07 修订): offlog_evt_to_hex/snap_rec_to_hex, 2 条快照 406B<800B, 快照 count 恢复 2 |
|
||||||
| V4.2 | 2026-08-18 | MQTT 命令集补齐: ssc_net_query/iot_net_query/iot_topic_query 只读组包 (协议 V1.02 对齐), 工具禁用未实现按钮 |
|
| V4.2 | 2026-08-18 | MQTT 命令集补齐: ssc_net_query/iot_net_query/iot_topic_query 只读组包 (协议 V1.02 对齐), 工具禁用未实现按钮 |
|
||||||
| V4.1 | 2026-08-18 | TCP/MQTT 脱机日志快照流: log_stat/log_query/log_clear 支持 stream=snapshot (协议 V1.03/V1.07), snap_rec_to_json 序列化, 事件流 count=0 按上限 |
|
| V4.1 | 2026-08-18 | TCP/MQTT 脱机日志快照流: log_stat/log_query/log_clear 支持 stream=snapshot (协议 V1.03/V1.07), snap_rec_to_json 序列化, 事件流 count=0 按上限 |
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* test_lup_ota_parser.c — 验证 lup_feed_byte_ota 0x9F 帧解析器
|
||||||
|
*
|
||||||
|
* 测试向量来自 DLD960LoopBootloader 源码 (back_9F_pre_ok/addr_ok/data):
|
||||||
|
* pre_ok : 9F 01 00 02 A5 00 A8 (CHECK = 01+00+02+A5+00 = A8)
|
||||||
|
* addr_ok: 9F 01 00 02 A6 00 A9
|
||||||
|
* addr_err:9F 01 00 02 A6 01 AA
|
||||||
|
* data : 9F <sub_l> <sub_h> 02 A7 <status> <sum>
|
||||||
|
*
|
||||||
|
* 编译: gcc -I../BLE/OnlyUpdateApp_Peripheral/APP/include -o test_lup_ota_parser test_lup_ota_parser.c
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ---- mock: 从 loop_uart_proto.h 复制的最小依赖 ---- */
|
||||||
|
#define LUP_MAGIC 0x7F
|
||||||
|
#define LUP_MAGIC_OTA 0x9F
|
||||||
|
#define LUP_MAX_VALUE_LEN 64
|
||||||
|
#define LUP_MAX_PKG_LEN (1 + 3 + LUP_MAX_VALUE_LEN + 2)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LUP_FRAME_STATE_IDLE = 0,
|
||||||
|
LUP_FRAME_STATE_HEADER,
|
||||||
|
LUP_FRAME_STATE_VALUE,
|
||||||
|
LUP_FRAME_STATE_CHECK,
|
||||||
|
LUP_FRAME_STATE_COMPLETE,
|
||||||
|
LUP_FRAME_STATE_OTA_LEN,
|
||||||
|
LUP_FRAME_STATE_OTA_CMD,
|
||||||
|
LUP_FRAME_STATE_OTA_CHECK
|
||||||
|
} LUP_FrameState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LUP_FrameState state;
|
||||||
|
uint8_t buf[LUP_MAX_PKG_LEN];
|
||||||
|
uint16_t idx;
|
||||||
|
uint16_t value_len;
|
||||||
|
uint16_t value_idx;
|
||||||
|
} LUP_FrameParser;
|
||||||
|
|
||||||
|
static LUP_FrameParser g_lup_parser;
|
||||||
|
|
||||||
|
static void lup_frame_reset(void)
|
||||||
|
{
|
||||||
|
g_lup_parser.state = LUP_FRAME_STATE_IDLE;
|
||||||
|
g_lup_parser.idx = 0;
|
||||||
|
g_lup_parser.value_len = 0;
|
||||||
|
g_lup_parser.value_idx = 0;
|
||||||
|
memset(g_lup_parser.buf, 0, sizeof(g_lup_parser.buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t *lup_frame_data(void) { return g_lup_parser.buf; }
|
||||||
|
static uint16_t lup_frame_len(void) { return g_lup_parser.idx; }
|
||||||
|
|
||||||
|
/* ---- 嵌入被测函数体 (从 loop_uart_proto.c 提取) ---- */
|
||||||
|
int lup_feed_byte_ota(uint8_t byte)
|
||||||
|
{
|
||||||
|
LUP_FrameParser *p = &g_lup_parser;
|
||||||
|
|
||||||
|
switch (p->state) {
|
||||||
|
case LUP_FRAME_STATE_IDLE:
|
||||||
|
if (byte == LUP_MAGIC_OTA) {
|
||||||
|
p->buf[0] = byte;
|
||||||
|
p->idx = 1;
|
||||||
|
p->state = LUP_FRAME_STATE_HEADER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_HEADER:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->idx == 3) {
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_LEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_LEN:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
{
|
||||||
|
uint8_t len_field = p->buf[3];
|
||||||
|
uint16_t data_bytes;
|
||||||
|
if (len_field < 1) {
|
||||||
|
data_bytes = 0;
|
||||||
|
} else {
|
||||||
|
data_bytes = len_field - 1;
|
||||||
|
}
|
||||||
|
if (data_bytes > LUP_MAX_VALUE_LEN) {
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p->value_len = data_bytes;
|
||||||
|
p->value_idx = 0;
|
||||||
|
}
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CMD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_CMD:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
if (p->value_len > 0) {
|
||||||
|
p->state = LUP_FRAME_STATE_VALUE;
|
||||||
|
} else {
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CHECK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_VALUE:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
p->value_idx++;
|
||||||
|
if (p->value_idx >= p->value_len) {
|
||||||
|
p->state = LUP_FRAME_STATE_OTA_CHECK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_OTA_CHECK:
|
||||||
|
p->buf[p->idx++] = byte;
|
||||||
|
{
|
||||||
|
uint8_t sum = 0;
|
||||||
|
uint16_t i;
|
||||||
|
for (i = 1; i < (uint16_t)(p->idx - 1); i++) {
|
||||||
|
sum += p->buf[i];
|
||||||
|
}
|
||||||
|
if (sum == p->buf[p->idx - 1]) {
|
||||||
|
p->state = LUP_FRAME_STATE_COMPLETE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
lup_frame_reset();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUP_FRAME_STATE_COMPLETE:
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
lup_frame_reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 测试辅助 ---- */
|
||||||
|
static int fails = 0;
|
||||||
|
|
||||||
|
/* 喂完整帧, 断言: 帧完成 且 len 正确 */
|
||||||
|
static void feed_frame(const uint8_t *frame, int len, const char *name)
|
||||||
|
{
|
||||||
|
int i, done = 0;
|
||||||
|
lup_frame_reset();
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (lup_feed_byte_ota(frame[i])) {
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done) {
|
||||||
|
printf("FAIL %s: frame not completed\n", name);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((int)lup_frame_len() != len) {
|
||||||
|
printf("FAIL %s: len=%d expected %d\n", name, (int)lup_frame_len(), len);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (memcmp(lup_frame_data(), frame, len) != 0) {
|
||||||
|
printf("FAIL %s: content mismatch\n", name);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("PASS %s\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 喂错误校验帧, 断言: 解析失败 (reset 回 IDLE) */
|
||||||
|
static void feed_bad(const uint8_t *frame, int len, const char *name)
|
||||||
|
{
|
||||||
|
int i, done = 0;
|
||||||
|
lup_frame_reset();
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (lup_feed_byte_ota(frame[i])) {
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
printf("FAIL %s: bad checksum accepted\n", name);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) {
|
||||||
|
printf("FAIL %s: parser not reset to IDLE (state=%d)\n", name, g_lup_parser.state);
|
||||||
|
fails++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("PASS %s\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
uint8_t f1[] = {0x9F, 0x01, 0x00, 0x02, 0xA5, 0x00, 0xA8}; /* pre_ok */
|
||||||
|
uint8_t f2[] = {0x9F, 0x01, 0x00, 0x02, 0xA6, 0x00, 0xA9}; /* addr_ok */
|
||||||
|
uint8_t f3[] = {0x9F, 0x01, 0x00, 0x02, 0xA6, 0x01, 0xAA}; /* addr_err */
|
||||||
|
uint8_t f4[] = {0x9F, 0x05, 0x00, 0x02, 0xA7, 0x00, 0xAE}; /* data ACK sub=5 ok */
|
||||||
|
uint8_t f5[] = {0x9F, 0x34, 0x12, 0x02, 0xA7, 0x01, 0xF0}; /* data ACK sub=0x1234 err */
|
||||||
|
uint8_t bad[] = {0x9F, 0x01, 0x00, 0x02, 0xA5, 0x00, 0xA9}; /* CHECK 错 */
|
||||||
|
uint8_t v7f[] = {0x7F, 0x00, 0x04, 0x4A, 0x00, 0x4E, 0x4E}; /* 0x7F 帧不应被解析 */
|
||||||
|
|
||||||
|
feed_frame(f1, sizeof(f1), "pre_ok (9F 01 00 02 A5 00 A8)");
|
||||||
|
feed_frame(f2, sizeof(f2), "addr_ok (9F 01 00 02 A6 00 A9)");
|
||||||
|
feed_frame(f3, sizeof(f3), "addr_err(9F 01 00 02 A6 01 AA)");
|
||||||
|
feed_frame(f4, sizeof(f4), "data ack sub=5 (9F 05 00 02 A7 00 AE)");
|
||||||
|
feed_frame(f5, sizeof(f5), "data ack sub=0x1234 err (9F 34 12 02 A7 01 F0)");
|
||||||
|
|
||||||
|
feed_bad(bad, sizeof(bad), "bad checksum rejected");
|
||||||
|
|
||||||
|
/* 0x7F 帧: IDLE 不认 → 应无帧完成, 状态回 IDLE */
|
||||||
|
{
|
||||||
|
int i, done = 0;
|
||||||
|
lup_frame_reset();
|
||||||
|
for (i = 0; i < (int)sizeof(v7f); i++) {
|
||||||
|
if (lup_feed_byte_ota(v7f[i])) { done = 1; break; }
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
printf("FAIL 0x7F frame parsed by ota parser\n");
|
||||||
|
fails++;
|
||||||
|
} else if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) {
|
||||||
|
printf("FAIL 0x7F frame left parser non-IDLE (state=%d)\n", g_lup_parser.state);
|
||||||
|
fails++;
|
||||||
|
} else {
|
||||||
|
printf("PASS 0x7F frame ignored by ota parser\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 粘帧: 两帧连续 → 各自解析成功 */
|
||||||
|
{
|
||||||
|
uint8_t glue[14];
|
||||||
|
int i, done_cnt = 0;
|
||||||
|
memcpy(glue, f1, 7);
|
||||||
|
memcpy(glue + 7, f2, 7);
|
||||||
|
lup_frame_reset();
|
||||||
|
for (i = 0; i < 14; i++) {
|
||||||
|
if (lup_feed_byte_ota(glue[i])) {
|
||||||
|
done_cnt++;
|
||||||
|
if (done_cnt == 1 && memcmp(lup_frame_data(), f1, 7) != 0) {
|
||||||
|
printf("FAIL glue frame1 mismatch\n"); fails++;
|
||||||
|
}
|
||||||
|
if (done_cnt == 2 && memcmp(lup_frame_data(), f2, 7) != 0) {
|
||||||
|
printf("FAIL glue frame2 mismatch\n"); fails++;
|
||||||
|
}
|
||||||
|
lup_frame_reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done_cnt != 2) {
|
||||||
|
printf("FAIL glue: %d frames completed, expected 2\n", done_cnt);
|
||||||
|
fails++;
|
||||||
|
} else {
|
||||||
|
printf("PASS glued frames parsed independently\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n%s (%d failures)\n", fails ? "FAILED" : "ALL PASS", fails);
|
||||||
|
return fails ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user