Files
STM32F4-Base/Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
T
wandering1 2575177e81 refactor(gate): bsp-err 四项检查改用自描述标签,废掉 R1~R4 这组撞车代号
纯改名,检查行为一个字没动:规则逻辑、阈值、豁免形状全部原样。

改名原因(用户一句「R4 到底是什么」问出来的):R1~R4 是我在 check_bsp_err.py 注释里
自造的序号,而这个裸形在本仓另有含义——R1~R8 已在 scripts/id_registry.json 登记为
"专项风险"的文档内编号(保护规划与看门狗的风险表都在用),带连字符的那一族又是审查
报告的可靠性 / 内存优化项编号(编号规范 §1 的"连字符承载语义"一行正是这么写的)。
**同一个裸形四种意思**。最刺人的一点:编号门禁**不会报**这种撞法,因为编号规范 §4.3
已把这些裸形声明为"同号不同义"、走 collides_with 白名单,校验 4 不计事故——同一天里
我起的另一组自造号被当场拦下、这组代号却一路绿灯,差别不在我做对了什么,而在白名单
挡的是"事故"、不是"可读性"。

新名同时是基线键的第二字段(格式 文件|标签|名字):
· err-type        错误通道不外泄(对外接口不拿 HAL_StatusTypeDef / bool 当成败)
· timeout-name    超时宏不取负值
· return-doc      公开函数必须写「返回值:」
· consume-result  返回值不得裸丢弃(须接住或显式 (void))
老名对照:R1=err-type、R2=timeout-name、R3=return-doc、R4=consume-result。

同步面:脚本里的规则码与 38 条基线键的字段**一起换**(中途不跑 --prune,否则改名会被
读成"38 条全还清 + 38 条新账"),换完 --emit-baseline 复核仍是 38/38/0、RC=0。现状类
文本全部换新名:AGENTS.md、pre-commit使用说明.md §2.27(新增"检查项叫什么"一行含老新
对照与撞号说明)、bsp_err.h 边界纪律、报告 §0.4 / §9 / 附录 A,以及代码注释里的 5 处
(gd5f2gq5ue.c/.h、nand_ftl.h、ui_menu_tab.h)。已提交的变更记录行里的老名按"冻结历史
不回改"处理,以 §2.27 对照行为准。ui_menu_tab.h 带 BOM,Edit 又剥掉、已按原样补回。

写说明文字时又被编号门禁拦了一次:我为了举例"老名撞车",在那段话里写了两个编号字面,
被判"未紧邻文档名 / 未登记前缀"共 3 类新账——连批评自造编号的段落本身都在自造编号。
改法是不写号、只描述形态并指向权威条目。

验证:@build **0 错 0 警告**,Program Size 与上一笔**逐字段相同**(Code 213,676、RO
18,860、RW 1,188、ZI 131,500)→ 证明确为注释与文案,故不跑 @test(无指令变化);
pre-commit run 全绿 **17 项 Passed**;check_doc_links --all --sections-strict 147
  篇 0
问题(中途我自己写的 §4.3 没紧邻文档名,被判成本文自引用,已补全路径);9 个文件字节
卫生 0 异常(UI 头 BOM 仍在、无 CR、无 U+FFFD、均有尾换行)。
2026-10-23 09:36:01 +08:00

