Files
STM32F4-Base/Drivers/BSP/bsp_err.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

144 lines
9.6 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.
/*
* 模块名称:BSP 统一错误与等待约定
* 模块功能:给 Drivers/BSP 下的所有驱动一个共用的返回码语义与等待时长语义,
* 使「这次失败能不能重试」成为跨模块可判定的信息,而不是每个模块各自猜。
* 适用平台:STM32F4 系列(ARMCC v5);本头文件不依赖 HAL,模拟器/主机侧同样可编译。
* 作者:王建锋
* 创建日期:2026-10-23
* 修改记录:
* 2026-10-23 王建锋 创建:错误码 + 可重试判定 + 等待值契约(首个试点 RS485;同日挂 bsp-err 钩子)
*/
#ifndef BSP_ERR_H
#define BSP_ERR_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* ============================================================================
* 一、错误码:0 = 成功,负值 = 失败
* ==========================================================================*/
/*
* 为什么 0 必须是成功:本仓现存调用点绝大多数已经写成 != 0 / == 0(实测跨模块调用
* 点约 798 处),反向约定要把每个判据都改一遍,纯赔本。
*
* ⚠ **一个数量与一个错误码不许挤在同一个返回值里**(面向新 API 的原则)。
* 现存两族,都**有意保留**、都已核对:
* · net_*(返回字节数,0=对端关闭,-1=错误,-2=超时无数据)——刻意的 BSD 形状,
* 已裁定不纳入(见文末边界纪律 3);
* · tpafe5160_snapshot() / adc_input_poll()(1=取到新帧,0=无新帧,BSP_ERR_PARAM=参数错)
* ——**0 不是错误**,是"这一拍没有新数据",套成败码会把慢消费者的正常路径变成失败。
* 反例订正:gd5f2gq5ue_read/write/erase 核过实现与头注释是"0=成功、其它=错误码",
* **不是**字节数返回,别再拿它当反面例子(本头文件初稿错过这个错)。
* 新 API 要报"做了多少"就走出参:bsp_err_t xxx(..., size_t *p_done)。
* 为什么值得写死这条:三态返回(>0 / 0 / <0)要求调用方把三种形态都判到,而实测调用点
* 大多是"先赋值再判 != 0"——第三种形态就是这么被吞掉的。
*/
typedef int bsp_err_t;
#define BSP_OK ((bsp_err_t)0) /* 成功 */
#define BSP_ERR_PARAM ((bsp_err_t)-1) /* 参数非法:指针空、长度越界 */
#define BSP_ERR_STATE ((bsp_err_t)-2) /* 调用时机不对:未初始化、状态机不允许 */
#define BSP_ERR_TIMEOUT ((bsp_err_t)-3) /* 超时 —— 可重试 */
#define BSP_ERR_IO ((bsp_err_t)-4) /* 总线/介质读写失败 */
#define BSP_ERR_NO_DEVICE ((bsp_err_t)-5) /* 芯片不在位、无响应(上电时序或硬件) */
#define BSP_ERR_BUSY ((bsp_err_t)-6) /* 对端或本侧忙 —— 可重试 */
#define BSP_ERR_CRC ((bsp_err_t)-7) /* 校验失败:ECC / CRC / 和校验 */
#define BSP_ERR_NO_SPACE ((bsp_err_t)-8) /* 容量或块池耗尽 */
#define BSP_ERR_DENIED ((bsp_err_t)-9) /* 被策略拒绝:写保护、鉴权、门未开 */
#define BSP_ERR_PARTIAL ((bsp_err_t)-10) /* 部分完成,配合 *p_done 判断做到哪一步 */
/*
* 函数功能:判断一个失败码是否「重试可能成功」
* 入口参数:e - 待判定的 bsp_err_t
* 返回值:非 0 = 可重试;0 = 重试也没用(参数、状态、策略、无器件这类确定性失败)
* 限定条件:只用于已确认失败的码(传 BSP_OK 得 0,不算错)
* 函数说明:这才是本头文件真正值钱的一条。现状是把「可重试」写在注释里——
* gd5f2gq5ue.c 的返回值注释就写着「GD5F_BUSY_TIMEOUT(-2) - 超时(可重试)」,
* 语义在,但跨模块不可判定:上层想统一退避,就得为每个驱动各写一遍 if。有了这条,
* 「超时/忙退避、确定性失败立刻放弃」可以只写在一处。不要据此无上限重试:
* 上限、退避节奏、喂看门狗都由调用方负责(见文末约定)。
*/
#define BSP_ERR_CAN_RETRY(e) (((e) == BSP_ERR_TIMEOUT) || ((e) == BSP_ERR_BUSY))
/* ============================================================================
* 二、等待时长契约:把「这个数到底是多久」写死
* ==========================================================================*/
/*
* 实测现状(同形不同义,这就是本契约存在的全部理由):
* · SD2506_I2C_TIMEOUT_MS 100、CH395F_SPI_BYTE_TIMEOUT_MS 100U
* 真的毫秒:喂 HAL 的 tick 域,本仓 HAL 时基 1ms。
* · osWaitForever 与 HAL_MAX_DELAY 同为 0xFFFFFFFFU
* 值相同、域不同:一个进 CMSIS-RTOS2、一个进 HAL。于是「把上层拿到的超时值直接
* 转手传给另一层」这种代码既不报错也不变慢,直到某天真等死。
* · GD5F_SPI_TIMEOUT 100(**核对后的准确说法,我先前记成"自旋圈数"是错的**)
* 它其实也是毫秒(全部用法都是 HAL_* 超时参数或 HAL_GetTick() 差值,逐行核过),
* 真正的问题是**名字没带 _MS 后缀**:读代码的人必须回实现里查一遍才知道单位,
* 而同名的 *_TIMEOUT 在别的模块里又是返回码——单位与用途都靠"点进去看"。
* · GD5F_BUSY_TIMEOUT、TPAFE5160_BUSY_TIMEOUT = -2(**2026-10-23 已消**:前者改名
* GD5F_WAIT_EXPIRED(值不变),后者收编成 BSP_ERR_TIMEOUT;留此记录是因为
* "时长宏与返回码共用一个名字形状"这类坑只写规则不会自己消失,得留个实证锚点)
*
* 因此约定:
* 一、公开 API 的超时参数一律 uint32_t 毫秒;
* 二、要无限等就显式传 BSP_WAIT_FOREVER,不许写 0xFFFFFFFFU、-1 或 0(0 是不等);
* 三、自旋圈数型超时不许叫 *_TIMEOUT_MS,命名须含 LOOP/COUNT 且注释写明非时间量;
* 返回码一律不许叫 *_TIMEOUT,改返回 BSP_ERR_TIMEOUT。
*
* ⚠ 两条硬纪律(AGENTS 铁律,这里只复述到最近的使用点):
* · 内核启动前不得传 BSP_WAIT_FOREVER:osKernelInitialize 之前任何阻塞调用都会挂;
* · 长等待与重试循环必须在循环内喂看门狗(本仓 IWDG 有 required_mask 机制)。
*/
#define BSP_WAIT_NONE ((uint32_t)0U) /* 不等待,立即返回 */
#define BSP_WAIT_FOREVER ((uint32_t)0xFFFFFFFFU) /* 无限等待,仅限任务上下文 */
/*
* 函数功能:把毫秒表达成超时入参(本仓 tick = 1ms,故等价)
* 入口参数:ms - 毫秒数
* 返回值:可直接传给等待类 API 的 uint32_t
* 限定条件:仅当 configTICK_RATE_HZ = 1000 时与 tick 数等价
* 函数说明:留着它的理由是换算点必须只有一处——哪天 tick 改了只改这里,而不是去
* 各驱动里翻 0xFFFFFFFF 和裸数字。
*/
#define BSP_WAIT_MS(ms) ((uint32_t)(ms))
/* ============================================================================
* 三、边界纪律(写在这里是为了说清什么算「不该改」)
* ==========================================================================*/
/*
* 1. 第三方的码不改、不吞、就地映射:HAL_StatusTypeDef、dhara_error_t、FRESULT、
* CH395F_ERR_*(芯片寄存器原生码)、FTP 的 226/425/450/550 协议码,全部保留原样,
* 只在跨越我们模块边界那一行映射成 bsp_err_t。它们是外部契约或协议本身,改名等于
* 伪造语义。映射函数由各驱动自己写一个 static——本头文件刻意不 include HAL,
* 以保持主机侧可编译。
* 2. 公开 API 面不得返回 HAL_StatusTypeDef 或 bool 表示成败:bool 把「为什么失败」整条
* 丢掉,HAL 枚举把 HAL 焊进调用方的 include 链。
* 3. net_* 的 BSD 兼容层保持 BSD 形状(返回字节数 + 每任务 errno 槽),**不进本约定**
* (用户确认 2026-10-23):它的调用方要的就是那个形状,模拟器的 Winsock 后端也照同一
* 形状实现;归一只到芯片层 ch395f_*。要收口的是"芯片码到 bsp_err_t"这一层,不是把
* BSD 形状改成另一套——那只会让 net_* 的调用方在每个判据上多绕一道。
* 4. **丢弃返回值必须写 (void),或由调用点接住**——本条已由 bsp-err 的 **consume-result** 检查机械化(2026-10-23)。
* 为什么不能指望工具,两条实测负结果:① ARMCC v5 对 __attribute__((warn_unused_result))
* **完全不生效**——凌晨在 rs485_transmit 上做过实验,属性在位、调用点裸调不接返回值,构建
* 仍 0 错 0 警告;② **cppcheck 也照不出来**——按本仓自己的 --enable 跑 gd5f2gq5ue.c,对 14 处
* 丢弃 HAL_SPI_Transmit/Receive 返回值、以及"未初始化局部量直接进 P_FAIL 判定"**一条不报**
* (它假定被调函数会写出参,而失败路径根本不写)。所以"丢弃底层传输的返回值"今天没有任何
* 工具会响,只能靠脚本 + 书写约定;consume-result 上线当天就照出 37 处存量丢弃,其中一处是真窟窿:
* bbt_rebuild 的 ECC 重开被丢弃(详见 ../测试说明/存储维护与容量恢复.md §4.3)。
* 5. 重试由调用方决定:必须有上限、必须喂看门狗、不得跨越就绪屏障盲目等。本头文件
* 不提供 retry helper——一个通用 while 重试包装太容易被拿去包住无限等待;每个日志
* 调用点内联约 216 B 栈的教训在前,栈预算不欢迎「顺手加一层」。
*/
#ifdef __cplusplus
}
#endif
#endif /* BSP_ERR_H */