存储子系统
super_agent_bot edited this page 2026-11-02 07:55:38 +08:00

存储子系统

一块 SPI NAND 上的三层软件栈: FatFs(FAT32)→ dhara FTL → GD5F2GQ5UE 驱动。

细节见 docs/架构设计/STM32F4-Base存储架构说明.md、docs/子系统设计/配置文件设计说明.md、docs/子系统设计/运行与自检记录设计说明.md。

1. 软件栈

NAND 上的三层存储栈

图: FatFs -> dhara FTL -> NAND 驱动.

1.1 器件与组织

NAND 物理分区与保留块

图: 物理分区 - 顶部保留块存坏块表.

项 值
型号 GD5F2GQ5UE
容量 2 Gbit(256 MB)
组织 2048 块 x 64 页 x 2048 B
接口 SPI1(PE0 CS / PB3 SCK / PB4 MISO / PB5 MOSI / PB8 WP / PE1 HOLD)
坏块表 持久化在顶部 4 个保留块, 不交给 dhara 管理

1.2 三层职责

  • FatFs: FAT32 文件系统, 提供 f_open/f_read/f_write/f_mkfs 等; 根目录 / 也是 FTP 的根。
  • dhara FTL: 逻辑块映射、磨损均衡、坏块管理、垃圾回收; 对上层屏蔽 NAND 物理特性。
  • GD5F 驱动: 页读写、ECC、BBT 扫描与持久化、忙/超时/ECC 错误分类。

2. 挂载、格式化与自愈

defaultTask 是存储的属主:

  1. f_mount(forced);
  2. 无卷 → 自动 f_mkfs(FAT32);
  3. 挂载后做卷健康探针;
  4. FAT 区陈旧/损坏 → 自愈重格式化;
  5. 加载 /config.ini(配置引擎属主)→ 置 SYS_EVT_STORAGE_READY;
  6. 进入 LED / syslog 落盘 / 配置 poll / 栈水位采样主循环。

注意: 自愈逻辑只处理“卷损坏”类错误; 严禁把文件名/参数类错误码(如 FR_INVALID_NAME)归入卷损坏触发集, 否则会误格式化。

3. 文件与目录

路径 内容 写者
/config.ini 通用配置([network]、[ftp] 等段) defaultTask(配置引擎属主)
/log/run.log 运行记录, 定长 64 B/条, 1024 条 / 64 KB 上限 defaultTask(syslog 唯一写者)
/log/selftest.log 自检记录, 定长 64 B/条, 512 条 / 32 KB 上限 defaultTask
/prot_set.bin 保护定值持久化文件 defaultTask(收到 SYS_EVT_PROT_SET_SAVE 后)
/ 其他文件 FTP 上传/下载的用户文件 ftpTask(经 FatFs)

⚠ FF_USE_LFN=0: 只支持严格 8.3 短名, 不得以点开头。自愈/探测逻辑不要用 .fschk 这类名字(会恒返 FR_INVALID_NAME)。

4. 配置引擎与 /config.ini

配置文件的分层与原子写

图: 配置引擎 - 段键表 + 临时文件 + rename.

4.1 格式

  • 位置: MCU 为卷根 /config.ini; PC 模拟器为工作目录 sim_config.ini。
  • 语法: key=value; [section]; ; / # 注释行; UTF-8(容忍 BOM); CRLF/LF 均可。
  • 行长 >127 字符该行无效; 文件 >4096 B 视为损坏。
  • 值类型: IP4、MAC、UINT(十进制, 兼容 0x)、STR(≤31 字符)。

4.2 出厂默认

; DTU 通用配置文件
[network]
; IP 地址
ip=192.168.1.100
; 子网掩码
mask=255.255.255.0
; 默认网关
gateway=192.168.1.1
; MAC 地址 (全零 = 使用芯片内置 MAC)
mac=00:00:00:00:00:00

MAC 全零是“使用芯片内置 MAC”的哨兵, 不是“MAC 为 0”。

4.3 保存语义

  • 外科手术式保存: 只替换受管键行, 保留用户注释、未知段、未知键(降级安全)。
  • 原子写: 临时文件 + unlink 目标 + f_rename; 并发读者只会看到旧完整文件或新完整文件。
  • 变更检测: defaultTask 每 2 s f_stat 比对 (size, mtime), 连续 2 次稳定才重载(沉降去抖); [network] 段变化触发 SYS_EVT_NET_RECONFIG, 网络代次 +1, 服务自愈。
  • 旧档: /cfg/netcfg.bin(NCFG 二进制)已不再读取; 存在则忽略(不读、不删、不迁移), 配置回落工厂默认。
  • 恢复出厂: FTP DELE /config.ini → 下次上电重新生成模板。

4.4 FTP 配置段

[ftp] 段保存 user / pwd_hash(SHA-256(user‖password))/ write。出厂默认 admin / admin; 默认口令在用有启动告警, 现场必须修改。详见网络子系统。

5. 日志与记录

  • syslog_post(SYSLOG_RUN / SYSLOG_SELFTEST, ...) 写入定长 64 B 文本记录; defaultTask 是唯一写者。
  • /log/run.log: 运行记录(上电/复位/关键事件), 1024 条 / 64 KB 上限。
  • /log/selftest.log: 自检记录(故障类), 512 条 / 32 KB 上限。
  • UI 的“运行记录/自检记录”页从同一引擎读取; FTP 可直接下载。
  • 控制台实时日志走 USART1: 4 KB 环形缓冲 + defaultTask 唯一发送(行级原子), 不是写文件。

6. FatFs 移植版的两个实证陷阱

这两条已多次导致“看起来成功、实际没写成”:

6.1 追加写必须带 FA_WRITE

该移植的 FA_OPEN_APPEND 不含 FA_WRITE。只用 FA_OPEN_APPEND 打开的文件, f_write 会恒返 FR_DENIED。正确写法:

f_open(&fp, path, FA_WRITE | FA_OPEN_APPEND);

6.2 f_write 可能返回 FR_OK + 短写

簇分配失败时, f_write 可能返回 FR_OK 但实际写回字节数不足(没有错误码)。判断失败必须校验写回字节数:

UINT wrote = 0;
FRESULT fr = f_write(&fp, buf, len, &wrote);
if (fr != FR_OK || wrote != len) {
    /* 失败处理 */
}

7. 测试与维护

  • 不要随便跑破坏性 GD5F 测试: 跑一轮破坏性测试会让 dhara 多记一个坏块, 且该计数持久化, 普通恢复清不掉。ENABLE_DESTRUCTIVE_TESTS 默认关闭; 关闭时套件只读, 可在在用设备上跑。
  • 容量恢复/全片擦除流程见 docs/测试说明/存储维护与容量恢复.md。
  • 存储性能/坏块/ECC 测试见 docs/测试说明/GD5F2GQ5UE_Test_Guide.md。
  • 改驱动前先读 docs/问题记录/GD5F2GQ5UE_Trap_Records.md。

8. 延伸阅读

主题 文档
存储架构(含陷阱与标志位) docs/架构设计/STM32F4-Base存储架构说明.md
配置引擎 docs/子系统设计/配置文件设计说明.md
网络配置(含 FTP 鉴权) docs/子系统设计/网络配置说明.md
运行/自检记录 docs/子系统设计/运行与自检记录设计说明.md
存储维护与容量恢复 docs/测试说明/存储维护与容量恢复.md
GD5F 陷阱台账 docs/问题记录/GD5F2GQ5UE_Trap_Records.md