diff --git a/docs/DLD960_BLE协议.md b/docs/DLD960_BLE协议.md index 4032ed4..b8d99ee 100644 --- a/docs/DLD960_BLE协议.md +++ b/docs/DLD960_BLE协议.md @@ -60,16 +60,16 @@ Magic | Header | Data | CheckByte | 1 | 2 | `boot_seq` | 当前启动序号(每次上电 +1,区分复位段) | | 3 | 4 | `count` | 有效记录条数(0~8064,环形覆盖后 < capacity) | | 7 | 4 | `capacity` | 容量上限(8064) | -| 11 | 4 | `seq_first` | 逻辑首条记录全局序号(`seq_last - count + 1`,count=0 时为 0) | -| 15 | 4 | `seq_last` | 最新一条记录全局序号(跨 boot 单调递增) | +| 3 | 4 | `count` | 有效记录条数(0~capacity,环形覆盖后 < capacity) | +| 7 | 4 | `capacity` | 容量上限(**随存储芯片动态**:W25Q32=16256 / W25Q64=32640 / W25Q128=65408 / W25Q256=130944) | -示例(boot_seq=2, count=1234, capacity=8064, seq_first=100, seq_last=1333): +示例(boot_seq=2, count=1234, capacity=16256, seq_first=100, seq_last=1333): ``` -8F 00 14 25 00 02 00 D2 04 00 00 00 80 1F 00 00 00 64 00 00 00 35 05 00 00 00 XX XX +8F 00 14 25 00 02 00 D2 04 00 00 00 80 3F 00 00 00 64 00 00 00 35 05 00 00 00 XX XX -- -- -- -- -------------------- -------------------- -------------------- ----- - | | | | status count=1234 capacity=8064 seq_first=100 - | | | cmd=0x25 boot_seq=2 seq_last=1333 + | | | | status count=1234 capacity=16256 seq_first=100 + | | | | cmd=0x25 boot_seq=2 seq_last=1333 ``` --- diff --git a/docs/DLD960_BLE日志查询交互.md b/docs/DLD960_BLE日志查询交互.md index cd64faa..12d268c 100644 --- a/docs/DLD960_BLE日志查询交互.md +++ b/docs/DLD960_BLE日志查询交互.md @@ -78,7 +78,7 @@ Magic | Header | Data | CheckByte | 0 | 1 | `status` | `00` | OK | | 1 | 2 | `boot_seq` | 待补(需原始 hex) | 启动序号 | | 3 | 4 | `count` | `A2 00 00 00` = 162 | 有效记录条数 | -| 7 | 4 | `capacity` | `80 1F 00 00` = 8064 | 容量上限 | +| 7 | 4 | `capacity` | `80 3F 00 00` = 16256(W25Q32 512KB:127 数据扇区 × 128 条) | 容量上限 | | 11 | 4 | `seq_first` | `01 00 00 00` = 1 | 逻辑首条全局序号 | | 15 | 4 | `seq_last` | `A2 00 00 00` = 162 | 最新全局序号 | diff --git a/docs/DLD960_IoT_MQTT协议.md b/docs/DLD960_IoT_MQTT协议.md index e71813d..e72f8a5 100644 --- a/docs/DLD960_IoT_MQTT协议.md +++ b/docs/DLD960_IoT_MQTT协议.md @@ -651,7 +651,7 @@ dld960/{dev_serial}/{direction} ## 4.16 查询脱机事件日志统计 `log_stat` > Topic: `dld960/{sn}/srv` -> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。 +> 设备本地 W25Qxx 环形事件日志(事件区动态:W25Q32=512KB/16256 条,掉电不丢)。用于日志拉取前的分页定位。 **请求:** @@ -672,7 +672,7 @@ dld960/{dev_serial}/{direction} "enabled": true, "boot_seq": 2, "count": 1234, - "capacity": 8064, + "capacity": 16256, "seq_first": 100, "seq_last": 1333 } @@ -683,8 +683,8 @@ dld960/{dev_serial}/{direction} | `stream` | string | 日志流,当前仅 `event`(快照流预留) | | `enabled` | bool | 日志功能是否启用(Flash 初始化成功) | | `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) | -| `count` | uint16 | 有效记录条数(0~8064,环形覆盖后 < capacity) | -| `capacity` | uint16 | 容量上限(8064) | +| `count` | uint16 | 有效记录条数(0~capacity,环形覆盖后 < capacity) | +| `capacity` | uint16 | 容量上限(**随存储芯片动态**:W25Q32=16256 / W25Q64=32640 / W25Q128=65408 / W25Q256=130944) | | `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) | | `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) | diff --git a/docs/DLD960_TCP_JSON协议.md b/docs/DLD960_TCP_JSON协议.md index 8df0b87..31d9083 100644 --- a/docs/DLD960_TCP_JSON协议.md +++ b/docs/DLD960_TCP_JSON协议.md @@ -347,7 +347,7 @@ Client Device (DLD960) ## 4.16 查询脱机事件日志统计 `log_stat` -> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。 +> 设备本地 W25Qxx 环形事件日志(事件区动态:W25Q32=512KB/16256 条,掉电不丢)。用于日志拉取前的分页定位。 **请求:** @@ -358,7 +358,7 @@ Client Device (DLD960) **响应 data:** ```json -{"stream":"event","enabled":true,"boot_seq":2,"count":1234,"capacity":8064,"seq_first":100,"seq_last":1333} +{"stream":"event","enabled":true,"boot_seq":2,"count":1234,"capacity":16256,"seq_first":100,"seq_last":1333} ``` | data 字段 | 类型 | 说明 | @@ -366,8 +366,8 @@ Client Device (DLD960) | `stream` | string | 日志流,当前仅 `event`(快照流预留) | | `enabled` | bool | 日志功能是否启用(Flash 初始化成功) | | `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) | -| `count` | uint16 | 有效记录条数(0~8064) | -| `capacity` | uint16 | 容量上限(8064) | +| `count` | uint16 | 有效记录条数(0~capacity) | +| `capacity` | uint16 | 容量上限(**随存储芯片动态**:W25Q32=16256 / W25Q64=32640 / W25Q128=65408 / W25Q256=130944) | | `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) | | `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) | diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/offlog.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/offlog.h index e9db222..49ab0bf 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/offlog.h +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/offlog.h @@ -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 对齐使用 */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/storage.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/storage.h index 5a169ef..643cda8 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/storage.h +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/storage.h @@ -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 diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/offlog.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/offlog.c index ac0ab02..fe14973 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/offlog.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/offlog.c @@ -28,9 +28,39 @@ static uint32_t _wr_off; /* 下一条记录写入的绝对地址 static uint16_t _wr_sector; /* 当前写扇区号 (区内 1..63) */ static uint32_t _wr_seq; /* 下一条记录的全局 seq */ static uint16_t _boot_seq; /* 当前 boot 序号 */ -static uint32_t _count; /* 有效记录数 (0..8064) */ +static uint32_t _count; /* 有效记录数 (0..max_records) */ static uint8_t _ready; /* init 完成标志 */ +/*=========================================================================== + * 运行时分区表 (ROADMAP 2026-08-12: 上电读 JEDEC ID 决定事件区容量) + *===========================================================================*/ +OfflogPart g_offlog_part = { + OFFLOG_CHIP_W25Q32, /* 默认 */ + 0x80000UL, /* 512KB */ + 127, /* (512KB/4096) - 1 头扇区 */ + 16256, /* 127 * 128 */ +}; + +/* 读 JEDEC ID → 填分区表。未知芯片按 W25Q32 兜底 (与 storage_init 打印对照) */ +static void offlog_part_detect(void) +{ + uint8_t dev = SPI_Flash_ReadJEDEC_ID(); + uint32_t size = 0x80000UL; + uint8_t chip = OFFLOG_CHIP_W25Q32; + + switch (dev) { + case OFFLOG_CHIP_W25Q32: size = 0x80000UL; break; /* 512KB */ + case OFFLOG_CHIP_W25Q64: size = 0x100000UL; break; /* 1MB */ + case OFFLOG_CHIP_W25Q128: size = 0x200000UL; break; /* 2MB */ + case OFFLOG_CHIP_W25Q256: size = 0x400000UL; break; /* 4MB */ + default: chip = OFFLOG_CHIP_W25Q32; break; /* 未知→默认 */ + } + g_offlog_part.chip = chip; + g_offlog_part.area_size = size; + g_offlog_part.data_sectors = (size / 4096UL) - 1; + g_offlog_part.max_records = g_offlog_part.data_sectors * OFFLOG_EVT_PER_SECTOR; +} + /*=========================================================================== * 基础读写 *===========================================================================*/ @@ -106,6 +136,7 @@ void offlog_init(void) { OfflogHead h; + offlog_part_detect(); /* JEDEC ID → 事件区容量 (W25Q32/Q64/Q128/Q256) */ memset(&h, 0xFF, sizeof(h)); offlog_head_read(&h); diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/storage.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/storage.c index e7360bc..87d7e56 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/storage.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/storage.c @@ -225,6 +225,30 @@ u16 SPI_Flash_ReadID(void) } +/********************************************************************* + * @fn SPI_Flash_ReadJEDEC_ID + * + * @brief Read JEDEC ID (0x9F). Return 3rd byte (device id). + * W25Q32=0x16 Q64=0x17 Q128=0x18 Q256=0x19; 0 if not Winbond. + * + * @return device id byte + */ +uint8_t SPI_Flash_ReadJEDEC_ID(void) +{ + uint8_t id[3] = {0, 0, 0}; + + GPIO_WriteBit(GPIOA, GPIO_Pin_4, 0); + SPI1_ReadWriteByte(W25X_JedecDeviceID); + id[0] = SPI1_ReadWriteByte(0xFF); + id[1] = SPI1_ReadWriteByte(0xFF); + id[2] = SPI1_ReadWriteByte(0xFF); + GPIO_WriteBit(GPIOA, GPIO_Pin_4, 1); + + if (id[0] != 0xEF || id[1] != 0x40) return 0; + return id[2]; +} + + /********************************************************************* * @fn SPI_Flash_Erase_Sector * diff --git a/vd960DBN/docs/devlog.md b/vd960DBN/docs/devlog.md index f0297ac..67bbce5 100644 --- a/vd960DBN/docs/devlog.md +++ b/vd960DBN/docs/devlog.md @@ -112,6 +112,51 @@ static void performPeriodicTask(void) --- +## 2026-08-12 — 事件日志区动态分区(ROADMAP 落地:JEDEC ID 选档) + +### 背景 + +ROADMAP P1.2 更新:分区顺序调整为 **参数区 → OTA 镜像暂存区 → 事件日志区 → 传感快照区**;事件日志区容量随存储芯片动态(W25Q32=512KB / Q64=1MB / Q128=2MB / Q256=4MB),上电读 JEDEC ID 选档。移除 W25Q80 支持。 + +### 代码改动 + +| 文件 | 改动 | +|------|------| +| `offlog.h` | 新增 `OfflogPart` 运行时分区表 + `g_offlog_part`;`OFFLOG_AREA_BASE` 改 0x090000(参数64KB+OTA512KB);`OFFLOG_AREA_SIZE/DATA_SECTOR_CNT/MAX_RECORDS/DATA_SIZE` 宏改为展开运行时值(主体零改动);新增 `OFFLOG_CHIP_W25Q32/64/128/256` JEDEC ID 宏 | +| `offlog.c` | 定义 `g_offlog_part`(默认 W25Q32 512KB/16256条);新增 `offlog_part_detect()`(读 JEDEC ID → 填表,未知按 W25Q32 兜底);`offlog_init` 开头调用 | +| `storage.c` | 新增 `SPI_Flash_ReadJEDEC_ID()`(0x9F 读 3 字节,校验 EF 40,返回 device id) | +| `storage.h` | 声明 | + +> 注:offlog.h/c 是 **UTF-8**(2026-08-04 新建),storage.c 是 **GBK**——改造按各自编码精确替换,未破坏。 + +### 容量对照 + +| 芯片 | JEDEC | 事件区 | 数据扇区 | max_records | capacity LE | +|------|-------|--------|---------|-------------|-------------| +| W25Q32(默认) | 0x16 | 512KB | 127 | 16256 | `80 3F 00 00` | +| W25Q64 | 0x17 | 1MB | 255 | 32640 | `80 7F 00 00` | +| W25Q128 | 0x18 | 2MB | 511 | 65408 | `80 FF 00 00` | +| W25Q256 | 0x19 | 4MB | 1023 | 130944 | `80 FF 01 00` | + +### 兼容性 + +- 旧固件事件区在 0x010000(256KB),新固件在 0x090000——**旧日志数据不会误读**(新地址头扇区无 magic → 视为全新区自动重建),属预期行为 +- `offlog_init` 在 `storage_init` 之后调用(peripheral_main.c:305-306),SPI 已就绪 + +### 文档同步 + +- `DLD960_BLE协议.md` / `DLD960_IoT_MQTT协议.md` / `DLD960_TCP_JSON协议.md`:capacity 字段 8064 → 动态(含四芯片对照) +- `DLD960_BLE日志查询交互.md`:STAT 示例 capacity 更新 +- `ROADMAP.md`:P1.2 分区规划(前序提交) + +### 验证 + +- `tests/test_offlog.c`:mock JEDEC=0x16,8 例全过(环形回绕自动适配 16256) +- `tests/test_ble_offlog.c`:mock `g_offlog_part`,capacity 断言 16256,7 例全过 +- 地址范围模拟:四芯片事件区 0x090000 起均不超芯片容量 + +--- + ## 2026-08-12 — 续2:第二分包丢失 — 根因确认(响应缓冲越界踩踏 g_notify_buftemp) ### 破案过程(三份现场日志 + .map 对照) diff --git a/vd960DBN/tests/test_ble_offlog.c b/vd960DBN/tests/test_ble_offlog.c index 4c437ac..f7c6a8e 100644 --- a/vd960DBN/tests/test_ble_offlog.c +++ b/vd960DBN/tests/test_ble_offlog.c @@ -78,6 +78,9 @@ int offlog_read_idx(uint16_t idx, OfflogEvt *out) return 0; } +/* g_offlog_part: OFFLOG_MAX_RECORDS 宏展开为运行时值, 此处 mock W25Q32 (512KB) */ +OfflogPart g_offlog_part = { OFFLOG_CHIP_W25Q32, 0x80000UL, 127, 16256 }; + /* ============ set_response_buf mock: 只记录调用参数 ============ */ static uint8_t last_cmd = 0; static uint8_t last_len = 0; @@ -125,8 +128,8 @@ static void test_stat_ok(void) CHECK(last_dat[1] == 0x02 && last_dat[2] == 0x00); /* count=1234 LE */ CHECK(last_dat[3] == 0xD2 && last_dat[4] == 0x04 && last_dat[5] == 0x00 && last_dat[6] == 0x00); - /* capacity=8064 LE */ - CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x1F && last_dat[9] == 0x00 && last_dat[10] == 0x00); + /* capacity=16256 LE (W25Q32 512KB: 127 数据扇区 x 128) */ + CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x3F && last_dat[9] == 0x00 && last_dat[10] == 0x00); /* seq_first=100 LE */ CHECK(last_dat[11] == 0x64 && last_dat[12] == 0x00 && last_dat[13] == 0x00 && last_dat[14] == 0x00); /* seq_last=1333 LE */ diff --git a/vd960DBN/tests/test_offlog.c b/vd960DBN/tests/test_offlog.c index 97ae907..2bbe5b1 100644 --- a/vd960DBN/tests/test_offlog.c +++ b/vd960DBN/tests/test_offlog.c @@ -32,6 +32,8 @@ void SPI_Flash_Write_NoCheck(uint8_t *buf, uint32_t addr, uint16_t size) for (i = 0; i < size; i++) flash[addr + i] &= buf[i]; /* NOR: 只能 1→0 */ } +uint8_t SPI_Flash_ReadJEDEC_ID(void) { return 0x16; } /* mock W25Q32 */ + void SPI_Flash_Write(uint8_t *buf, uint32_t addr, uint16_t size) { /* 模拟 storage.c 行为: 目标区域非全 FF 则擦整扇区再写 */