/* 掉电不丢的一块字节区(需求 §10.7 的「落盘」)—— HAL 的第六样东西。 计划 §4.1 写着「HAL 只抽象四样」,§10.3 的两把锁是第五样。这里的第六样同样 要给个理由:§10.7 要求事件**在上位机不在线时也记得下来**,而「记得下来」在 ESP32-S3 上就是往 NOR flash 的一个分区里写。事件日志本身(环形、批量、 按序号增量拉取)是与平台无关的逻辑,宿主机上必须跑得到;能跑不到的只有 「怎么擦一个扇区、怎么写一段字节」这三行。抽的就是这三行。 ---- 抽象的是 NOR flash,不是文件 ---------------------------------------- 接口上带着 NOR 的两条物理事实,因为**上层绕不开它们**: 1. **写只能把 1 变成 0。** 同一个位置写两遍不是覆盖,是按位与。 所以 sim 的实现也是按位与 —— 真板上「以为覆盖成功了」的那种错, 在一个 memcpy 式的假实现里一次也复现不出来。 2. **擦除的粒度是扇区**(4 KB),擦完全是 0xFF。 还有一条没写进接口但同样要记住的:擦一次就少一次寿命(十万量级)。 §10.7 的量级是「每天几十条」,17 个扇区轮着用,一辈子擦不了几轮。 ⚠️ **不做 wear leveling、不做坏块管理、不做文件系统。** 这块区只有一个用户 (evlog),它自己就是个环形缓冲,天然轮流擦全部扇区。套一层 SPIFFS 反而 多出一堆掉电一致性问题 —— 那正是这一节最不想要的东西。 */ #ifndef HAL_STORE_H #define HAL_STORE_H #include #include "hal_err.h" #ifdef __cplusplus extern "C" { #endif typedef struct { uint32_t sector_bytes; /* 擦除粒度。ESP32-S3 的 NOR 是 4096 */ uint32_t sectors; /* 这块区有几个扇区 */ } hal_store_geom_t; /* 幂等。ESP 侧是找到 `evlog` 分区;找不到返回 HAL_ERR_STATE (分区表里漏了这一项 —— 那是编译期就该发现的错,不是硬件故障)。 */ hal_err_t hal_store_init(void); /* 没 init 时两个字段都是 0。调用方拿它算容量,所以**不许猜**这两个数。 */ void hal_store_geometry(hal_store_geom_t *g); /* 读。off/len 以字节计,越界返回 HAL_ERR_ARG。 */ hal_err_t hal_store_read(uint32_t off, void *buf, uint32_t len); /* 写。⚠️ **按位与语义**(见上):往没擦过的地方写,得到的是两次内容的与, 而且**不报错**。想覆盖就得先擦整个扇区。 off 与 len 都要求 4 字节对齐(esp_flash 的要求,sim 里同样拦)。 */ hal_err_t hal_store_write(uint32_t off, const void *buf, uint32_t len); /* 擦一个扇区(整块变 0xFF)。sector 是索引,不是字节偏移。 */ hal_err_t hal_store_erase(uint32_t sector); #ifdef __cplusplus } #endif #endif /* HAL_STORE_H */