420 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef GD5F2GQ5UE_H
#define GD5F2GQ5UE_H
/*
* 模块名称:GD5F2GQ5UE SPI NAND Flash 驱动
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
* 适用平台:STM32F407ZGT6 + SPI1 硬件 SPI
* 作者:王建锋
* 创建日期:2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
* 2026-07-17 王建锋 切换为硬件 SPI,修正擦除地址,参考 NuttX 驱动
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include <stdint.h>
/* ======================== 宏定义 ======================== */
/* SPI 命令码 */
#define GD5F_CMD_WRITE_ENABLE 0x06
#define GD5F_CMD_WRITE_DISABLE 0x04
#define GD5F_CMD_GET_FEATURE 0x0F
#define GD5F_CMD_SET_FEATURE 0x1F
#define GD5F_CMD_READ_ID 0x9F
#define GD5F_CMD_PAGE_READ 0x13
#define GD5F_CMD_READ_FROM_CACHE 0x0B
#define GD5F_CMD_PROGRAM_LOAD 0x02
#define GD5F_CMD_PROGRAM_EXEC 0x10
#define GD5F_CMD_BLOCK_ERASE 0xD8
#define GD5F_CMD_RESET 0xFF
/* 寄存器地址 */
#define GD5F_REG_PROTECT 0xA0
#define GD5F_REG_FEATURE 0xB0
#define GD5F_REG_STATUS 0xC0
#define GD5F_REG_DRIVER 0xD0
/* 状态位定义 */
#define GD5F_STATUS_OIP (1 << 0)
#define GD5F_STATUS_WEL (1 << 1)
#define GD5F_STATUS_E_FAIL (1 << 2)
#define GD5F_STATUS_P_FAIL (1 << 3)
#define GD5F_STATUS_ECCS0 (1 << 4)
#define GD5F_STATUS_ECCS1 (1 << 5)
/* Feature 位定义 */
#define GD5F_FEATURE_ECC_EN (1 << 4)
#define GD5F_FEATURE_QE (1 << 0)
/* 芯片参数 */
#define GD5F_PAGE_SIZE 2048
#define GD5F_SPARE_SIZE 64
#define GD5F_TOTAL_PAGE_SIZE 2112
#define GD5F_PAGES_PER_BLOCK 64
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
/*
* 页读取就绪判定 = WIP 轮询(与 Linux 主线 / GigaDevice 厂商驱动完全一致,
* 单发令形状)。2026-09-20 沉降 / 13h 二次发令史实统一记于
* GD5F2GQ5UE_Trap_Records.md TRAP-GD5F-05。
*/
#define GD5F_TOTAL_BLOCKS 2048
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
/*
* SPI DMA 传输阈值:超过该字节数走 DMA,否则走 CPU 轮询。
* 取值约束:① 必须 >= 存储路径上可能出现在任务栈的最大缓冲(dhara meta 132B、
* dhara_nand_is_free 64B)——任务栈随 FreeRTOS 堆驻留 CCM(0x10000000),
* 而 DMA1/DMA2 物理不可达 CCM,栈缓冲一旦进 DMA 即静默失败(2026-09-20 实证);
* ② 必须 < 页大小(2048),页级传输保留 DMA。
* 公开此宏供测试用例对齐阈值上下界,避免测试取值与驱动漂移。
*/
#define GD5F_DMA_THRESHOLD 1024
/* ==================== DMA 可达内存窗口(N-26,2026-10-12) ====================
* DMA1/DMA2 物理不可达 CCM(0x10000000~0x1000FFFF):以 CCM 缓冲做 DMA 源/目的
* 会静默不落地(2026-09-20 首轮 CCM 扩容实验实证根因;口径见
* docs/内存管理/内存内容分配策略.md §2 与 §4 规则 R2/R3)。
* 白名单窗口必须与分散加载 RW_IRAM1(0x20000000,128KB,SRAM1+SRAM2)一致
* (MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct);将来新增其它 DMA 可达内存
* (如外部 SDRAM)须同步扩展本窗口。
* ⚠ 缓冲**自身地址**无法编译期断言:.bss 地址链接期才确定,armcc 的常量表达式
* 取不到(__builtin 系在 MicroLIB 下亦不可用,见 Src/main.c Error_Handler 注释)。
* 静态缓冲的落位由 scatter(RW_CCM 只列 heap_4.o)+ nand_ftl.c 的 init 期自检
* (gd5f_dma_buf_ok)双层保证;动态传入缓冲由每次传输的守卫强制。 */
#define GD5F_DMA_RAM_BASE 0x20000000UL
#define GD5F_DMA_RAM_END 0x20020000UL
#define GD5F_CCM_RAM_BASE 0x10000000UL
#define GD5F_CCM_RAM_END 0x10010000UL
/* 编译期守卫(N-26):AC5 无 _Static_assert(C11 不支持),用项目惯用的
* 负数组 typedef(同 App/cfg/cfg_file.c、Drivers/BSP/UI/ui_menu_tab.h)——
* 条件不成立则数组尺寸 -1 → 编译错。
* ① DMA 白名单窗口与 CCM 窗口**永不得重叠**(有人改上面常量时立即爆炸);
* ② 阈值契约(见 GD5F_DMA_THRESHOLD 处注释):≥ 存储路径最大栈缓冲
* (dhara meta 132 B = DHARA_META_SIZE 上限)且 < 页大小(页级传输保留 DMA)。 */
typedef char gd5f_dma_window_no_ccm_check_t[
((GD5F_DMA_RAM_END <= GD5F_CCM_RAM_BASE) || (GD5F_DMA_RAM_BASE >= GD5F_CCM_RAM_END))
? 1 : -1];
typedef char gd5f_dma_threshold_contract_check_t[
((GD5F_DMA_THRESHOLD >= 132U) && (GD5F_DMA_THRESHOLD < GD5F_PAGE_SIZE))
? 1 : -1];
/* 坏块表持久化:保留块池数量(从 NAND 顶部向下挑选出厂好块存储 BBT) */
#define GD5F_BBT_POOL_COUNT 4
/* 制造商 ID 和设备 ID */
#define GD5F_MANUFACTURER_ID 0xC8
#define GD5F_DEVICE_ID 0x52
/* 返回值定义 */
#define GD5F_OK 0
#define GD5F_ERROR -1
#define GD5F_WAIT_EXPIRED -2 /* 旧名 GD5F_BUSY_TIMEOUT;S-5 的 timeout-name 检查:**返回码不许叫 *_TIMEOUT** */
#define GD5F_ECC_ERROR -3
#define GD5F_PROGRAM_FAIL -4
#define GD5F_ERASE_FAIL -5
#define GD5F_ID_MISMATCH -6
/* 控制引脚宏 */
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
/* ======================== 函数声明 ======================== */
/*
* 函数功能:初始化 GD5F2GQ5UE(读 ID + 使能 ECC + 解除块保护)
* 入口参数:无
* 返回值:0 - 成功,其他 - 错误码
* 限定条件:SPI1 和相关 GPIO 已由 CubeMX 初始化完成
* 函数说明:1. 发送复位命令并等待完成
* 2. 读取芯片 ID 并校验
* 3. 使能内部 ECC (B0h bit4)
* 4. 解除所有块保护 (A0h = 0x00)
*/
int32_t gd5f2gq5ue_init(void);
/*
* 函数功能:读取芯片 ID(MID + DID)
* 入口参数:mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
* 返回值:0 - 成功
* 限定条件:SPI 已初始化
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
*/
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 size_t > 0
* 返回值:GD5F_OK(0) - SPI 传输完成;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间。
* ⚠ **raw 契约(E-56,2026-10-03 显式文档化)**:返回 0 只表示 **SPI 传输完成**,
* **不代表数据无位翻转**——ECC 不可纠正页(ECCS 非零)在 SPI 层属正常完成,
* 本函数仍返回 0。数据正确性由 **ECC 层**保证:生产主路径经 dhara glue
* (`nand_ftl.c`)自有 ECC 检查保护;**直接使用本 raw API** 的调用方
* (测试/诊断路径)须自行处理位翻转。详见
* docs/问题记录/GD5F2GQ5UE_Trap_Records.md。
*/
int32_t gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 写入字节数 size_t > 0
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
*/
int32_t gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB)
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值:GD5F_OK(0) - 成功;其它 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:擦除操作以块为单位,offset 和 size 必须块对齐
*/
int32_t gd5f2gq5ue_erase(long offset, size_t size);
/*
* 函数功能:复位芯片
* 入口参数:无
* 返回值:0 - 成功
* 限定条件:SPI 已初始化
* 函数说明:发送 FFh 复位命令后等待 5ms
*/
int32_t gd5f2gq5ue_reset(void);
/*
* 函数功能:查询块是否坏块
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
* 返回值:0 - 好块,1 - 坏块
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:通过 BBT(Bad Block Table)查询,BBT 在 init 阶段 ECC 使能前扫描构建
*/
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);
/*
* 函数功能:标记块为坏块
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
* 返回值:无
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:更新 RAM 中的 BBT,并持久化到保留块池(带版本号 + CRC,
* 轮转写入,掉电安全);仅在管理区内的块才纳入持久化 BBT
*/
void gd5f2gq5ue_mark_block_bad(uint32_t block);
void gd5f2gq5ue_bbt_clear(void);
void gd5f2gq5ue_print_bbt(void);
/*
* 函数功能:重建 BBT(重新扫描出厂坏块并持久化,覆盖运行时注入的坏块)
* 入口参数:无
* 返回值:GD5F_OK - 成功,其他 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:用于测试(如 TC-STORAGE-03 注入坏块后)或出厂重置后恢复。
* 临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记,覆盖 RAM BBT,
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除运行时 mark 的坏块。
* 调用后建议再执行 nand_ftl_format() 清空 dhara map。
*/
int32_t gd5f2gq5ue_bbt_rebuild(void);
/*
* 函数功能:获取 dhara 可用块数(总块数减去保留块池)
* 入口参数:无
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
*/
uint32_t gd5f_get_usable_blocks(void);
/*
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
* 入口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
* p_version - 版本号输出 uint32_t* 不为 NULL
* 返回值:0 - 成功,其他 - 错误码
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
*/
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version);
/*
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
* 入口参数:无
* 返回值:0 - 成功
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:仅供测试/出厂重置使用;擦除后下次启动 load 找不到副本,
* 仅保留 RAM 中工厂扫描结果(调用方需自行还原 RAM BBT)。
*/
int32_t gd5f_bbt_wipe_pool(void);
/*
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
* 入口参数:无
* 返回值:0 - 成功
* 限定条件:gd5f2gq5ue_init() 已成功调用
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
*/
int32_t gd5f_bbt_reload(void);
/* ==================== SPI 原语(FTL 共享) ==================== */
/*
* 函数功能:等待芯片空闲(轮询状态位 BUSY/OIP)
* 入口参数:timeout_ms - 等待上限 uint32_t 毫秒
* 返 回 值:GD5F_OK - 已空闲;GD5F_WAIT_EXPIRED - 时限内未空闲(**可重试**,旧名 BUSY_TIMEOUT);GD5F_ERROR - SPI 读取失败
* 限定条件:SPI 已初始化;不得在中断里调用
* 函数说明:E-32 把"超时"与"读状态本身失败"分开返回,两者处置不同(前者再等、后者查硬件)
*/
int32_t gd5f_wait_busy(uint32_t timeout_ms);
/*
* 函数功能:发送 06h WRITE ENABLE(编程/擦除前必须成功执行一次)
* 入口参数:无
* 返 回 值:GD5F_OK - 成功;其它 - SPI 传输或等待失败错误码
* 限定条件:SPI 已初始化
* 函数说明:E-53 规定本函数返回值必须被检查并上报,忽略它会让后续 program/erase 静默无效
*/
int32_t gd5f_write_enable(void);
/*
* 函数功能:读取状态寄存器(0Fh / READ STATUS)
* 入口参数:p_status - 状态字节输出 uint8_t* 不得为 NULL
* 返 回 值:GD5F_OK - 成功(*p_status 有效);GD5F_ERROR - SPI 失败(*p_status 不可信,调用方不得读用)
* 限定条件:SPI 已初始化
* 函数说明:E-42 同族纪律——失败时出参内容不可信,故判成败看返回值而不是看 *p_status 是否像样
*/
int32_t gd5f_read_status(uint8_t *p_status);
/*
* 函数功能:页读取(13h:把数据从存储阵列装进内部缓存)
* 入口参数:page_addr - 物理页地址 uint32_t 0 - GD5F_TOTAL_PAGES-1
* 返 回 值:GD5F_OK - 命令已发出且等待完成;其它 - 透传底层 SPI/等待错误码
* 限定条件:SPI 已初始化;**本函数成功不代表数据无位翻转**,正确性由随后的 gd5f_check_ecc() 判定
* 函数说明:装载完成后须用 gd5f_read_from_cache() 取数据
*/
int32_t gd5f_page_read(uint32_t page_addr);
/*
* 函数功能:从内部缓存读出数据(0Bh READ FROM CACHE)
* 入口参数:column - 缓存内起始列 uint16_t 0 - GD5F_PAGE_SIZE-1
* p_buf - 数据输出缓冲 uint8_t* 不得为 NULL
* size - 读取字节数 uint32_t 1 - GD5F_PAGE_SIZE-column
* 返 回 值:GD5F_OK - 成功;GD5F_ERROR - 参数非法或 SPI 失败(**此时 p_buf 内容不可信**)
* 限定条件:必须先成功调用 gd5f_page_read() 完成阵列→缓存装载
* 函数说明:E-42 同族——调用方不得在失败分支继续使用 p_buf(FTL 侧据此把读失败保守判为"非空页")
*/
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
/*
* 函数功能:把数据装进内部缓存(02h PROGRAM LOAD)
* 入口参数:column - 缓存内起始列 uint16_t 0 - GD5F_PAGE_SIZE-1
* p_buf - 待写数据 const uint8_t* 不得为 NULL
* size - 字节数 uint32_t 1 - GD5F_PAGE_SIZE-column
* 返 回 值:GD5F_OK - 装载完成;GD5F_WAIT_EXPIRED - 装载后等待 OIP 超时(**可重试**);GD5F_ERROR - 参数/SPI 失败
* 限定条件:先成功执行 gd5f_write_enable();缓冲须在 DMA 可达窗口内(GD5F_DMA_RAM_* 白名单)
* 函数说明:装载本身不写阵列,真正的编程由 gd5f_program_exec() 触发
*/
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
/*
* 函数功能:触发页编程(10h PROGRAM EXEC)并检查 P_FAIL
* 入口参数:page_addr - 目标物理页地址 uint32_t 合法页号
* 返 回 值:GD5F_OK - 编程完成且无 P_FAIL;**GD5F_PROGRAM_FAIL - 芯片置 P_FAIL(块退化的信号)**;其它 - 透传底层/SPI 错误
* 限定条件:本调用前须已 gd5f_program_load() 成功
* 函数说明:P_FAIL 是**器件自己认定这一页写不下去**的位,与 SPI 层错误性质不同(FTL 据此标坏块)
*/
int32_t gd5f_program_exec(uint32_t page_addr);
/*
* 函数功能:擦除一个块(D8h 擦除序列 + 等待 + 检查 E_FAIL)
* 入口参数:block_addr - 物理块地址 uint32_t 0 - GD5F_TOTAL_BLOCKS-1
* 返 回 值:GD5F_OK - 擦净;**GD5F_ERASE_FAIL - 芯片置 E_FAIL(坏块信号)**;其它 - 透传底层/SPI 错误
* 限定条件:先成功执行 gd5f_write_enable()
* 函数说明:E_FAIL 与 SPI 错误的区别同 program_exec;擦除超时归 GD5F_WAIT_EXPIRED(可重试)
*/
int32_t gd5f_block_erase(uint32_t block_addr);
/*
* 函数功能:检查上一次页读取的 ECC 结果(读 00h/BCh 特征寄存器的 ECC 状态)
* 入口参数:无
* 返 回 值:GD5F_OK - 无不可纠错误(可能含已纠正的位翻转);GD5F_ECC_ERROR - **存在不可纠正的 ECC 错误**
* 限定条件:SPI 已初始化且 ECC 已使能(init 期扫描在使能前调用本函数无意义)
* 函数说明:这是数据正确性的唯一判定点——SPI 层读到"内容"不等于"内容对"(见 §8.1 与本头 E-42/E-53 注释)
*/
int32_t gd5f_check_ecc(void);
/*
* 函数功能:判定缓冲整段是否位于 DMA 可达内存(GD5F_DMA_RAM_* 白名单窗口)
* 入口参数:p_buf - 传输缓冲首地址 const void*
* size - 传输字节数 size_t
* 返回值:1 - 整段 DMA 可达;0 - 不可达(含 size=0 / 地址回绕 / 越窗)
* 限定条件:无
* 函数说明:N-26(2026-10-12)由 gd5f2gq5ue.c 私有守卫升级为公开 API——
* 驱动内部每次 >阈值 传输前用它(不可达即回落 CPU 轮询),
* FTL 层(nand_ftl.c)在 init 期用它自检静态页缓冲的落位。
*/
int gd5f_dma_buf_ok(const void *p_buf, size_t size);
/*
* 函数功能:NAND 长耗时操作看门狗心跳弱钩子
* 入口参数:无
* 返回值:无
* 限定条件:任意上下文可调(内核启动前调用为空操作)
* 函数说明:gd5f_wait_busy 的长轮询(块擦除等待最长 5 s、GC 页拷贝、
* 日志写)都运行在调用者任务上下文(default/ftp/ui/timesync 等);
* 本钩子默认空实现,App/util/task_watchdog.c 提供强符号,按调用者
* 任务句柄路由到该任务的心跳位,防止长耗时窗口心跳缺席导致 IWDG
* 误复位(2026-10-02 台架:0 字节 STOR 截断 → dhara GC > 800 ms
* 无心跳 → 停喂 → IWDG 误复位)。
*/
void nand_ftl_wd_beat(void);
/*
* 函数功能:NAND 长耗时操作看门狗直接喂狗弱钩子
* 入口参数:无
* 返回值:无
* 限定条件:任意上下文可调(内核启动前 / IWDG 未启动时为空操作)
* 函数说明:心跳钩子(nand_ftl_wd_beat)只能维持"心跳位",喂狗动作本身在
* defaultTask 主循环(task_wd_poll)——若长耗时 FTL 操作(GC/整卷
* 格式化)发生在 defaultTask 上下文,主循环被阻塞在 FTL 调用里,
* 喂狗点停摆 → 心跳位再新也无人喂 → IWDG 误复位(2026-10-02 台架
* 实证:console tx fault 条目落盘触发 GC,poller 饥饿 ~2 s → IWDG
* 复位、无任何 stall 日志)。本钩子让 FTL 长路径在操作进行中直接
* 喂狗,覆盖"喂狗点自身被长 I/O 阻塞"的形态。强符号由
* App/util/task_watchdog.c 提供(仅 IWDG 已启动时生效)。
*/
void nand_ftl_wd_feed(void);
/*
* 函数功能:SPI1 DMA 完成 / 错误通知(由实际注册 HAL SPI 回调的模块转调)
* 入口参数:无
* 返回值:无
* 限定条件:中断上下文(HAL SPI 回调内),两者只写一个完成标志
* 函数说明:HAL 的 SPI 完成/错误回调是全局唯一符号,本模块只实现 TxCplt/RxCplt
* 两个(供自己的收发用),Error 回调的所有权留给回调的实际注册方
* (当前 = Drivers/BSP/CH395F/ch395f.c,按 hspi->Instance 分发)。**SPI1
* 分支必须转调这两个函数**,否则本模块的 DMA 等待在出错时只能靠超时
* 兜底。2026-10-22:取代原先被 CH395F 跨模块裸 extern 直接写的全局变量。
*/
void gd5f_spi1_notify_done(void);
void gd5f_spi1_notify_error(void);
#ifdef __cplusplus
}
#endif
#endif /* GD5F2GQ5UE_H */