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:
@@ -60,16 +60,16 @@ Magic | Header | Data | CheckByte
|
|||||||
| 1 | 2 | `boot_seq` | 当前启动序号(每次上电 +1,区分复位段) |
|
| 1 | 2 | `boot_seq` | 当前启动序号(每次上电 +1,区分复位段) |
|
||||||
| 3 | 4 | `count` | 有效记录条数(0~8064,环形覆盖后 < capacity) |
|
| 3 | 4 | `count` | 有效记录条数(0~8064,环形覆盖后 < capacity) |
|
||||||
| 7 | 4 | `capacity` | 容量上限(8064) |
|
| 7 | 4 | `capacity` | 容量上限(8064) |
|
||||||
| 11 | 4 | `seq_first` | 逻辑首条记录全局序号(`seq_last - count + 1`,count=0 时为 0) |
|
| 3 | 4 | `count` | 有效记录条数(0~capacity,环形覆盖后 < capacity) |
|
||||||
| 15 | 4 | `seq_last` | 最新一条记录全局序号(跨 boot 单调递增) |
|
| 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
|
| | | | status count=1234 capacity=16256 seq_first=100
|
||||||
| | | cmd=0x25 boot_seq=2 seq_last=1333
|
| | | | cmd=0x25 boot_seq=2 seq_last=1333
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ Magic | Header | Data | CheckByte
|
|||||||
| 0 | 1 | `status` | `00` | OK |
|
| 0 | 1 | `status` | `00` | OK |
|
||||||
| 1 | 2 | `boot_seq` | 待补(需原始 hex) | 启动序号 |
|
| 1 | 2 | `boot_seq` | 待补(需原始 hex) | 启动序号 |
|
||||||
| 3 | 4 | `count` | `A2 00 00 00` = 162 | 有效记录条数 |
|
| 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 | 逻辑首条全局序号 |
|
| 11 | 4 | `seq_first` | `01 00 00 00` = 1 | 逻辑首条全局序号 |
|
||||||
| 15 | 4 | `seq_last` | `A2 00 00 00` = 162 | 最新全局序号 |
|
| 15 | 4 | `seq_last` | `A2 00 00 00` = 162 | 最新全局序号 |
|
||||||
|
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ dld960/{dev_serial}/{direction}
|
|||||||
## 4.16 查询脱机事件日志统计 `log_stat`
|
## 4.16 查询脱机事件日志统计 `log_stat`
|
||||||
|
|
||||||
> Topic: `dld960/{sn}/srv`
|
> Topic: `dld960/{sn}/srv`
|
||||||
> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。
|
> 设备本地 W25Qxx 环形事件日志(事件区动态:W25Q32=512KB/16256 条,掉电不丢)。用于日志拉取前的分页定位。
|
||||||
|
|
||||||
**请求:**
|
**请求:**
|
||||||
|
|
||||||
@@ -672,7 +672,7 @@ dld960/{dev_serial}/{direction}
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"boot_seq": 2,
|
"boot_seq": 2,
|
||||||
"count": 1234,
|
"count": 1234,
|
||||||
"capacity": 8064,
|
"capacity": 16256,
|
||||||
"seq_first": 100,
|
"seq_first": 100,
|
||||||
"seq_last": 1333
|
"seq_last": 1333
|
||||||
}
|
}
|
||||||
@@ -683,8 +683,8 @@ dld960/{dev_serial}/{direction}
|
|||||||
| `stream` | string | 日志流,当前仅 `event`(快照流预留) |
|
| `stream` | string | 日志流,当前仅 `event`(快照流预留) |
|
||||||
| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) |
|
| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) |
|
||||||
| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) |
|
| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) |
|
||||||
| `count` | uint16 | 有效记录条数(0~8064,环形覆盖后 < capacity) |
|
| `count` | uint16 | 有效记录条数(0~capacity,环形覆盖后 < capacity) |
|
||||||
| `capacity` | uint16 | 容量上限(8064) |
|
| `capacity` | uint16 | 容量上限(**随存储芯片动态**:W25Q32=16256 / W25Q64=32640 / W25Q128=65408 / W25Q256=130944) |
|
||||||
| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) |
|
| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) |
|
||||||
| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) |
|
| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) |
|
||||||
|
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ Client Device (DLD960)
|
|||||||
|
|
||||||
## 4.16 查询脱机事件日志统计 `log_stat`
|
## 4.16 查询脱机事件日志统计 `log_stat`
|
||||||
|
|
||||||
> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。
|
> 设备本地 W25Qxx 环形事件日志(事件区动态:W25Q32=512KB/16256 条,掉电不丢)。用于日志拉取前的分页定位。
|
||||||
|
|
||||||
**请求:**
|
**请求:**
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ Client Device (DLD960)
|
|||||||
**响应 data:**
|
**响应 data:**
|
||||||
|
|
||||||
```json
|
```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 字段 | 类型 | 说明 |
|
| data 字段 | 类型 | 说明 |
|
||||||
@@ -366,8 +366,8 @@ Client Device (DLD960)
|
|||||||
| `stream` | string | 日志流,当前仅 `event`(快照流预留) |
|
| `stream` | string | 日志流,当前仅 `event`(快照流预留) |
|
||||||
| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) |
|
| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) |
|
||||||
| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) |
|
| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) |
|
||||||
| `count` | uint16 | 有效记录条数(0~8064) |
|
| `count` | uint16 | 有效记录条数(0~capacity) |
|
||||||
| `capacity` | uint16 | 容量上限(8064) |
|
| `capacity` | uint16 | 容量上限(**随存储芯片动态**:W25Q32=16256 / W25Q64=32640 / W25Q128=65408 / W25Q256=130944) |
|
||||||
| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) |
|
| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) |
|
||||||
| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) |
|
| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) |
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,19 @@
|
|||||||
* @author wfq
|
* @author wfq
|
||||||
* @version V1.0
|
* @version V1.0
|
||||||
* @date 2026-08-04
|
* @date 2026-08-04
|
||||||
* @brief DBN 脱机事件日志 (W25Q32 外部 SPI NOR, 环形)
|
* @brief DBN 脱机事件日志 (W25Qxx 外部 SPI NOR, 环形)
|
||||||
*
|
*
|
||||||
* 目的: 解决"平台重复上线/异常重启但现场无日志"的取证问题。
|
* 目的: 解决"平台重复上线/异常重启但现场无日志"的取证问题。
|
||||||
* 区分「设备真复位」vs「MQTT 断连重连」(两者都会导致平台重收 initialize):
|
* 区分「设备真复位」vs「MQTT 断连重连」(两者都会导致平台重收 initialize):
|
||||||
* - 真复位: 会有 OFFLOG_EVT_BOOT 事件, boot_seq 递增
|
* - 真复位: 会有 OFFLOG_EVT_BOOT 事件, boot_seq 递增
|
||||||
* - 断连重连: boot_seq 不变, 只有 IOT_* 网络事件
|
* - 断连重连: boot_seq 不变, 只有 IOT_* 网络事件
|
||||||
*
|
*
|
||||||
* 分区 (W25Q32, 4MB):
|
* 分区 (2026-08-12 ROADMAP 新规划):
|
||||||
* | 参数区 0x000000 64KB (现有配置, 不动)
|
* | 参数区 0x000000 64KB (固定)
|
||||||
* | 事件区 0x010000 256KB = 扇区 16..79 (头扇区 + 63 数据扇区)
|
* | OTA 区 0x010000 512KB (固定)
|
||||||
* | OTA 区 0x050000 512KB (预留)
|
* | 事件区 0x090000 动态: W25Q32=512KB / Q64=1MB / Q128=2MB / Q256=4MB
|
||||||
* | 快照区 0x0D0000 ~3.25MB (预留, 后续)
|
* | 快照区 事件区后 剩余容量 (预留, 后续)
|
||||||
|
* 上电读 JEDEC ID (0x9F) 决定事件区容量 (g_offlog_part 运行时分区表)
|
||||||
*
|
*
|
||||||
* 环形实现:
|
* 环形实现:
|
||||||
* - 扇区 0 (区内) = 头扇区, 存元数据 (boot_seq/写位置/条数), 扇区切换时更新
|
* - 扇区 0 (区内) = 头扇区, 存元数据 (boot_seq/写位置/条数), 扇区切换时更新
|
||||||
@@ -37,15 +38,35 @@
|
|||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* 分区与容量
|
* 分区与容量
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
#define OFFLOG_AREA_BASE 0x010000UL /* 事件日志区起始 (绝对地址) */
|
/* 固定分区 (ROADMAP 2026-08-12): 参数区 64KB + OTA 512KB, 事件区紧随其后 */
|
||||||
#define OFFLOG_AREA_SIZE 0x040000UL /* 256KB */
|
#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_HEAD_SECTOR 0 /* 区内扇区号: 头扇区 */
|
||||||
#define OFFLOG_DATA_SECTOR_CNT 63 /* 数据扇区数 (1..63) */
|
|
||||||
#define OFFLOG_EVT_PER_SECTOR 128 /* 4096 / 32 */
|
#define OFFLOG_EVT_PER_SECTOR 128 /* 4096 / 32 */
|
||||||
|
|
||||||
#define OFFLOG_DATA_BASE (OFFLOG_AREA_BASE + 4096)
|
/* 芯片 JEDEC ID (0x9F 命令第 3 字节, EF 40 xx) */
|
||||||
#define OFFLOG_DATA_SIZE (OFFLOG_DATA_SECTOR_CNT * 4096)
|
#define OFFLOG_CHIP_W25Q32 0x16
|
||||||
#define OFFLOG_MAX_RECORDS (OFFLOG_DATA_SECTOR_CNT * OFFLOG_EVT_PER_SECTOR) /* 8064 */
|
#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, 扇区切换时更新) */
|
/* 头扇区记录 (32B, 扇区切换时更新) */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -54,7 +75,7 @@ typedef struct {
|
|||||||
uint32_t wr_sector; /* 上次写扇区号 (区内 1..63) */
|
uint32_t wr_sector; /* 上次写扇区号 (区内 1..63) */
|
||||||
uint32_t wr_off; /* 上次写偏移 (相对 OFFLOG_AREA_BASE) */
|
uint32_t wr_off; /* 上次写偏移 (相对 OFFLOG_AREA_BASE) */
|
||||||
uint32_t wr_seq; /* 下一条记录全局 seq (掉电恢复锚点) */
|
uint32_t wr_seq; /* 下一条记录全局 seq (掉电恢复锚点) */
|
||||||
uint32_t count; /* 有效记录数 (0..8064) */
|
uint32_t count; /* 有效记录数 (0..max_records) */
|
||||||
uint32_t rsvd[2];
|
uint32_t rsvd[2];
|
||||||
} OfflogHead; /* 28B, 按 32B 对齐使用 */
|
} 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 SPI_Flash_Erase_Sector(uint32_t Dst_Addr);
|
||||||
|
|
||||||
void storage_init(void);
|
void storage_init(void);
|
||||||
|
uint8_t SPI_Flash_ReadJEDEC_ID(void); /* 0x9F JEDEC device id (W25Q32=0x16...) */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,9 +28,39 @@ static uint32_t _wr_off; /* 下一条记录写入的绝对地址
|
|||||||
static uint16_t _wr_sector; /* 当前写扇区号 (区内 1..63) */
|
static uint16_t _wr_sector; /* 当前写扇区号 (区内 1..63) */
|
||||||
static uint32_t _wr_seq; /* 下一条记录的全局 seq */
|
static uint32_t _wr_seq; /* 下一条记录的全局 seq */
|
||||||
static uint16_t _boot_seq; /* 当前 boot 序号 */
|
static uint16_t _boot_seq; /* 当前 boot 序号 */
|
||||||
static uint32_t _count; /* 有效记录数 (0..8064) */
|
static uint32_t _count; /* 有效记录数 (0..max_records) */
|
||||||
static uint8_t _ready; /* init 完成标志 */
|
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;
|
OfflogHead h;
|
||||||
|
|
||||||
|
offlog_part_detect(); /* JEDEC ID → 事件区容量 (W25Q32/Q64/Q128/Q256) */
|
||||||
memset(&h, 0xFF, sizeof(h));
|
memset(&h, 0xFF, sizeof(h));
|
||||||
offlog_head_read(&h);
|
offlog_head_read(&h);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* @fn SPI_Flash_Erase_Sector
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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)
|
## 2026-08-12 — 续2:第二分包丢失 — 根因确认(响应缓冲越界踩踏 g_notify_buftemp)
|
||||||
|
|
||||||
### 破案过程(三份现场日志 + .map 对照)
|
### 破案过程(三份现场日志 + .map 对照)
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ int offlog_read_idx(uint16_t idx, OfflogEvt *out)
|
|||||||
return 0;
|
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: 只记录调用参数 ============ */
|
/* ============ set_response_buf mock: 只记录调用参数 ============ */
|
||||||
static uint8_t last_cmd = 0;
|
static uint8_t last_cmd = 0;
|
||||||
static uint8_t last_len = 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);
|
CHECK(last_dat[1] == 0x02 && last_dat[2] == 0x00);
|
||||||
/* count=1234 LE */
|
/* count=1234 LE */
|
||||||
CHECK(last_dat[3] == 0xD2 && last_dat[4] == 0x04 && last_dat[5] == 0x00 && last_dat[6] == 0x00);
|
CHECK(last_dat[3] == 0xD2 && last_dat[4] == 0x04 && last_dat[5] == 0x00 && last_dat[6] == 0x00);
|
||||||
/* capacity=8064 LE */
|
/* capacity=16256 LE (W25Q32 512KB: 127 数据扇区 x 128) */
|
||||||
CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x1F && last_dat[9] == 0x00 && last_dat[10] == 0x00);
|
CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x3F && last_dat[9] == 0x00 && last_dat[10] == 0x00);
|
||||||
/* seq_first=100 LE */
|
/* seq_first=100 LE */
|
||||||
CHECK(last_dat[11] == 0x64 && last_dat[12] == 0x00 && last_dat[13] == 0x00 && last_dat[14] == 0x00);
|
CHECK(last_dat[11] == 0x64 && last_dat[12] == 0x00 && last_dat[13] == 0x00 && last_dat[14] == 0x00);
|
||||||
/* seq_last=1333 LE */
|
/* seq_last=1333 LE */
|
||||||
|
|||||||
@@ -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 */
|
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)
|
void SPI_Flash_Write(uint8_t *buf, uint32_t addr, uint16_t size)
|
||||||
{
|
{
|
||||||
/* 模拟 storage.c 行为: 目标区域非全 FF 则擦整扇区再写 */
|
/* 模拟 storage.c 行为: 目标区域非全 FF 则擦整扇区再写 */
|
||||||
|
|||||||
Reference in New Issue
Block a user