fix(vd960DBN): init/clear 懒擦修复不断重启 — 34s 全量擦除超 IWDG 4s
现场: 烧录快照固件后 CH32V208 不断重启 + 串口乱码
根因: snap_init 首擦 751 扇区 ~34s (头在擦完才写→复位后仍全新→死循环);
snap_clear 34s / offlog_clear 2.8s 同类; 均超 IWDG 4s
修复(懒擦): init/clear 只擦写指针起点扇区 ~45ms, 其余由环形写切扇区
逻辑自动擦; clear 为逻辑清除 (count=0 旧数据不可读)
附带: usart_biz.c 非 0x7F 帧 %s 打印改 hex (Loop 数据当字符串=乱码源)
snapshot.h 线程模型注释修正 (lup_process_frame 实际在主循环 uart_srv)
测试: test_snapshot 9例(新增 lazy_erase) + offlog/ble_offlog 回归全过
This commit is contained in:
@@ -1006,7 +1006,7 @@ void manage_dbn_ble_default(uint8_t *pkg, uint8_t len)
|
||||
(unsigned long)_start_seq, (unsigned long)_req_count, (unsigned)_j);
|
||||
} break;
|
||||
case CMD_DBN_OFFLOG_CLEAR: {
|
||||
/* clear offlog (audit trail). blocking ~2.8s (63 sectors erase), main-loop ctx ok */
|
||||
/* clear offlog (audit trail). blocking ~45ms (logical clear + current sector), main-loop ctx ok */
|
||||
uint8_t _i = 0;
|
||||
offlog_clear();
|
||||
tmp_ble_buf[_i++] = 0x00; /* ok */
|
||||
@@ -1097,7 +1097,7 @@ void manage_dbn_ble_default(uint8_t *pkg, uint8_t len)
|
||||
(unsigned long)_start_seq, (unsigned long)_req_count, (unsigned)_j);
|
||||
} break;
|
||||
case CMD_DBN_SNAP_CLEAR: {
|
||||
/* clear snapshot (audit trail). blocking (sectors erase), main-loop ctx ok */
|
||||
/* clear snapshot (audit trail). blocking ~45ms (logical clear + current sector) */
|
||||
uint8_t _i = 0;
|
||||
snap_clear();
|
||||
tmp_ble_buf[_i++] = 0x00; /* ok */
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
* - 顺序写, 写满擦下一扇区 (环形覆盖), 天然磨损均衡
|
||||
* - 上电从头部记录的写位置向后扫描, 找第一条无效记录 = 真实写位置 (掉电恢复)
|
||||
*
|
||||
* 线程模型 (关键):
|
||||
* - snap_enqueue(): 中断上下文 (USART2 ISR → lup_process_frame → iot_sensor_ingest),
|
||||
* 只做打包 + RAM 环形暂存 (memcpy, 无阻塞, 无 SPI 操作); 满时丢新不覆盖
|
||||
* - snap_flush(): 主循环上下文 (iot_mqtt_publish_sensor), RAM 暂存 → SPI 落盘
|
||||
* (扇区切换擦除 ~45ms 阻塞仅发生在主循环, 与 offlog 同底线)
|
||||
* - 单生产者 (中断) 单消费者 (主循环) 无锁环形: 生产者只写 tail,
|
||||
* 消费者只读 head, 留一格判满
|
||||
* 线程模型 (关键, 2026-08-12 修正):
|
||||
* - USART2 ISR 只逐字节喂 lup_feed_byte(), 完整帧由主循环 uart_srv() 调
|
||||
* lup_process_frame → iot_sensor_ingest → snap_enqueue() — 全程主循环
|
||||
* - snap_enqueue(): 打包 + RAM 环形暂存 (无 SPI, 无阻塞); 满时丢新不覆盖
|
||||
* - snap_flush(): iot_mqtt_publish_sensor 每轮调用, RAM 暂存 → SPI 落盘
|
||||
* (扇区切换擦除 ~45ms 阻塞发生在主循环, 与 offlog 同底线)
|
||||
* - 单生产者单消费者无锁环形: 生产者只写 tail/count, 消费者只读 head
|
||||
*
|
||||
* 记录格式 SnapRec (64B 定长, 无 padding):
|
||||
* magic(1) len(1) flags(1) rsvd(1) | seq(4) ts_ms(4) boot_seq(2) rsvd2(2) | coils(48)
|
||||
@@ -124,7 +124,7 @@ typedef struct {
|
||||
void snap_init(void); /* 上电初始化 (扫描恢复) */
|
||||
uint8_t snap_enabled(void); /* Flash 初始化成功 */
|
||||
|
||||
void snap_enqueue(const LUP_SensorReport *sr); /* 中断安全: 打包→RAM 暂存 (满丢新) */
|
||||
void snap_enqueue(const LUP_SensorReport *sr); /* 主循环入队: 打包→RAM 暂存 (满丢新) */
|
||||
void snap_flush(void); /* 主循环: RAM 暂存→SPI 落盘 */
|
||||
|
||||
uint32_t snap_count(void); /* 有效记录数 */
|
||||
|
||||
@@ -142,11 +142,9 @@ void offlog_init(void)
|
||||
|
||||
if (!(h.magic[0] == OFFLOG_HEAD_MAGIC0 && h.magic[1] == OFFLOG_HEAD_MAGIC1 &&
|
||||
h.magic[2] == OFFLOG_HEAD_MAGIC2 && h.magic[3] == OFFLOG_HEAD_MAGIC3)) {
|
||||
/* 全新区: 擦全部数据扇区, 写头 */
|
||||
uint32_t i;
|
||||
for (i = 0; i < OFFLOG_DATA_SECTOR_CNT; i++) {
|
||||
SPI_Flash_Erase_Sector(OFFLOG_DATA_BASE / 4096 + i);
|
||||
}
|
||||
/* 全新区: 懒擦 — 只擦当前写扇区 ~45ms (原擦全部 127 扇区 ~5.7s,
|
||||
主循环阻塞超 IWDG 4s 风险), 其余由环形写切扇区时自动擦 */
|
||||
SPI_Flash_Erase_Sector(OFFLOG_DATA_BASE / 4096);
|
||||
memset(&h, 0xFF, sizeof(h));
|
||||
h.magic[0] = OFFLOG_HEAD_MAGIC0;
|
||||
h.magic[1] = OFFLOG_HEAD_MAGIC1;
|
||||
@@ -478,12 +476,11 @@ int offlog_evt_to_json(const OfflogEvt *e, char *buf, int buf_len)
|
||||
*===========================================================================*/
|
||||
void offlog_clear(void)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!_ready) return;
|
||||
|
||||
for (i = 0; i < OFFLOG_DATA_SECTOR_CNT; i++) {
|
||||
SPI_Flash_Erase_Sector(OFFLOG_DATA_BASE / 4096 + i);
|
||||
}
|
||||
/* 逻辑清除 + 只擦写指针起点扇区 (~45ms, 原 2.8s 接近 IWDG 4s 风险):
|
||||
count=0 使旧数据不可读, 其余扇区由环形写覆盖时自动擦 */
|
||||
SPI_Flash_Erase_Sector(OFFLOG_DATA_BASE / 4096);
|
||||
_wr_off = OFFLOG_DATA_BASE;
|
||||
_wr_sector = 1;
|
||||
_count = 0;
|
||||
|
||||
@@ -167,11 +167,9 @@ void snap_init(void)
|
||||
|
||||
if (!(h.magic[0] == SNAP_HEAD_MAGIC0 && h.magic[1] == SNAP_HEAD_MAGIC1 &&
|
||||
h.magic[2] == SNAP_HEAD_MAGIC2 && h.magic[3] == SNAP_HEAD_MAGIC3)) {
|
||||
/* 全新区: 擦全部数据扇区, 写头 */
|
||||
uint32_t i;
|
||||
for (i = 0; i < SNAP_DATA_SECTOR_CNT; i++) {
|
||||
SPI_Flash_Erase_Sector(SNAP_DATA_BASE / 4096 + i);
|
||||
}
|
||||
/* 全新区: 懒擦 — 只擦当前写扇区 (数据扇区1) ~45ms, 其余扇区由环形写
|
||||
切扇区时自动擦。原擦全部 751 扇区 ~34s → 主循环阻塞超 IWDG 4s → 复位 */
|
||||
SPI_Flash_Erase_Sector(SNAP_DATA_BASE / 4096);
|
||||
memset(&h, 0xFF, sizeof(h));
|
||||
h.magic[0] = SNAP_HEAD_MAGIC0;
|
||||
h.magic[1] = SNAP_HEAD_MAGIC1;
|
||||
@@ -306,7 +304,9 @@ static void snap_pack(const LUP_SensorReport *sr, SnapRec *rec)
|
||||
}
|
||||
|
||||
/*===========================================================================
|
||||
* 中断安全入队 (USART2 ISR 上下文; 无 SPI, 无阻塞; 满丢新不覆盖)
|
||||
* 入队 (主循环上下文: uart_srv → lup_process_frame → iot_sensor_ingest)
|
||||
* USART2 ISR 只逐字节喂 lup_feed_byte, 完整帧由主循环 uart_srv 处理 →
|
||||
* 本函数不在中断里, 但保留 RAM 暂存 + flush 两段式: 批量落盘省 SPI 写次数
|
||||
* - 生产者只写 g_snap_wr / g_snap_pending / g_snap_count
|
||||
* - 消费者只读 g_snap_rd; count 为 volatile 字节, 单核单指令读写天然原子
|
||||
* - 满判定: count >= DEPTH (容量 = SNAP_RAM_DEPTH)
|
||||
@@ -389,12 +389,11 @@ int snap_read_idx(uint32_t idx, SnapRec *out)
|
||||
*===========================================================================*/
|
||||
void snap_clear(void)
|
||||
{
|
||||
uint32_t i;
|
||||
if (!_ready) return;
|
||||
|
||||
for (i = 0; i < SNAP_DATA_SECTOR_CNT; i++) {
|
||||
SPI_Flash_Erase_Sector(SNAP_DATA_BASE / 4096 + i);
|
||||
}
|
||||
/* 逻辑清除 + 只擦写指针起点扇区 (~45ms): count=0 使旧数据不可读,
|
||||
其余扇区由环形写覆盖时自动擦。原擦全部 751 扇区 ~34s > IWDG 4s → 复位 */
|
||||
SPI_Flash_Erase_Sector(SNAP_DATA_BASE / 4096);
|
||||
_wr_off = SNAP_DATA_BASE;
|
||||
_wr_sector = 1;
|
||||
_count = 0;
|
||||
|
||||
@@ -191,8 +191,12 @@ void uart_srv(void)
|
||||
|
||||
}
|
||||
else {
|
||||
// 非 0x7F 魔法字节
|
||||
PRINT("Rcv_len:%d,dat: %s\n", g_pkg_uart_2.offset, g_pkg_uart_2.pkg);
|
||||
// 非 0x7F 魔法字节 — hex 打印 (原 %s 会把二进制当字符串 → 乱码)
|
||||
PRINT("Rcv_len:%d,dat:", g_pkg_uart_2.offset);
|
||||
for(i = 0; i < g_pkg_uart_2.offset; i++){
|
||||
PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
||||
}
|
||||
PRINT("\n");
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
Reference in New Issue
Block a user