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
+6 -6
View File
@@ -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
``` ```
--- ---
+1 -1
View File
@@ -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` = 16256W25Q32 512KB127 数据扇区 × 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 | 最新全局序号 |
+4 -4
View File
@@ -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 单调递增) |
+4 -4
View File
@@ -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
* *
+45
View File
@@ -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` |
### 兼容性
- 旧固件事件区在 0x010000256KB),新固件在 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=0x168 例全过(环形回绕自动适配 16256)
- `tests/test_ble_offlog.c`mock `g_offlog_part`capacity 断言 162567 例全过
- 地址范围模拟:四芯片事件区 0x090000 起均不超芯片容量
---
## 2026-08-12 — 续2:第二分包丢失 — 根因确认(响应缓冲越界踩踏 g_notify_buftemp ## 2026-08-12 — 续2:第二分包丢失 — 根因确认(响应缓冲越界踩踏 g_notify_buftemp
### 破案过程(三份现场日志 + .map 对照) ### 破案过程(三份现场日志 + .map 对照)
+5 -2
View File
@@ -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 */
+2
View File
@@ -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 则擦整扇区再写 */