feat(vd960DBN): 事件日志区动态分区 — JEDEC ID 选档 (ROADMAP 落地)

- offlog.h: OfflogPart 运行时分区表 + g_offlog_part; AREA_BASE 改 0x090000
  (参数64KB+OTA512KB); 容量宏展开为运行时值(主体零改动)
- offlog.c: offlog_part_detect() 读 JEDEC ID → 事件区容量
  W25Q32=512KB/16256条, Q64=1MB/32640, Q128=2MB/65408, Q256=4MB/130944
- storage.c/h: SPI_Flash_ReadJEDEC_ID() (0x9F, 校验 EF 40)
- 旧固件事件区 0x010000 自动废弃(新地址无 magic → 重建), 预期行为
- 测试: mock JEDEC=0x16 + g_offlog_part, 15 例全过
- 文档: BLE/MQTT/TCP 协议 capacity 8064→动态, 交互示例, devlog
This commit is contained in:
wangfq
2026-08-12 16:46:19 +08:00
parent 6c6ddf8b0f
commit a8120cc852
11 changed files with 158 additions and 31 deletions
@@ -4,18 +4,19 @@
* @author wfq
* @version V1.0
* @date 2026-08-04
* @brief DBN 脱机事件日志 (W25Q32 外部 SPI NOR, 环形)
* @brief DBN 脱机事件日志 (W25Qxx 外部 SPI NOR, 环形)
*
* 目的: 解决"平台重复上线/异常重启但现场无日志"的取证问题。
* 区分「设备真复位」vs「MQTT 断连重连」(两者都会导致平台重收 initialize):
* - 真复位: 会有 OFFLOG_EVT_BOOT 事件, boot_seq 递增
* - 断连重连: boot_seq 不变, 只有 IOT_* 网络事件
*
* 分区 (W25Q32, 4MB):
* | 参数区 0x000000 64KB (现有配置, 不动)
* | 事件区 0x010000 256KB = 扇区 16..79 (头扇区 + 63 数据扇区)
* | OTA 区 0x050000 512KB (预留)
* | 快照区 0x0D0000 ~3.25MB (预留, 后续)
* 分区 (2026-08-12 ROADMAP 新规划):
* | 参数区 0x000000 64KB (固定)
* | OTA 区 0x010000 512KB (固定)
* | 事件区 0x090000 动态: W25Q32=512KB / Q64=1MB / Q128=2MB / Q256=4MB
* | 快照区 事件区后 剩余容量 (预留, 后续)
* 上电读 JEDEC ID (0x9F) 决定事件区容量 (g_offlog_part 运行时分区表)
*
* 环形实现:
* - 扇区 0 (区内) = 头扇区, 存元数据 (boot_seq/写位置/条数), 扇区切换时更新
@@ -37,15 +38,35 @@
/*===========================================================================
* 分区与容量
*===========================================================================*/
#define OFFLOG_AREA_BASE 0x010000UL /* 事件日志区起始 (绝对地址) */
#define OFFLOG_AREA_SIZE 0x040000UL /* 256KB */
/* 固定分区 (ROADMAP 2026-08-12): 参数区 64KB + OTA 512KB, 事件区紧随其后 */
#define OFFLOG_PARAM_SIZE 0x10000UL /* 参数区 64KB (固定) */
#define OFFLOG_OTA_SIZE 0x80000UL /* OTA 镜像暂存 512KB (固定) */
#define OFFLOG_AREA_BASE (OFFLOG_PARAM_SIZE + OFFLOG_OTA_SIZE) /* 0x090000 */
#define OFFLOG_HEAD_SECTOR 0 /* 区内扇区号: 头扇区 */
#define OFFLOG_DATA_SECTOR_CNT 63 /* 数据扇区数 (1..63) */
#define OFFLOG_EVT_PER_SECTOR 128 /* 4096 / 32 */
#define OFFLOG_DATA_BASE (OFFLOG_AREA_BASE + 4096)
#define OFFLOG_DATA_SIZE (OFFLOG_DATA_SECTOR_CNT * 4096)
#define OFFLOG_MAX_RECORDS (OFFLOG_DATA_SECTOR_CNT * OFFLOG_EVT_PER_SECTOR) /* 8064 */
/* 芯片 JEDEC ID (0x9F 命令第 3 字节, EF 40 xx) */
#define OFFLOG_CHIP_W25Q32 0x16
#define OFFLOG_CHIP_W25Q64 0x17
#define OFFLOG_CHIP_W25Q128 0x18
#define OFFLOG_CHIP_W25Q256 0x19
/* 运行时分区表 (offlog_init 由 JEDEC ID 填充; 默认 W25Q32) */
typedef struct {
uint8_t chip; /* OFFLOG_CHIP_xxx */
uint32_t area_size; /* 事件日志区大小 */
uint32_t data_sectors; /* 数据扇区数 (不含头扇区) */
uint32_t max_records; /* 最大记录数 = data_sectors * 128 */
} OfflogPart;
extern OfflogPart g_offlog_part;
/* 兼容宏: 展开为运行时值 (offlog.c 主体引用不变) */
#define OFFLOG_AREA_SIZE (g_offlog_part.area_size)
#define OFFLOG_DATA_SECTOR_CNT (g_offlog_part.data_sectors)
#define OFFLOG_DATA_SIZE ((g_offlog_part.data_sectors) * 4096UL)
#define OFFLOG_MAX_RECORDS (g_offlog_part.max_records)
#define OFFLOG_DATA_BASE (OFFLOG_AREA_BASE + 4096UL)
/* 头扇区记录 (32B, 扇区切换时更新) */
typedef struct {
@@ -54,7 +75,7 @@ typedef struct {
uint32_t wr_sector; /* 上次写扇区号 (区内 1..63) */
uint32_t wr_off; /* 上次写偏移 (相对 OFFLOG_AREA_BASE) */
uint32_t wr_seq; /* 下一条记录全局 seq (掉电恢复锚点) */
uint32_t count; /* 有效记录数 (0..8064) */
uint32_t count; /* 有效记录数 (0..max_records) */
uint32_t rsvd[2];
} OfflogHead; /* 28B, 按 32B 对齐使用 */
@@ -36,5 +36,6 @@ void SPI_Flash_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t size
void SPI_Flash_Erase_Sector(uint32_t Dst_Addr);
void storage_init(void);
uint8_t SPI_Flash_ReadJEDEC_ID(void); /* 0x9F JEDEC device id (W25Q32=0x16...) */
#endif