feat(S-5): 收编 SD2506/TPAFE 并给 dhara 失败分类,门禁基线 19→17
前一笔立契约与门禁(`Drivers/BSP/bsp_err.h` + `bsp-err` 钩子 + RS485 试点);这一笔按用户
指定顺序 C3 → C1 → C2 把三个模块真正收编,并让存储失败第一次变得可读、可分类。
**C3 · SD2506 整模块(11 个公共 API + 6 个内部 helper)**:三枚私有宏 `SD2506_OK`/
`SD2506_ERROR`/`SD2506_I2C_ERROR` **删除而不是留别名**——留别名等于新代码还能用旧形状,
把迁移洗掉;编译器替我们找全所有调用点。新增 `sd2506_err_from_hal()`,**把 I2C 超时从
"和没人应答共用一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、
I2C1 上电时序敏感,这是本波唯一真正的语义改善,不是改名。`sd2506_init` 的 charge 寄存器
**回读校验不符**原返参数类错误,改归 `BSP_ERR_IO`(那不是调用方的错)。
**C1 · 同形两义**:`GD5F_BUSY_TIMEOUT` → `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫
TIMEOUT 其实是返回码")。**GD5F 私有码集整体收编刻意没做**:169 处引用里 48 处在板级套件、
还要动 Trap 记录用词,属另一波带真机+套件回归的工作,不夹带。TPAFE5160 收编 23 处:
`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到器件无响应比
私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。实测 `tpafe5160.o`/`gd5f2gq5ue.o` 的 Code
增量为 **0**——改名不产生一条指令。
⚠ **`tpafe5160_snapshot()` 刻意不套成败码**:它是 1=有新帧 / 0=无新帧 / −1=参数错 的三态
**状态**返回,0 不是错误而是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。
顺带订正我上一版在 `bsp_err.h` 的断言——"本层只有 `net_*` 把数量与错误挤同一返回值"是错的,
`snapshot`/`adc_input_poll` 就是第二族;两族现都已写明并各注保留理由。
**C2 · FTL 坍缩点分类**:`ftl_flush_cache`/`ftl_read_page` 不再把 dhara 失败压成
`GD5F_ERROR`,
按**处置差异**分三类,并在最早同时知道 LPN 与原始码的位置就地打印:`ECC`/`TOO_BAD` →
`BSP_ERR_CRC`(页级,重试或换页,不代表卷坏);`JOURNAL_FULL`/`MAP_FULL` →
`BSP_ERR_NO_SPACE`(介质健康但块池/日志见底,此时重格式化只是把问题推回去);
`BAD_BLOCK`/`RECOVER`/`CORRUPT_MAP`/越界 → `BSP_ERR_IO`(完整性故障,保卷 + 上报,不许自愈
重格式化)。这与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编
`error.c`,用 `dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write` 仍回
`RES_ERROR`
(FatFs 契约);要让 defaultTask 卷健康探针按类分支还差跨调用存活的快照 → C4 待还。
**两次靠核实拦住的自我错误**:① 我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进
varargs 的格式 bug——核 `Lib/dhara/error.h` 后确认 `dhara_error_t` 是 `enum`(标量),那行日志
本来合法,改了反而是引入错误;② `bsp-err` 一次 `replace_all` 越界把 8 处 **GD5F 码空间**的
判据改成 `!= BSP_OK`(数值同为 0、无行为变化),但那正是"把两个码空间混起来",已逐一还原。
`cppcheck` 亦抓到 `cls` 声明作用域过宽,已收进失败分支。
**验证**:`@build` 0 错 0 警告(`Code` 213,648、RO 18,860、`ER_IROM1` 232,508 B /
22.2%、
`ROM 合计` 233,696;**RAM 三项零增量**);`@test` PASS(692 ms,烧入戳 `Oct 23 01:35:32`)
且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` 与 ID/时间/温度/电池四条读回正常、
`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、`adc=` 帧计数持续递增(证明
snapshot 三态未被改坏)、`[ERR]`/Fault/FAIL 0 条。主机单测:系统时钟 100/100、dhara NAND
移植层 149/149(两者都因本次改动需补 `Drivers/BSP` include 路径,补后在钩子内实跑 Passed)。
`pre-commit run` 全量 RC=0(20 个实跑钩子 Passed);`bsp-err` 基线 **19 → 17**(还掉两个
`*_BUSY_TIMEOUT = -2`)、`--prune` 干净;`doc-links --all --sections-strict` 147 篇
0 问题;
`id-check --strict`、`layer-deps`、`mem-numbers`、`mem-budget`、`ui-golden` 全过;33
个文件
字节卫生 0 异常(无 CR / 无 U+FFFD / 无行尾空格 / 均有尾换行)。
**口径与数字联动**:新增 `docs/架构设计/STM32F4-Base存储架构说明.md` §10.4(三类码与处置
理由);`pre-commit使用说明.md` §2.27 与 `AGENTS.md` 基线计数 19 → 17;TPAFE README、
GD5F 测试指南与 Trap 记录均**保留旧名留痕**以便对旧日志;配图桶按 .map 对象级增量重算
(`dhara/FTL` 10,931 → 11,222,+291 全部落在 `nand_ftl.o`;应用代码/UI +36 落在
`app_main.o`;其余散量入"其他"7,557;桶和 = Code+RO 断言通过、文本溢出 0);
`ram_baseline --update/--write`、`ram_report_20261023.md` 快照、报告 §1.3/§5.1/§5.4
与
部署文档 §3.3 数字全部同步;报告 §0.4/§9 的 S-5 状态改为 🟡 主体已落地。
This commit is contained in:
@@ -567,3 +567,18 @@ repos:
|
||||
language: system
|
||||
files: ^Drivers/BSP/|^scripts/check_layer_deps\.py$|^\.pre-commit-config\.yaml$
|
||||
pass_filenames: false
|
||||
|
||||
# 15) BSP 错误码与超时命名约定(报告 S-5 的机械检查那一半)。
|
||||
# 三条规则:公开 API 不得用 HAL_StatusTypeDef/bool 当错误通道(谓词 is_/has_/can_/valid
|
||||
# 不在此列);*_TIMEOUT 宏不得取负值(名字叫 TIMEOUT 其实是返回码,读代码的人会当成
|
||||
# 时长);非 void 的公开 API 必须有「返回值:」注释。存量钉 BASELINE、只减不增(2026-10-23 首清点 19 条,C1 已还 2 → 17 条),
|
||||
# 修完跑 --prune 点名收紧基线。约定正文 = Drivers/BSP/bsp_err.h,规则说明 =
|
||||
# scripts/check_bsp_err.py 文件头。注:ARMCC v5 对 warn_unused_result 不生效(实测),
|
||||
# 所以"丢弃返回值必须写 (void)"这条只能靠书写约定与评审兜着。
|
||||
# 恒扫全库(要按 git 清单建公开头索引),故 pass_filenames: false。
|
||||
- id: bsp-err
|
||||
name: BSP 错误码与超时命名约定
|
||||
entry: python scripts/check_bsp_err.py
|
||||
language: system
|
||||
files: ^Drivers/BSP/|^scripts/check_bsp_err\.py$|^\.pre-commit-config\.yaml$
|
||||
pass_filenames: false
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
## 提交门禁(pre-commit)
|
||||
|
||||
- 任一失败即拦截,共 **38 个钩子**(逐个详解 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 16 个(`-unit-test` 后缀)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,N-23/U4)+ `mem-budget`(RAM 92% 预算门禁,N-27)+ `font-charset`(UI 字符集差集检查,防缺字白框,U4)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 11 页黄金参考,U4)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 6 条基线只减不增,报告 S-1)
|
||||
- 任一失败即拦截,共 **39 个钩子**(逐个详解 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 16 个(`-unit-test` 后缀)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,N-23/U4)+ `mem-budget`(RAM 92% 预算门禁,N-27)+ `font-charset`(UI 字符集差集检查,防缺字白框,U4)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 11 页黄金参考,U4)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 6 条基线只减不增,报告 S-1)+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」;约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 清点 19 条 → **已还 2 条、现 17 条**),报告 S-5)
|
||||
- **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5)
|
||||
- ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关
|
||||
- **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/`
|
||||
@@ -173,7 +173,7 @@
|
||||
| ADC 采样节拍 / 通道映射 / V0.1 硬件缺陷 | `docs/子系统设计/ADC采样节拍设计说明.md`、`docs/硬件平台/ADC通道映射说明.md` |
|
||||
| 存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) | `docs/架构设计/STM32F4-Base存储架构说明.md` |
|
||||
| RAM / 栈预算 / 容量台账 | `docs/内存管理/RAM使用分析与优化方案.md`、`内存内容分配策略.md` |
|
||||
| pre-commit 38 个钩子逐个详解 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| pre-commit 39 个钩子逐个详解 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| 实体编号怎么写 / 前缀总表 / `id-check` 八项校验 | `docs/编号规范.md`(§4 总表、§8 门禁与实测基线) |
|
||||
| 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` |
|
||||
| 自动化测试 / 实机测试 / 板级套件 | `docs/测试说明/自动化测试流程.md`、`实机测试实施指南.md`、`board-test-suite` skill |
|
||||
|
||||
+8
-6
@@ -181,9 +181,9 @@ int app_main_init(void) {
|
||||
* 带错误码的结果回执,使 I2C 不通时能看到"在哪一步、什么错"。
|
||||
*/
|
||||
{
|
||||
int rtc_ret = sd2506_init();
|
||||
bsp_err_t rtc_ret = sd2506_init();
|
||||
DBG_INFO("SD2506 init: %s (ret=%d)",
|
||||
(rtc_ret == SD2506_OK) ? "OK" : "FAILED", rtc_ret);
|
||||
(rtc_ret == BSP_OK) ? "OK" : "FAILED", rtc_ret);
|
||||
}
|
||||
if (sys_clock_init() != 0) {
|
||||
syslog_post(SYSLOG_SELFTEST, "clock chip fault");
|
||||
@@ -198,7 +198,7 @@ int app_main_init(void) {
|
||||
DBG_INFO("NAND init: %s",
|
||||
(ret == GD5F_OK) ? "OK" :
|
||||
(ret == GD5F_ERROR) ? "ERROR" :
|
||||
(ret == GD5F_BUSY_TIMEOUT) ? "BUSY TIMEOUT" :
|
||||
(ret == GD5F_WAIT_EXPIRED) ? "BUSY TIMEOUT" :
|
||||
(ret == GD5F_ECC_ERROR) ? "ECC ERROR" :
|
||||
(ret == GD5F_PROGRAM_FAIL) ? "PROGRAM FAIL" :
|
||||
(ret == GD5F_ERASE_FAIL) ? "ERASE FAIL" :
|
||||
@@ -208,10 +208,12 @@ int app_main_init(void) {
|
||||
}
|
||||
|
||||
/* 4) TPAFE5160 ADC 测试 */
|
||||
int tp_ret = tpafe5160_init();
|
||||
bsp_err_t tp_ret = tpafe5160_init();
|
||||
DBG_INFO("ADC init: %s",
|
||||
(tp_ret == TPAFE5160_OK) ? "OK" : "TIMEOUT");
|
||||
if (tp_ret != TPAFE5160_OK) {
|
||||
(tp_ret == BSP_OK) ? "OK" :
|
||||
(tp_ret == BSP_ERR_TIMEOUT) ? "TIMEOUT" :
|
||||
(tp_ret == BSP_ERR_NO_DEVICE) ? "NO CHIP" : "FAIL");
|
||||
if (tp_ret != BSP_OK) {
|
||||
syslog_post(SYSLOG_SELFTEST, "ADC fault");
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ int sys_clock_init(void) {
|
||||
* 驱动内部已 +2000)。此处【禁止】再 +2000,否则年份虚增到 4026,
|
||||
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。
|
||||
* 返回值必须检查:I2C 失败时字段为垃圾,不得用于建立基准。 */
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == SD2506_OK) ? 1 : 0;
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == BSP_OK) ? 1 : 0;
|
||||
|
||||
/* ============================================================
|
||||
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
|
||||
@@ -235,11 +235,11 @@ int sys_clock_init(void) {
|
||||
init_t.week = rtc_calc_week(init_t.year, init_t.month, init_t.day);
|
||||
|
||||
/* 写回 SD2506:断电后该时间被保留,下次上电即为有效值 */
|
||||
if (sd2506_set_time(&init_t) == SD2506_OK) {
|
||||
if (sd2506_set_time(&init_t) == BSP_OK) {
|
||||
DBG_INFO("sys_clock_init: RTC set %04d-%02d-%02d %02d:%02d:%02d",
|
||||
init_t.year, init_t.month, init_t.day,
|
||||
init_t.hour, init_t.minute, init_t.second);
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == SD2506_OK) ? 1 : 0; /* 重新读取校准后的值(同样检查返回值) */
|
||||
rtc_ok = (sd2506_get_time(&sd_time) == BSP_OK) ? 1 : 0; /* 重新读取校准后的值(同样检查返回值) */
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_init: RTC default write FAIL");
|
||||
}
|
||||
@@ -460,7 +460,7 @@ int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time) {
|
||||
|
||||
/* Step 2: 写回 SD2506 RTC(若提供了 sd_time) */
|
||||
if (sd_time != NULL) {
|
||||
if (sd2506_set_time(sd_time) == SD2506_OK) {
|
||||
if (sd2506_set_time(sd_time) == BSP_OK) {
|
||||
DBG_INFO("sys_clock_set: SD2506 RTC written");
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_set: SD2506 RTC write FAIL");
|
||||
|
||||
@@ -170,7 +170,7 @@ int gd5f_dma_buf_ok(const void *p_buf, size_t size) {
|
||||
/*
|
||||
* 函数功能:等待芯片操作完成(轮询 OIP 位)
|
||||
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
||||
* 返回值:GD5F_OK(0) - 操作完成;GD5F_BUSY_TIMEOUT(-2) - 超时(可重试);
|
||||
* 返回值:GD5F_OK(0) - 操作完成;GD5F_WAIT_EXPIRED(-2) - 超时(可重试);
|
||||
* GD5F_ERROR(-1) - SPI 传输真故障(不可重试,须上报自检)
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时。
|
||||
@@ -179,7 +179,7 @@ int gd5f_dma_buf_ok(const void *p_buf, size_t size) {
|
||||
* 立即成立 → **把"操作完成"伪造出来**,后续命令在未就绪的芯片上执行,
|
||||
* 最终表现为"静默报成功但数据没落盘"。现区分两类失败:
|
||||
* ① SPI 层失败(HAL≠OK)→ GD5F_ERROR(真故障);
|
||||
* ② OIP 长时间不落 → GD5F_BUSY_TIMEOUT(可重试)。
|
||||
* ② OIP 长时间不落 → GD5F_WAIT_EXPIRED(可重试)。
|
||||
* 调用点据此分级:超时重试 / 真故障上报自检并降级(见审计 §5 第二档 #1)。
|
||||
*/
|
||||
/* 看门狗心跳弱钩子:默认空实现(无看门狗/模拟器构建);强符号由
|
||||
@@ -225,7 +225,7 @@ int32_t gd5f_wait_busy(uint32_t timeout_ms) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
return GD5F_WAIT_EXPIRED;
|
||||
}
|
||||
if ((uint32_t)(HAL_GetTick() - last_beat) >= 100u) {
|
||||
last_beat = HAL_GetTick();
|
||||
@@ -355,7 +355,7 @@ int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||
HAL_SPI_DMAStop(&hspi1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
return GD5F_WAIT_EXPIRED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -437,7 +437,7 @@ int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
|
||||
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||
HAL_SPI_DMAStop(&hspi1);
|
||||
GD5F_CS_HIGH();
|
||||
return GD5F_BUSY_TIMEOUT;
|
||||
return GD5F_WAIT_EXPIRED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -116,7 +116,7 @@ typedef char gd5f_dma_threshold_contract_check_t[
|
||||
/* 返回值定义 */
|
||||
#define GD5F_OK 0
|
||||
#define GD5F_ERROR -1
|
||||
#define GD5F_BUSY_TIMEOUT -2
|
||||
#define GD5F_WAIT_EXPIRED -2 /* 旧名 GD5F_BUSY_TIMEOUT;S-5 R2:**返回码不许叫 *_TIMEOUT** */
|
||||
#define GD5F_ECC_ERROR -3
|
||||
#define GD5F_PROGRAM_FAIL -4
|
||||
#define GD5F_ERASE_FAIL -5
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "diskio.h"
|
||||
#include "nand.h"
|
||||
#include "map.h"
|
||||
#include "bsp_err.h" /* C2:把 dhara 错误归类成跨模块可判定的失败类 */
|
||||
|
||||
/* FTL 分区起始地址(块 0,偏移 0,占用全部 256MB) */
|
||||
#define FTL_FATFS_OFFSET 0U
|
||||
@@ -126,39 +127,95 @@ static uint8_t s_initialized;
|
||||
|
||||
/* ======================== 私有函数声明 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void);
|
||||
static int ftl_read_page(dhara_sector_t lpn);
|
||||
static bsp_err_t ftl_flush_cache(void);
|
||||
static bsp_err_t ftl_read_page(dhara_sector_t lpn);
|
||||
|
||||
/* SPI 原语从 gd5f2gq5ue.h 获取 */
|
||||
|
||||
/* ======================== dhara 错误的可读名与失败类 ======================== */
|
||||
|
||||
/*
|
||||
* 函数功能:把 dhara 错误枚举翻成短字符串(**本仓 dhara 没编 error.c,没有 dhara_strerror()**,
|
||||
* 用它会链接失败,故自己给一张小表)
|
||||
* 入口参数:err - dhara 错误枚举值
|
||||
* 返回值:静态字符串常量("NONE" / "ECC" / "MAP_FULL" ...,越界返回 "OOB")
|
||||
* 限定条件:只用于日志,不得用于判等
|
||||
* 函数说明:以前这里只打 `err=%d`——现场拿到一串数字还得翻 Lib/dhara/error.h 才知道是什么,
|
||||
* 存储故障又大多是"看完日志就没机会复现"的那类,可读名是排障成本最低的改法。
|
||||
*/
|
||||
static const char *ftl_err_name(dhara_error_t err) {
|
||||
static const char *const s_names[] = {
|
||||
"NONE", "BAD_BLOCK", "ECC", "TOO_BAD", "RECOVER",
|
||||
"JOURNAL_FULL", "NOT_FOUND", "MAP_FULL", "CORRUPT_MAP",
|
||||
};
|
||||
|
||||
if ((unsigned int)err < (sizeof(s_names) / sizeof(s_names[0]))) {
|
||||
return s_names[err];
|
||||
}
|
||||
return "OOB";
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把 dhara 错误归类成统一失败类(C2,报告 S-5)
|
||||
* 入口参数:err - dhara 错误枚举值
|
||||
* 返回值:BSP_ERR_CRC(位翻转不可纠 / 页太坏);BSP_ERR_NO_SPACE(journal 或映射层预算
|
||||
* 耗尽);其余(坏块 / 找回失败 / 映射损坏 / 越界)→ BSP_ERR_IO
|
||||
* 限定条件:入参应是**失败**的 err(DHARA_E_NONE 会得 BSP_OK)
|
||||
* 函数说明:分这三类的理由是它们在**处置上真的不同**,不是为了码好看:
|
||||
* · ECC/TOO_BAD → 该重试读或标页坏,**不是**卷的问题;
|
||||
* · JOURNAL_FULL/MAP_FULL → 介质还健康,是**块池/日志预算见底**,此时重格式化只是
|
||||
* 把问题推回去(E-31 的教训正是"别把非空白错误当新卷清 map"),该走容量恢复流程;
|
||||
* · BAD_BLOCK/CORRUPT_MAP/RECOVER → 映射或介质完整性故障,该保卷 + 上报,不许自愈重格式化。
|
||||
* 本函数先把这三类**打出来**(就地日志,最早且唯一同时知道 LPN 与原始码的位置);
|
||||
* 要让 defaultTask 的卷健康探针按类分支,还需要一个跨调用存活的快照(S-5 的 C4 待还)。
|
||||
*/
|
||||
static bsp_err_t ftl_class_from_dhara(dhara_error_t err) {
|
||||
switch (err) {
|
||||
case DHARA_E_NONE:
|
||||
return BSP_OK;
|
||||
case DHARA_E_ECC:
|
||||
case DHARA_E_TOO_BAD:
|
||||
return BSP_ERR_CRC;
|
||||
case DHARA_E_JOURNAL_FULL:
|
||||
case DHARA_E_MAP_FULL:
|
||||
return BSP_ERR_NO_SPACE;
|
||||
default:
|
||||
return BSP_ERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== 页面缓存管理 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void) {
|
||||
static bsp_err_t ftl_flush_cache(void) {
|
||||
if (!s_cache_dirty) {
|
||||
return GD5F_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
dhara_error_t err;
|
||||
if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err);
|
||||
return GD5F_ERROR;
|
||||
bsp_err_t cls = ftl_class_from_dhara(err); /* 只在失败分支需要(cppcheck variableScope) */
|
||||
DBG_ERROR("Cache flush FAIL: LPN=%lu dhara=%s class=%d",
|
||||
s_cached_lpn, ftl_err_name(err), cls);
|
||||
return cls;
|
||||
}
|
||||
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
static int ftl_read_page(dhara_sector_t lpn) {
|
||||
static bsp_err_t ftl_read_page(dhara_sector_t lpn) {
|
||||
dhara_error_t err;
|
||||
|
||||
if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err);
|
||||
return GD5F_ERROR;
|
||||
bsp_err_t cls = ftl_class_from_dhara(err); /* 只在失败分支需要(cppcheck variableScope) */
|
||||
DBG_ERROR("Page read FAIL: LPN=%lu dhara=%s class=%d",
|
||||
lpn, ftl_err_name(err), cls);
|
||||
return cls;
|
||||
}
|
||||
|
||||
s_cached_lpn = lpn;
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/* ======================== dhara NAND HAL ======================== */
|
||||
@@ -380,7 +437,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
|
||||
* 显式格式化后);失败的 resume 已把 journal 复位为空态 →
|
||||
* 按新卷处理,由上层自动建卷(此处 dhara_map_clear 为空操作:
|
||||
* resume 失败时 count 已清零,map 本已为空)。 */
|
||||
DBG_INFO("No valid map found (err=%d), creating fresh", err);
|
||||
DBG_INFO("No valid map found (dhara=%s), creating fresh", ftl_err_name(err));
|
||||
dhara_map_clear(&s_map);
|
||||
} else {
|
||||
/* E-31(2026-09-21 审计 P0 修复):非"空白"错误(ECC /
|
||||
@@ -391,8 +448,9 @@ DSTATUS disk_initialize(BYTE pdrv) {
|
||||
* defaultTask 自检上报 "filesystem fault" 并保留卷;瞬态故障
|
||||
* 由主循环重试/下次上电重新 resume;重格式化只走显式命令
|
||||
* (nand_ftl_format)。 */
|
||||
DBG_ERROR("Map resume failed (err=%d), volume kept, "
|
||||
"storage unavailable", err);
|
||||
DBG_ERROR("Map resume failed (dhara=%s class=%d), volume kept, "
|
||||
"storage unavailable", ftl_err_name(err),
|
||||
ftl_class_from_dhara(err));
|
||||
return STA_NOINIT;
|
||||
}
|
||||
} else {
|
||||
@@ -461,10 +519,10 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
if (ftl_flush_cache() != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
if (ftl_read_page(lpn) != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -496,10 +554,10 @@ DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
if (ftl_flush_cache() != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
if (ftl_read_page(lpn) != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -508,7 +566,7 @@ DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||
s_cache_dirty = 1;
|
||||
|
||||
if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
if (ftl_flush_cache() != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -531,7 +589,7 @@ DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
|
||||
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
if (ftl_flush_cache() != BSP_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
{
|
||||
@@ -574,7 +632,7 @@ int nand_ftl_format(void) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
if (ftl_flush_cache() != BSP_OK) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
|
||||
+42
-12
@@ -16,6 +16,27 @@
|
||||
|
||||
#include "rs485.h"
|
||||
|
||||
/*
|
||||
* 函数功能:把 HAL 的状态码映射成项目统一的 bsp_err_t(边界映射,2026-10-22 试点)
|
||||
* 入口参数:status - HAL_* 返回的状态
|
||||
* 返回值:对应的 bsp_err_t;HAL_BUSY 归为 BSP_ERR_BUSY(可重试),HAL_OK 归 BSP_OK
|
||||
* 限定条件:仅本文件内部使用
|
||||
* 函数说明:HAL 的码**不在本模块之外出现**(见 bsp_err.h 边界纪律 1/2);所以映射写在这里、
|
||||
* 写成 static,而不是往公共头里塞一个 include HAL 的 inline。
|
||||
*/
|
||||
static bsp_err_t rs485_err_from_hal(HAL_StatusTypeDef status) {
|
||||
switch (status) {
|
||||
case HAL_OK:
|
||||
return BSP_OK;
|
||||
case HAL_TIMEOUT:
|
||||
return BSP_ERR_TIMEOUT;
|
||||
case HAL_BUSY:
|
||||
return BSP_ERR_BUSY;
|
||||
default:
|
||||
return BSP_ERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== 全局实例 ======================== */
|
||||
|
||||
/* RS-485 全局实例(接收缓冲内聚于句柄的 rx_buf 字段) */
|
||||
@@ -60,19 +81,20 @@ void rs485_init(rs485_handle_t *p_handle,
|
||||
* p_data - 待发送数据缓冲区 uint8_t*(非空)
|
||||
* len - 待发送字节数 uint16_t 1 - N
|
||||
* timeout - 发送超时(ms),传 0 使用 RS485_TIMEOUT_DEFAULT uint32_t 0 / 1 - N
|
||||
* 返回值:HAL_OK - 发送成功 HAL_StatusTypeDef
|
||||
* HAL_TIMEOUT - 发送超时
|
||||
* HAL_ERROR - 发送错误
|
||||
* 返回值:BSP_OK - 发送成功 bsp_err_t
|
||||
* BSP_ERR_PARAM - 参数非法(空指针、长度 0 或超 RS485_TX_MAX_LEN)
|
||||
* BSP_ERR_TIMEOUT - 发送超时(可重试:BSP_ERR_CAN_RETRY 为真)
|
||||
* BSP_ERR_IO - HAL 报总线/状态错误
|
||||
* 限定条件:须在主循环或 FreeRTOS 任务中调用,不可在 UART 中断中调用
|
||||
* 函数说明:发送前拉高 DE,等待 HAL 时基延时确保 RS485 收发器驱动使能稳定后再启动
|
||||
* UART 发送(HAL 时基最小粒度 1ms,此处延时 1ms,远大于 50us 的 DE 建立时间)。
|
||||
* HAL_UART_Transmit 内部等待 TC(发送完成)标志,确保最后一字节完全移出后
|
||||
* 再拉低 DE 切回接收状态。
|
||||
*/
|
||||
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
const uint8_t *p_data,
|
||||
uint16_t len,
|
||||
uint32_t timeout) {
|
||||
bsp_err_t rs485_transmit(rs485_handle_t *p_handle,
|
||||
const uint8_t *p_data,
|
||||
uint16_t len,
|
||||
uint32_t timeout) {
|
||||
HAL_StatusTypeDef status = HAL_OK;
|
||||
|
||||
if (timeout == 0U) {
|
||||
@@ -81,8 +103,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
|
||||
/* 防御性边界(2026-09-24,UART 缓冲遍历类 HardFault 加固):
|
||||
* 长度必须非零且不超过收发缓冲上限——脏长度交给 HAL 会变成"按长度遍历整片内存" */
|
||||
if ((p_data == NULL) || (len == 0U) || (len > RS485_TX_MAX_LEN)) {
|
||||
return HAL_ERROR;
|
||||
if ((p_handle == NULL) || (p_data == NULL) || (len == 0U) || (len > RS485_TX_MAX_LEN)) {
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
RS485_DIR_TX(p_handle);
|
||||
@@ -97,7 +119,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
|
||||
RS485_DIR_RX(p_handle);
|
||||
|
||||
return status;
|
||||
|
||||
return rs485_err_from_hal(status);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,9 +135,16 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
|
||||
* 用户须在回调中重新调用本函数重新开启接收。
|
||||
*/
|
||||
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle) {
|
||||
bsp_err_t rs485_receive_start(rs485_handle_t *p_handle) {
|
||||
HAL_StatusTypeDef status;
|
||||
|
||||
if (p_handle == NULL) {
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
p_handle->rx_size = 0;
|
||||
return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_handle->rx_buf, RS485_RX_BUF_SIZE);
|
||||
status = HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_handle->rx_buf, RS485_RX_BUF_SIZE);
|
||||
|
||||
return rs485_err_from_hal(status);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -19,6 +19,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include "bsp_err.h" /* bsp_err_t / BSP_WAIT_*:对外只报统一错误码(2026-10-22 试点) */
|
||||
|
||||
/*
|
||||
* 常量定义
|
||||
@@ -65,12 +66,14 @@ void rs485_init(rs485_handle_t *p_handle,
|
||||
* data - 待发送数据缓冲区
|
||||
* len - 待发送字节数
|
||||
* timeout - 发送超时(ms),传 0 使用 RS485_TIMEOUT_DEFAULT
|
||||
* 返回值:HAL_OK / HAL_TIMEOUT / HAL_ERROR
|
||||
* 限定条件:在主循环或任务中调用,不可在 UART 中断中调用
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_PARAM - 参数非法;BSP_ERR_TIMEOUT - 发送超时(可重试,
|
||||
* 见 BSP_ERR_CAN_RETRY);BSP_ERR_IO - 总线错误
|
||||
* 限定条件:在主循环或任务中调用,不可在 UART 中断中调用;timeout 单位是毫秒,
|
||||
* 传 0 用 RS485_TIMEOUT_DEFAULT(不许拿 0xFFFFFFFF 当"无限",用 BSP_WAIT_FOREVER)
|
||||
* 函数说明:发送前自动拉高 DE,发送完成后等待 TC 标志再拉低 DE,
|
||||
* 确保最后一字节完全移出后再切换到接收状态
|
||||
*/
|
||||
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
bsp_err_t rs485_transmit(rs485_handle_t *p_handle,
|
||||
const uint8_t *p_data,
|
||||
uint16_t len,
|
||||
uint32_t timeout);
|
||||
@@ -78,7 +81,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
/*
|
||||
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
|
||||
* 入口参数:handle - RS-485 句柄指针
|
||||
* 返回值:HAL_OK / HAL_ERROR
|
||||
* 返回值:BSP_OK - 已启动;BSP_ERR_PARAM - 句柄空;BSP_ERR_BUSY - 接收通道正被占用
|
||||
* BSP_ERR_STATE - 接收通道不处于可启动状态
|
||||
* 限定条件:UART 须已开启全局中断,NVIC 中须使能对应 UART 中断
|
||||
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
|
||||
* 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
|
||||
@@ -86,7 +90,7 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
|
||||
* 用户须在回调中重新调用本函数重新开启接收。
|
||||
*/
|
||||
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle);
|
||||
bsp_err_t rs485_receive_start(rs485_handle_t *p_handle);
|
||||
|
||||
/*
|
||||
* 函数功能:将 RS-485 句柄与 HAL 回调关联
|
||||
|
||||
+150
-115
@@ -37,41 +37,69 @@ uint8_t sd2506_dec_to_bcd(uint8_t dec) {
|
||||
return ((dec / 10) << 4) | (dec % 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把 HAL 的 I2C 状态映射成统一错误码(S-5,2026-10-23)
|
||||
* 入口参数:status - HAL_I2C_* 返回的状态
|
||||
* 返回值:HAL_OK → BSP_OK;HAL_TIMEOUT → BSP_ERR_TIMEOUT(可重试);其余 → BSP_ERR_IO
|
||||
* 限定条件:仅本文件内部使用
|
||||
* 函数说明:**这一映射是本次迁移的实质收益**——旧码 SD2506_I2C_ERROR 把"总线没人应答"
|
||||
* 和"时钟拉长/超时"合成同一个 -2,调用方想重试也不知道该不该重试;SD2506 又是启动链
|
||||
* 第一站(I2C1 上电时序敏感),归出 BSP_ERR_TIMEOUT 之后,上层才谈得上"退避再读一次"。
|
||||
*/
|
||||
static bsp_err_t sd2506_err_from_hal(HAL_StatusTypeDef status) {
|
||||
switch (status) {
|
||||
case HAL_OK:
|
||||
return BSP_OK;
|
||||
case HAL_TIMEOUT:
|
||||
return BSP_ERR_TIMEOUT;
|
||||
case HAL_BUSY:
|
||||
return BSP_ERR_BUSY;
|
||||
default:
|
||||
return BSP_ERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写单字节寄存器
|
||||
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||||
* val - 写入数据 uint8_t 0x00 - 0xFF
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:I2C 外设已初始化
|
||||
* 函数说明:通过 I2C 向指定地址写入 1 字节数据
|
||||
* 限定条件:I2C 外设已初始化
|
||||
* 函数说明:通过 I2C 向指定地址写入 1 字节
|
||||
*/
|
||||
static int sd2506_write_reg(uint8_t reg, uint8_t val) {
|
||||
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, &val, 1,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
static bsp_err_t sd2506_write_reg(uint8_t reg, uint8_t val) {
|
||||
HAL_StatusTypeDef status;
|
||||
|
||||
status = HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, &val, 1,
|
||||
SD2506_I2C_TIMEOUT_MS);
|
||||
if (status != HAL_OK) {
|
||||
return sd2506_err_from_hal(status);
|
||||
}
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读单字节寄存器
|
||||
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||||
* p_val - 读取值指针 uint8_t*(非空)
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:I2C 外设已初始化,p_val 为非空指针
|
||||
* 函数说明:通过 I2C 从指定地址读取 1 字节数据
|
||||
* 限定条件:I2C 外设已初始化,val 为非空指针函数说明:通过 I2C 从指定地址读取 1 字节
|
||||
*/
|
||||
static int sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
|
||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, p_val, 1,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
static bsp_err_t sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
|
||||
HAL_StatusTypeDef status;
|
||||
|
||||
status = HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, p_val, 1,
|
||||
SD2506_I2C_TIMEOUT_MS);
|
||||
if (status != HAL_OK) {
|
||||
return sd2506_err_from_hal(status);
|
||||
}
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -79,18 +107,21 @@ static int sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
|
||||
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||||
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||||
* len - 数据长度 uint8_t 1 - N
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||||
* 函数说明:从 reg 开始连续写入 len 字节数据
|
||||
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||||
*/
|
||||
static int sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
|
||||
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
static bsp_err_t sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
|
||||
HAL_StatusTypeDef status;
|
||||
|
||||
status = HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
|
||||
SD2506_I2C_TIMEOUT_MS);
|
||||
if (status != HAL_OK) {
|
||||
return sd2506_err_from_hal(status);
|
||||
}
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,59 +129,62 @@ static int sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
|
||||
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||||
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||||
* len - 数据长度 uint8_t 1 - N
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||||
* 函数说明:从 reg 开始连续读取 len 字节数据
|
||||
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||||
*/
|
||||
static int sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
|
||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, p_data, len,
|
||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||
return SD2506_I2C_ERROR;
|
||||
static bsp_err_t sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
|
||||
HAL_StatusTypeDef status;
|
||||
|
||||
status = HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||
I2C_MEMADD_SIZE_8BIT, p_data, len,
|
||||
SD2506_I2C_TIMEOUT_MS);
|
||||
if (status != HAL_OK) {
|
||||
return sd2506_err_from_hal(status);
|
||||
}
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:开启写保护(允许写入 00H~71H 寄存器)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功 2 - I2C 错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO / BSP_ERR_TIMEOUT - I2C 失败
|
||||
* 限定条件:I2C 外设已初始化
|
||||
* 函数说明:顺序:先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
|
||||
*/
|
||||
static int sd2506_write_enable(void) {
|
||||
int ret = 0;
|
||||
static bsp_err_t sd2506_write_enable(void) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
|
||||
/* 写允许顺序(SD2506/SD24xx 手册要求):先置 WRTC1(CTR2 bit7)=1,
|
||||
* 再置 WRTC2(CTR1 bit2) + WRTC3(CTR1 bit7)=1。顺序反了芯片不会解开写保护。 */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x80U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:关闭写保护(禁止写入 00H~71H 寄存器)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功 2 - I2C 错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO / BSP_ERR_TIMEOUT - I2C 失败
|
||||
* 限定条件:I2C 外设已初始化
|
||||
* 函数说明:顺序:先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
|
||||
*/
|
||||
static int sd2506_write_disable(void) {
|
||||
int ret = 0;
|
||||
static bsp_err_t sd2506_write_disable(void) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
|
||||
/* 写禁止顺序(手册要求):先清 WRTC2/WRTC3(CTR1),再清 WRTC1(CTR2) */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x00U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/* ======================== 公共 API 实现 ======================== */
|
||||
@@ -165,13 +199,13 @@ static int sd2506_write_disable(void) {
|
||||
* 窗口重置并回读验证(手册 §4.8:强烈建议每次上电重置 18H)
|
||||
* 3. 上电即写禁止态(WRTC 全 0),无需再写 0FH
|
||||
*/
|
||||
int sd2506_init(void) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_init(void) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t a_id[8] = {0};
|
||||
|
||||
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
||||
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
/* 启动段可观测性(2026-10-02 S10):本函数原先所有失败路径均静默返回,
|
||||
* 而它是启动死机最可疑的源头(I2C1 硬件不通即卡此步且无任何输出)。
|
||||
* 日志字符串必须纯 ASCII(文件无 BOM,ARMCC 按 CP936 解码)。 */
|
||||
@@ -186,22 +220,22 @@ int sd2506_init(void) {
|
||||
uint8_t charge_val = 0;
|
||||
|
||||
ret = sd2506_read_reg(SD2506_REG_CHARGE, &charge_val);
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("sd2506_init: charge reg read FAIL (err=%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (charge_val != SD2506_CHARGE_INIT_VALUE) {
|
||||
int ret_read = 0;
|
||||
bsp_err_t ret_read = BSP_OK;
|
||||
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("sd2506_init: write-enable FAIL (err=%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sd2506_write_reg(SD2506_REG_CHARGE, SD2506_CHARGE_INIT_VALUE);
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
(void)sd2506_write_disable();
|
||||
DBG_ERROR("sd2506_init: charge reg write FAIL (err=%d)", ret);
|
||||
return ret;
|
||||
@@ -209,15 +243,16 @@ int sd2506_init(void) {
|
||||
|
||||
ret_read = sd2506_read_reg(SD2506_REG_CHARGE, &charge_val);
|
||||
ret = sd2506_write_disable();
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("sd2506_init: write-disable FAIL (err=%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret_read != SD2506_OK || charge_val != SD2506_CHARGE_INIT_VALUE) {
|
||||
if (ret_read != BSP_OK || charge_val != SD2506_CHARGE_INIT_VALUE) {
|
||||
DBG_ERROR("sd2506_init: charge reg verify FAIL (read=%d val=0x%02X)",
|
||||
ret_read, charge_val);
|
||||
return SD2506_ERROR;
|
||||
/* 回读校验不符 = 通信/介质类故障,不是调用方的参数错(旧码把两者都写成 -1) */
|
||||
return BSP_ERR_IO;
|
||||
}
|
||||
|
||||
DBG_INFO("sd2506_init: charge reg 18H reset to 0x%02X (was drifted)",
|
||||
@@ -225,7 +260,7 @@ int sd2506_init(void) {
|
||||
}
|
||||
|
||||
DBG_INFO("sd2506_init: OK");
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -237,12 +272,12 @@ int sd2506_init(void) {
|
||||
* 2. 一次性写入 7 字节时间数据 (00H~06H)
|
||||
* 3. 关闭写保护
|
||||
*/
|
||||
int sd2506_set_time(const sd2506_time_t *p_time) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_set_time(const sd2506_time_t *p_time) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t buf[7] = {0};
|
||||
|
||||
if (p_time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* 组装 7 字节时间数据 (BCD 码) */
|
||||
@@ -256,40 +291,40 @@ int sd2506_set_time(const sd2506_time_t *p_time) {
|
||||
|
||||
/* 开启写保护 */
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 一次性写入 7 字节时间数据 (00H~06H)
|
||||
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进入 */
|
||||
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 关闭写保护 */
|
||||
ret = sd2506_write_disable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读取 RTC 时间日期
|
||||
* 入口参数:p_time - 输出时间结构体指针 sd2506_time_t*(非空)
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:一次读取 7 字节时间数据 (00H~06H),所有实时数据被锁存避免错误
|
||||
* note: sd2506_init() must be called first
|
||||
*/
|
||||
int sd2506_get_time(sd2506_time_t *p_time) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_get_time(sd2506_time_t *p_time) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t buf[7] = {0};
|
||||
|
||||
if (p_time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* 一次读取 7 字节时间数据 (00H~06H)
|
||||
* 手册说明: 读取时所有实时数据被锁存,避免错误 */
|
||||
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -301,28 +336,28 @@ int sd2506_get_time(sd2506_time_t *p_time) {
|
||||
p_time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
|
||||
p_time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读取内部温度
|
||||
* 入口参数:p_temp - 输出温度值指针 int8_t*(非空)
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:读取寄存器 0x16,bit7 为符号位
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
* note: reads register 0x16
|
||||
*/
|
||||
int sd2506_get_temperature(int8_t *p_temp) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_get_temperature(int8_t *p_temp) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t val = 0;
|
||||
|
||||
if (p_temp == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
ret = sd2506_read_reg(SD2506_REG_TEMP, &val);
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -335,54 +370,54 @@ int sd2506_get_temperature(int8_t *p_temp) {
|
||||
*p_temp = (int8_t)(val & 0x7FU);
|
||||
}
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读取电池电压(毫伏)
|
||||
* 入口参数:p_voltage - 输出电压指针 uint16_t*(非空)
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:组合 9 位值 (bit8=BAT8_VAL, bit7~0=BAT_VL),转换为毫伏
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
* note: combines 9-bit value
|
||||
*/
|
||||
int sd2506_get_battery_voltage(uint16_t *p_voltage) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_get_battery_voltage(uint16_t *p_voltage) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t val_high = 0;
|
||||
uint8_t val_low = 0;
|
||||
|
||||
if (p_voltage == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* 读取 1AH bit7 (BAT8_VAL) 与 1BH (BAT_VL) */
|
||||
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 组合 9 位值 bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
|
||||
* 例: 1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
|
||||
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
|
||||
*p_voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读取 8 字节芯片 ID
|
||||
* 入口参数:p_id - 输出缓冲区指针 uint8_t[8](非空)
|
||||
* 返回值:0-成功,2-I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:读取寄存器 0x72~0x79
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
* note: reads registers 0x72-0x79
|
||||
*/
|
||||
int sd2506_get_id(uint8_t p_id[8]) {
|
||||
bsp_err_t sd2506_get_id(uint8_t p_id[8]) {
|
||||
if (p_id == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
return sd2506_read_regs(SD2506_REG_ID_START, p_id, SD2506_ID_SIZE);
|
||||
@@ -396,16 +431,16 @@ int sd2506_get_id(uint8_t p_id[8]) {
|
||||
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||||
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||||
* 函数说明:SRAM 范围 0x2C~0x71
|
||||
* return: 0=OK, -1=param err, -2=I2C err
|
||||
* return: BSP_OK / BSP_ERR_PARAM / BSP_ERR_IO / BSP_ERR_TIMEOUT
|
||||
* note: SRAM range 0x2C-0x71
|
||||
*/
|
||||
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
|
||||
bsp_err_t sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
|
||||
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
if (addr + len > SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||||
@@ -419,25 +454,25 @@ int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
|
||||
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||||
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||||
* 函数说明:SRAM 写入需走写保护流程(虽 SRAM 本身无需)
|
||||
* return: 0=OK, -1=param err, -2=I2C err
|
||||
* return: BSP_OK / BSP_ERR_PARAM / BSP_ERR_IO / BSP_ERR_TIMEOUT
|
||||
*/
|
||||
int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len) {
|
||||
bsp_err_t sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len) {
|
||||
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
if (addr + len > SD2506_SRAM_SIZE) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 为 2CH~71H)
|
||||
* 但为安全起见, SRAM 写入也走写保护流程 */
|
||||
int ret = 0;
|
||||
bsp_err_t ret = BSP_OK;
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
ret = sd2506_write_disable();
|
||||
return ret;
|
||||
@@ -447,17 +482,17 @@ int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len) {
|
||||
* 函数功能:设置闹钟中断
|
||||
* 入口参数:p_time - 报警时间结构体指针 sd2506_time_t*(非空)
|
||||
* mask - 报警匹配掩码 uint8_t
|
||||
* 返回值:0-成功,-2=I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:写入 8 字节报警数据 (07H~0EH),使能 CTR2.INTAE+INTS0+IM
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
*/
|
||||
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
|
||||
int ret = 0;
|
||||
bsp_err_t sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
uint8_t buf[8] = {0};
|
||||
|
||||
if (p_time == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* 组装 8 字节报警数据 (07H~0EH) */
|
||||
@@ -472,42 +507,42 @@ int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
|
||||
|
||||
/* 开启写保护 */
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 写入报警寄存器 (07H~0EH) */
|
||||
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 使能报警中断: CTR2 中 INTAE=1, INTS1=0, INTS0=1, IM=1 (周期中断) */
|
||||
ret = sd2506_write_reg(SD2506_REG_CTR2,
|
||||
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
/* 关闭写保护 */
|
||||
ret = sd2506_write_disable();
|
||||
if (ret != SD2506_OK) return ret;
|
||||
if (ret != BSP_OK) return ret;
|
||||
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:清除闹钟中断标志
|
||||
* 入口参数:无
|
||||
* 返回值:0-成功,-2=I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* 函数说明:ARST 位于 CTR3(11H) bit7,本驱动从未写 CTR3 → ARST=0,
|
||||
* 故读 CTR1 不会自动清 INTAF(手册 L519/L523),须手动写 CTR1 将 INTAF 置 0;
|
||||
* 写 0FH 须在写允许窗口内且保持 WRTC2/WRTC3=1(手册 L521),
|
||||
* 故写入值取写允许态 0xFF 清 INTAF 位后的 0xDF。
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
*/
|
||||
int sd2506_clear_alarm(void) {
|
||||
int ret = 0;
|
||||
int ret_off = 0;
|
||||
bsp_err_t sd2506_clear_alarm(void) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
bsp_err_t ret_off = BSP_OK;
|
||||
|
||||
/* 写 0FH 须在写允许窗口内(手册 L519),且 WRTC2/WRTC3 必须保持 1(L521) */
|
||||
ret = sd2506_write_enable();
|
||||
if (ret != SD2506_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -515,7 +550,7 @@ int sd2506_clear_alarm(void) {
|
||||
(uint8_t)(SD2506_CTR1_WRITE_ON & (uint8_t)~SD2506_CTR1_INTAF));
|
||||
|
||||
ret_off = sd2506_write_disable();
|
||||
if (ret == SD2506_OK) {
|
||||
if (ret == BSP_OK) {
|
||||
ret = ret_off;
|
||||
}
|
||||
|
||||
@@ -525,13 +560,13 @@ int sd2506_clear_alarm(void) {
|
||||
/*
|
||||
* 函数功能:读取控制寄存器 1 (CTR1)
|
||||
* 入口参数:p_val - 输出值指针 uint8_t*(非空)
|
||||
* 返回值:0-成功,-2=I2C 错误 int
|
||||
* 返回值:BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT(可重试) bsp_err_t
|
||||
* 限定条件:sd2506_init() 已调用成功
|
||||
* return: 0=OK, -2=I2C error
|
||||
* return: BSP_OK / BSP_ERR_IO / BSP_ERR_TIMEOUT (retryable)
|
||||
*/
|
||||
int sd2506_read_ctr1(uint8_t *p_val) {
|
||||
bsp_err_t sd2506_read_ctr1(uint8_t *p_val) {
|
||||
if (p_val == NULL) {
|
||||
return SD2506_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
return sd2506_read_reg(SD2506_REG_CTR1, p_val);
|
||||
|
||||
+32
-26
@@ -136,11 +136,15 @@ extern "C" {
|
||||
* CTR2 需另行清除;ARST 在 CTR3 与本寄存器无关) */
|
||||
#define SD2506_CTR1_WRITE_OFF 0x7BU
|
||||
|
||||
/* ======================== 返回值定义 ======================== */
|
||||
/* ======================== 返回值定义 ========================
|
||||
* 本模块**不再自带错误码**(2026-10-23,S-5):统一走 Drivers/BSP/bsp_err.h。
|
||||
* 旧码映射 —— SD2506_OK → BSP_OK;SD2506_ERROR(参数类)→ BSP_ERR_PARAM;
|
||||
* SD2506_I2C_ERROR → BSP_ERR_IO,其中 I2C 超时刻意单独归成 **BSP_ERR_TIMEOUT
|
||||
* (可重试)**:旧码把"总线上没人应答"和"超时"混成一个 -2,调用方没法规避。
|
||||
* 三处旧宏已**删除而不是留别名**——留着就还能被新代码继续用,等于把迁移洗掉。
|
||||
*/
|
||||
|
||||
#define SD2506_OK 0
|
||||
#define SD2506_ERROR -1
|
||||
#define SD2506_I2C_ERROR -2
|
||||
#include "bsp_err.h"
|
||||
|
||||
/* ======================== 超时时间 ======================== */
|
||||
|
||||
@@ -173,118 +177,120 @@ typedef struct {
|
||||
/*
|
||||
* 函数功能:初始化 SD2506API-G RTC
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,-1 - 校验失败,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 失败或回读校验不符;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:CubeMX 已完成 I2C1 初始化
|
||||
* 函数说明:1. 读取芯片 ID 验证通信
|
||||
* 2. 充电寄存器 18H 回读校验(出厂 82H 非易失),值漂移时经写允许
|
||||
* 窗口重置并回读验证
|
||||
* 3. 上电即写禁止态(WRTC 全 0),无需再写 0FH
|
||||
*/
|
||||
int sd2506_init(void);
|
||||
bsp_err_t sd2506_init(void);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 RTC 时间日期
|
||||
* 入口参数:time - 时间结构体指针,包含要设置的时间
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:1. 先开写保护
|
||||
* 2. 一次性写入 7 字节时间数据 (00H~06H)
|
||||
* 3. 关闭写保护
|
||||
* 注意:不可单独写某一个时间寄存器
|
||||
*/
|
||||
int sd2506_set_time(const sd2506_time_t *p_time);
|
||||
bsp_err_t sd2506_set_time(const sd2506_time_t *p_time);
|
||||
|
||||
/*
|
||||
* 函数功能:读取 RTC 时间日期
|
||||
* 入口参数:time - 时间结构体指针,用于存储读取结果
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:1. 一次读取 7 字节时间数据 (00H~06H)
|
||||
* 2. BCD 转十进制
|
||||
* 3. 屏蔽小时 bit7 (12/24标志位)
|
||||
*/
|
||||
int sd2506_get_time(sd2506_time_t *p_time);
|
||||
bsp_err_t sd2506_get_time(sd2506_time_t *p_time);
|
||||
|
||||
/*
|
||||
* 函数功能:读取芯片内部温度
|
||||
* 入口参数:temp - 温度输出指针 (整数部分, 有符号)
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:读取 16H 寄存器,bit7 为符号位,范围 -40~+85
|
||||
*/
|
||||
int sd2506_get_temperature(int8_t *p_temp);
|
||||
bsp_err_t sd2506_get_temperature(int8_t *p_temp);
|
||||
|
||||
/*
|
||||
* 函数功能:读取电池电压 (毫伏)
|
||||
* 入口参数:voltage - 电压输出指针 (单位: mV)
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:1. 读取 1AH bit7 (BAT8_VAL) 和 1BH (BAT_VL)
|
||||
* 2. 组合 9 位数据得到电压值 (如 0x135 = 309 = 3.09V)
|
||||
*/
|
||||
int sd2506_get_battery_voltage(uint16_t *p_voltage);
|
||||
bsp_err_t sd2506_get_battery_voltage(uint16_t *p_voltage);
|
||||
|
||||
/*
|
||||
* 函数功能:读取芯片 8 字节 ID
|
||||
* 入口参数:id - 8 字节输出缓冲区
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:读取 72H~79H 共 8 字节唯一 ID
|
||||
*/
|
||||
int sd2506_get_id(uint8_t p_id[8]);
|
||||
bsp_err_t sd2506_get_id(uint8_t p_id[8]);
|
||||
|
||||
/*
|
||||
* 函数功能:读取用户 SRAM 数据
|
||||
* 入口参数:addr - SRAM 起始地址 (0~69)
|
||||
* buf - 数据输出缓冲区
|
||||
* len - 读取长度 (1~70)
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误,-1 - 参数错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_PARAM - 参数错误;BSP_ERR_IO - I2C 通信失败;
|
||||
* BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
||||
*/
|
||||
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len);
|
||||
bsp_err_t sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len);
|
||||
|
||||
/*
|
||||
* 函数功能:写入用户 SRAM 数据
|
||||
* 入口参数:addr - SRAM 起始地址 (0~69)
|
||||
* buf - 数据输入缓冲区
|
||||
* len - 写入长度 (1~70)
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误,-1 - 参数错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_PARAM - 参数错误;BSP_ERR_IO - I2C 通信失败;
|
||||
* BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
||||
* SRAM 无需开写保护即可写入
|
||||
*/
|
||||
int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len);
|
||||
bsp_err_t sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len);
|
||||
|
||||
/*
|
||||
* 函数功能:设置报警中断
|
||||
* 入口参数:time - 报警时间结构体
|
||||
* mask - 报警匹配掩码 (SD2506_AL_EN_xxx 位或组合)
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:1. 开写保护
|
||||
* 2. 写入报警寄存器 (07H~0EH)
|
||||
* 3. 使能报警中断 INTAE
|
||||
* 4. 关写保护
|
||||
*/
|
||||
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask);
|
||||
bsp_err_t sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask);
|
||||
|
||||
/*
|
||||
* 函数功能:清除报警中断标志 INTAF
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:ARST(CTR3 bit7) 未启用,读 CTR1 不清 INTAF;本函数经写允许窗口
|
||||
* 手动写 CTR1 将 INTAF 置 0
|
||||
*/
|
||||
int sd2506_clear_alarm(void);
|
||||
bsp_err_t sd2506_clear_alarm(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读取当前 0FH 控制寄存器1 状态
|
||||
* 入口参数:val - 输出值指针
|
||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_IO - I2C 通信失败;BSP_ERR_TIMEOUT - I2C 超时(可重试)
|
||||
*/
|
||||
int sd2506_read_ctr1(uint8_t *p_val);
|
||||
bsp_err_t sd2506_read_ctr1(uint8_t *p_val);
|
||||
|
||||
/*
|
||||
* 函数功能:BCD 码转十进制
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
- **周期重触发由 TIM3 更新事件(1 kHz)驱动**(2026-10-10):`app_main_init` 末尾 `HAL_TIM_Base_Start_IT(&htim3)` 启动节拍,`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback` 在 TIM3 分支内调 `tpafe5160_start_conv_irq()`(ISR 内仅两次 GPIO 写)。B1 阶段曾由 `adcTask` 每 1 ms 软触发(已改)。⚠ **节拍源必须是 TIM3 而非 TIM6**:TIM3 的同一个更新事件还经 **TRGO** 硬件触发 STM32 ADC1,两片转换器由此同源对齐;F4 上 TIM6/TIM7 的 TRGO 只接 DAC,喂不到 ADC1
|
||||
- **取数只用 `tpafe5160_snapshot(int16_t *dst, uint8_t count)`**(**仅任务上下文**,内部临界区原子拷贝;返回 1=新帧 / 0=无新帧 / -1=参数无效)。配套的 `tpafe5160_frame_seq()` 给帧序号、`tpafe5160_frstdata_err()` 给"FRSTDATA 无跳变"帧计数
|
||||
- ⚠️ **不要用缓冲区指针取数**:旧的 `get_buf()` / `data_ready()` / `clear_ready()` 已于 2026-10-10 **删除**(审计 E-45 / E-58)——`get_buf` 只给指针、任务再逐通道拷,中间被 BUSY EXTI 抢占就拷回**两帧混合**数据;`s_ready` 电平标志在 `start_conv_irq` 里被清 0,等于**用重触发去丢帧**。现由**帧序号**(只增不清)+ **临界区快照**取代
|
||||
- **`tpafe5160_init()` 做首帧有效性自检**(E-44):完整跑一帧转换+读取,若整帧读下来 FRSTDATA **没有发生任何跳变**(芯片没在驱动并行总线;板上上拉会把它钉死为高)→ 返回 `TPAFE5160_FRSTDATA_ERR`(-3) 并打 `[ERR] Init FRSTDATA no-toggle`。判据刻意不锁死"哪个 RD 边沿拉高/拉低"(手册只给"RD 下降沿 → FRSTDATA 高/低"两个跳变,本板未实测边沿次序),只要求"一帧内必须跳变" → 既抓死芯片,也不会因极性假设错误误杀好芯片
|
||||
- **`tpafe5160_init()` 做首帧有效性自检**(E-44):完整跑一帧转换+读取,若整帧读下来 FRSTDATA **没有发生任何跳变**(芯片没在驱动并行总线;板上上拉会把它钉死为高)→ 返回 `BSP_ERR_NO_DEVICE`(旧 `TPAFE5160_FRSTDATA_ERR(-3)`,2026-10-23 S-5 收编)并打 `[ERR] Init FRSTDATA no-toggle`。判据刻意不锁死"哪个 RD 边沿拉高/拉低"(手册只给"RD 下降沿 → FRSTDATA 高/低"两个跳变,本板未实测边沿次序),只要求"一帧内必须跳变" → 既抓死芯片,也不会因极性假设错误误杀好芯片
|
||||
- 采样数据的消费者 `App/task/adc_task.c` **已实现**(B1,2026-10-10:`tpafe5160_snapshot()` 原子取数 + 帧统计;`adcTask` 在 `Src/freertos.c` 的 `USER CODE` 区创建)。**v2.2(2026-10-10)**:采样节拍改由定时器更新中断硬件触发,本任务退化为纯消费者;**v2.8(2026-10-10)**:节拍源改 TIM3(同时经 TRGO 触发 ADC1 + DMA),STM32 侧改为消费 DMA 乒乓缓冲,单拍耗时 122 µs → **8 µs**
|
||||
- CONVST→BUSY→RD 三段时序对**建立/保持时间敏感**,改动需对照芯片手册
|
||||
- ⚠️ 本项目 ADC 相关功能**新增任务/缓冲前须核算 RAM**(见 `docs/内存管理/`);接消费者前的强制门禁见根 `AGENTS.md`「接 ADC / RS-485 消费者前的强制门禁」
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
/* ======================== 私有函数声明 ======================== */
|
||||
|
||||
static int tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
||||
static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
||||
|
||||
/* ======================== 公共函数定义 ======================== */
|
||||
|
||||
@@ -76,7 +76,7 @@ static int tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
||||
* 4. 采一帧并校验 FRSTDATA 有跳变(E-44:旧实现只看 BUSY,芯片死亡
|
||||
* 时照样报 Init OK,此后所有读数都是上拉/总线残值——静默死亡)
|
||||
*/
|
||||
int tpafe5160_init(void) {
|
||||
bsp_err_t tpafe5160_init(void) {
|
||||
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
|
||||
tpafe5160_set_os(TPAFE5160_OS_NONE);
|
||||
|
||||
@@ -86,7 +86,7 @@ int tpafe5160_init(void) {
|
||||
|
||||
/* 等待 BUSY 释放,确保上电后无残留转换 */
|
||||
int ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||
if (ret != TPAFE5160_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("Init wait BUSY timeout");
|
||||
return ret;
|
||||
}
|
||||
@@ -95,12 +95,12 @@ int tpafe5160_init(void) {
|
||||
int16_t probe[TPAFE5160_CH_NUM];
|
||||
tpafe5160_start_conv();
|
||||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||
if (ret != TPAFE5160_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("Init probe BUSY timeout");
|
||||
return ret;
|
||||
}
|
||||
if (tpafe5160_read_channels(TPAFE5160_CH_NUM, probe) != TPAFE5160_OK) {
|
||||
ret = TPAFE5160_FRSTDATA_ERR;
|
||||
if (tpafe5160_read_channels(TPAFE5160_CH_NUM, probe) != BSP_OK) {
|
||||
ret = BSP_ERR_NO_DEVICE;
|
||||
DBG_ERROR("Init FRSTDATA no-toggle (chip not driving DB bus)");
|
||||
return ret;
|
||||
}
|
||||
@@ -140,17 +140,17 @@ void tpafe5160_start_conv(void) {
|
||||
* 限定条件:已调用 tpafe5160_start_conv()
|
||||
* 函数说明:轮询 BUSY 引脚等待下降沿
|
||||
*/
|
||||
int tpafe5160_wait_busy(uint32_t timeout_ms) {
|
||||
bsp_err_t tpafe5160_wait_busy(uint32_t timeout_ms) {
|
||||
uint32_t tick_start = HAL_GetTick();
|
||||
|
||||
/* 等待 BUSY 释放(低电平表示空闲) */
|
||||
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
|
||||
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
|
||||
return TPAFE5160_BUSY_TIMEOUT;
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return TPAFE5160_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -173,11 +173,11 @@ uint8_t tpafe5160_is_busy(void) {
|
||||
* 2. 连续8次拉低RD读取各通道数据
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生跳变(E-44)
|
||||
*/
|
||||
int tpafe5160_read_all(int16_t *buf) {
|
||||
int ret = 0;
|
||||
bsp_err_t tpafe5160_read_all(int16_t *buf) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
|
||||
if (buf == NULL) {
|
||||
return TPAFE5160_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
/* 启动转换 */
|
||||
@@ -185,19 +185,19 @@ int tpafe5160_read_all(int16_t *buf) {
|
||||
|
||||
/* 等待转换完成 */
|
||||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||
if (ret != TPAFE5160_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("read_all BUSY timeout");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 连续读取8个通道(内含 FRSTDATA 有效性校验) */
|
||||
ret = tpafe5160_read_channels(TPAFE5160_CH_NUM, buf);
|
||||
if (ret != TPAFE5160_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
DBG_ERROR("read_all FRSTDATA no-toggle (data not trustworthy)");
|
||||
return TPAFE5160_FRSTDATA_ERR;
|
||||
return BSP_ERR_NO_DEVICE;
|
||||
}
|
||||
|
||||
return TPAFE5160_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -208,21 +208,21 @@ int tpafe5160_read_all(int16_t *buf) {
|
||||
* 限定条件:GPIO 已初始化
|
||||
* 函数说明:启动转换并等待完成后,连续读取至指定通道
|
||||
*/
|
||||
int tpafe5160_read_channel(uint8_t channel, int16_t *value) {
|
||||
int ret = 0;
|
||||
bsp_err_t tpafe5160_read_channel(uint8_t channel, int16_t *value) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
int16_t buf[TPAFE5160_CH_NUM] = {0};
|
||||
|
||||
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
|
||||
return TPAFE5160_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
ret = tpafe5160_read_all(buf);
|
||||
if (ret != TPAFE5160_OK) {
|
||||
if (ret != BSP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*value = buf[channel];
|
||||
return TPAFE5160_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -274,7 +274,7 @@ uint16_t tpafe5160_read_bus(void) {
|
||||
* 旧实现完全不看这根脚(宏 TPAFE5160_FRSTDATA_READ 有定义、代码零引用),
|
||||
* 却在注释里承诺"通过 FRSTDATA 验证第一通道",芯片死亡时返回 OK + 常量垃圾。
|
||||
*/
|
||||
static int tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||||
static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||||
uint8_t i;
|
||||
uint8_t lvl0 = (TPAFE5160_FRSTDATA_READ() == GPIO_PIN_SET) ? 1u : 0u;
|
||||
uint8_t toggled = 0;
|
||||
@@ -295,7 +295,7 @@ static int tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||||
}
|
||||
}
|
||||
|
||||
return toggled ? TPAFE5160_OK : TPAFE5160_FRSTDATA_ERR;
|
||||
return toggled ? BSP_OK : BSP_ERR_NO_DEVICE;
|
||||
}
|
||||
|
||||
/* ======================== 中断模式实现 ======================== */
|
||||
@@ -383,7 +383,7 @@ int tpafe5160_snapshot(int16_t *dst, uint8_t count) {
|
||||
uint32_t seq;
|
||||
|
||||
if (dst == NULL || count == 0u || count > TPAFE5160_CH_NUM) {
|
||||
return TPAFE5160_ERROR;
|
||||
return BSP_ERR_PARAM;
|
||||
}
|
||||
|
||||
seq = s_frame_seq;
|
||||
@@ -427,7 +427,7 @@ void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
|
||||
s_frame_seq++;
|
||||
|
||||
/* E-44:中断里不打日志,只累计;消费者据 tpafe5160_frstdata_err() 上报自检 */
|
||||
if (ret != TPAFE5160_OK && s_frstdata_err < 0xFFFFu) {
|
||||
if (ret != BSP_OK && s_frstdata_err < 0xFFFFu) {
|
||||
s_frstdata_err++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,16 @@ typedef enum {
|
||||
} tpafe5160_range_t;
|
||||
|
||||
/* 返回值定义 */
|
||||
#define TPAFE5160_OK 0
|
||||
#define TPAFE5160_ERROR -1
|
||||
#define TPAFE5160_BUSY_TIMEOUT -2
|
||||
/* 整帧读完 FRSTDATA 无跳变 = 芯片没在驱动数据线(死亡 / 未响应 RD),
|
||||
* 此时读回的是上拉/总线残值而非转换结果(E-44,见 read_channels 注释) */
|
||||
#define TPAFE5160_FRSTDATA_ERR -3
|
||||
/* 本模块的成败码已收编到 Drivers/BSP/bsp_err.h(2026-10-23,S-5 C1):
|
||||
* 旧 TPAFE5160_OK(0) → BSP_OK;旧 TPAFE5160_ERROR(-1) → BSP_ERR_PARAM(三处全是参数校验,
|
||||
* 数值也同为 -1,**行为零变化**);旧 TPAFE5160_BUSY_TIMEOUT(-2) → BSP_ERR_TIMEOUT,
|
||||
* 于是 BUSY 不释放终于变成跨模块可判定的"可重试";
|
||||
* 旧 TPAFE5160_FRSTDATA_ERR(-3) → BSP_ERR_NO_DEVICE——它就是 E-44 的"芯片没驱动 DB 总线",
|
||||
* 语义是**器件不在响应**,归到 NO_DEVICE 比留在私有 -3 更有用(且避开与 BSP_ERR_TIMEOUT 撞值)。
|
||||
* ⚠ 例外:**tpafe5160_snapshot() 不是成败 API**,它是 1=有新帧 / 0=无新帧 / -1=参数错 的
|
||||
* 三态**状态**返回(0 不是错误,是"这一拍没有新数据"),因此**不套 bsp_err_t**,
|
||||
* 只把参数错那一支写成 BSP_ERR_PARAM(同值)。强行收编会把"无新帧"变成失败。 */
|
||||
#include "bsp_err.h"
|
||||
|
||||
/* 默认超时时间 (ms) */
|
||||
#define TPAFE5160_CONV_TIMEOUT_MS 10
|
||||
@@ -84,13 +88,13 @@ typedef enum {
|
||||
/*
|
||||
* 函数功能:初始化 TPAFE5160(设置过采样、等待就绪)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,-2 - BUSY 超时
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_TIMEOUT - BUSY 不释放(可重试)
|
||||
* 限定条件:CubeMX 已完成 GPIO 初始化
|
||||
* 函数说明:1. 设置过采样为无过采样 (000)
|
||||
* 2. 确保 RD 为高、CONVST 为低
|
||||
* 3. 等待 BUSY 释放(转换空闲)
|
||||
*/
|
||||
int tpafe5160_init(void);
|
||||
bsp_err_t tpafe5160_init(void);
|
||||
|
||||
/*
|
||||
* 函数功能:设置过采样率
|
||||
@@ -113,11 +117,11 @@ void tpafe5160_start_conv(void);
|
||||
/*
|
||||
* 函数功能:等待转换完成
|
||||
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
||||
* 返回值:0 - 转换完成,-2 - 超时
|
||||
* 返回值:BSP_OK - 转换完成;BSP_ERR_TIMEOUT - 超时(可重试:BSP_ERR_CAN_RETRY)
|
||||
* 限定条件:已调用 tpafe5160_start_conv()
|
||||
* 函数说明:轮询 BUSY 引脚等待下降沿
|
||||
*/
|
||||
int tpafe5160_wait_busy(uint32_t timeout_ms);
|
||||
bsp_err_t tpafe5160_wait_busy(uint32_t timeout_ms);
|
||||
|
||||
/*
|
||||
* 函数功能:查询当前是否正在转换
|
||||
@@ -131,23 +135,24 @@ uint8_t tpafe5160_is_busy(void);
|
||||
/*
|
||||
* 函数功能:读取全部8通道转换结果
|
||||
* 入口参数:buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
|
||||
* 返回值:0 - 成功,-2 - BUSY 超时,-3 - FRSTDATA 无跳变(读回值不可信)
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_TIMEOUT - BUSY 超时;BSP_ERR_NO_DEVICE - FRSTDATA 无跳变
|
||||
* (E-44:芯片没驱动 DB 总线,读回的是上拉残值,不可信)
|
||||
* 限定条件:GPIO 已初始化
|
||||
* 函数说明:1. 启动转换并等待 BUSY 释放
|
||||
* 2. 连续8次拉低RD读取各通道数据
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生过跳变(= 芯片确实在驱动数据线,E-44)
|
||||
*/
|
||||
int tpafe5160_read_all(int16_t *buf);
|
||||
bsp_err_t tpafe5160_read_all(int16_t *buf);
|
||||
|
||||
/*
|
||||
* 函数功能:读取指定通道的转换结果
|
||||
* 入口参数:channel - 通道号 uint8_t 0 - 7
|
||||
* value - 输出指针 int16_t* 不为 NULL
|
||||
* 返回值:0 - 成功,-1 - 通道号无效,-2 - BUSY 超时
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_PARAM - 通道号无效或指针空;BSP_ERR_TIMEOUT - BUSY 超时
|
||||
* 限定条件:GPIO 已初始化
|
||||
* 函数说明:启动转换并等待完成后,连续读取至指定通道
|
||||
*/
|
||||
int tpafe5160_read_channel(uint8_t channel, int16_t *value);
|
||||
bsp_err_t tpafe5160_read_channel(uint8_t channel, int16_t *value);
|
||||
|
||||
/*
|
||||
* 函数功能:原始ADC值转电压值
|
||||
@@ -213,7 +218,7 @@ uint32_t tpafe5160_frame_seq(void);
|
||||
* 函数功能:原子快照最新一帧(消费者取数的唯一正确姿势)
|
||||
* 入口参数:dst - 输出缓冲区 int16_t* 不为 NULL
|
||||
* count - 拷贝通道数 uint8_t 1 - 8
|
||||
* 返回值:1 - 取到新帧,0 - 无新帧,-1 - 参数无效
|
||||
* 返回值:1 - 取到新帧,0 - 无新帧,BSP_ERR_PARAM(-1) - 参数无效(**三态状态返回,故意不套成败码**:0 表示"这一拍没有新数据",不是错误)
|
||||
* 限定条件:**任务上下文**调用(内部用 taskENTER_CRITICAL,不可在中断里调)
|
||||
* 函数说明:E-58——旧 get_buf() 只返回缓冲区指针,任务再逐通道拷;两步之间被 BUSY
|
||||
* EXTI 抢占并跨过一帧周期,拷回的就是**两帧混合**的 8 通道数据(同一时刻
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 模块名称: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
|
||||
* 不是时间,是自旋圈数(DMA 等待循环)。改 SPI 分频或换主频,实际时长跟着变,
|
||||
* 而且没有任何地方报警。
|
||||
* · 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-22):它的调用方要的就是那个形状,模拟器的 Winsock 后端也照同一
|
||||
* 形状实现;归一只到芯片层 ch395f_*。要收口的是"芯片码到 bsp_err_t"这一层,不是把
|
||||
* BSD 形状改成另一套——那只会让 net_* 的调用方在每个判据上多绕一道。
|
||||
* 4. **丢弃返回值必须写 (void)**。为什么不能靠编译器:实测 ARMCC v5 对
|
||||
* __attribute__((warn_unused_result)) 完全不生效——2026-10-23 凌晨在 rs485_transmit 上做过
|
||||
* 实验,属性在位、调用点裸调不接返回值,构建仍然 0 错 0 警告。所以这条只能靠门禁脚本
|
||||
* (scripts/check_bsp_err.py)与"写 (void) 表示故意丢弃"的书写约定来兜;也意味着
|
||||
* 评审时看见没有 (void) 也没接返回值的调用,就是漏判嫌疑。
|
||||
* 5. 重试由调用方决定:必须有上限、必须喂看门狗、不得跨越就绪屏障盲目等。本头文件
|
||||
* 不提供 retry helper——一个通用 while 重试包装太容易被拿去包住无限等待;每个日志
|
||||
* 调用点内联约 216 B 栈的教训在前,栈预算不欢迎「顺手加一层」。
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BSP_ERR_H */
|
||||
+24
-24
@@ -8,28 +8,28 @@
|
||||
</style>
|
||||
</defs>
|
||||
<text x="410.0" y="24" text-anchor="middle" class="title">Flash (1MB) 使用布局 — 2026-10-22 @build</text>
|
||||
<rect class="bar-seg" x="60" y="50" width="150.6" height="40" fill="#2196F3" rx="2"/>
|
||||
<text x="135.3" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">代码段 (Code) 208.4KB</text>
|
||||
<rect class="bar-seg" x="210.62583923339844" y="50" width="13.2" height="40" fill="#4CAF50" rx="2"/>
|
||||
<text x="227.8" y="70.0" text-anchor="start" dominant-baseline="central" class="legend-text" fill="#333">只读数据 (RO) 18.2KB</text>
|
||||
<rect class="bar-seg" x="223.78890991210938" y="50" width="575.4" height="40" fill="#E0E0E0" rx="2"/>
|
||||
<text x="511.5" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">Flash 空闲 796.2KB</text>
|
||||
<text x="60" y="108" class="label" fill="#333">已用: 227.8KB (22.2%) 空闲: 796.2KB (77.8%) ER_IROM1(Code+RO): 226.6KB (22.1%) 总量: 1MB (= ROM 合计口径)</text>
|
||||
<text x="60" y="124" class="label" fill="#555">Code + RO 分布(ER_IROM1 226.6KB,按 .map 对象归类)</text>
|
||||
<rect class="bar-seg" x="60" y="130" width="279.6" height="24" fill="#4CAF50" rx="2"/>
|
||||
<text x="199.8" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">85.7KB</text>
|
||||
<rect class="bar-seg" x="339.64582399779397" y="130" width="181.8" height="24" fill="#2196F3" rx="2"/>
|
||||
<text x="430.5" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">55.7KB</text>
|
||||
<rect class="bar-seg" x="521.4223053324602" y="130" width="82.6" height="24" fill="#F44336" rx="2"/>
|
||||
<text x="562.7" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">25.3KB</text>
|
||||
<rect class="bar-seg" x="604.0540656992176" y="130" width="57.0" height="24" fill="#607D8B" rx="2"/>
|
||||
<text x="632.6" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">17.5KB</text>
|
||||
<rect class="bar-seg" x="661.0507221398781" y="130" width="34.9" height="24" fill="#FF9800" rx="2"/>
|
||||
<rect class="bar-seg" x="695.9036227637794" y="130" width="34.3" height="24" fill="#00BCD4" rx="2"/>
|
||||
<rect class="bar-seg" x="730.2176760539107" y="130" width="27.0" height="24" fill="#9C27B0" rx="2"/>
|
||||
<rect class="bar-seg" x="757.2174347661233" y="130" width="17.5" height="24" fill="#795548" rx="2"/>
|
||||
<rect class="bar-seg" x="774.709248216194" y="130" width="1.5" height="24" fill="#B0BEC5" rx="2"/>
|
||||
<rect class="bar-seg" x="776.2014408327877" y="130" width="23.8" height="24" fill="#E0E0E0" rx="2"/>
|
||||
<rect class="bar-seg" x="60" y="50" width="150.8" height="40" fill="#2196F3" rx="2"/>
|
||||
<text x="135.4" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">代码段 (Code) 208.6KB</text>
|
||||
<rect class="bar-seg" x="210.77545166015625" y="50" width="13.3" height="40" fill="#4CAF50" rx="2"/>
|
||||
<text x="228.1" y="70.0" text-anchor="start" dominant-baseline="central" class="legend-text" fill="#333">只读数据 (RO) 18.4KB</text>
|
||||
<rect class="bar-seg" x="224.08531188964844" y="50" width="575.1" height="40" fill="#E0E0E0" rx="2"/>
|
||||
<text x="511.6" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">Flash 空闲 795.8KB</text>
|
||||
<text x="60" y="108" class="label" fill="#333">已用: 228.2KB (22.3%) 空闲: 795.8KB (77.7%) ER_IROM1(Code+RO): 227.1KB (22.2%) 总量: 1MB (= ROM 合计口径)</text>
|
||||
<text x="60" y="124" class="label" fill="#555">Code + RO 分布(ER_IROM1 227.1KB,按 .map 对象归类)</text>
|
||||
<rect class="bar-seg" x="60" y="130" width="279.1" height="24" fill="#4CAF50" rx="2"/>
|
||||
<text x="199.6" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">85.7KB</text>
|
||||
<rect class="bar-seg" x="339.1406747294717" y="130" width="181.6" height="24" fill="#2196F3" rx="2"/>
|
||||
<text x="429.9" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">55.7KB</text>
|
||||
<rect class="bar-seg" x="520.7033736473584" y="130" width="82.5" height="24" fill="#F44336" rx="2"/>
|
||||
<text x="561.9" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">25.3KB</text>
|
||||
<rect class="bar-seg" x="603.1858688733291" y="130" width="56.9" height="24" fill="#607D8B" rx="2"/>
|
||||
<text x="631.6" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">17.5KB</text>
|
||||
<rect class="bar-seg" x="660.0795671546786" y="130" width="35.7" height="24" fill="#FF9800" rx="2"/>
|
||||
<rect class="bar-seg" x="695.7956715467855" y="130" width="34.3" height="24" fill="#00BCD4" rx="2"/>
|
||||
<rect class="bar-seg" x="730.0477402928071" y="130" width="27.0" height="24" fill="#9C27B0" rx="2"/>
|
||||
<rect class="bar-seg" x="756.9987269255251" y="130" width="17.5" height="24" fill="#795548" rx="2"/>
|
||||
<rect class="bar-seg" x="774.4589433481859" y="130" width="1.5" height="24" fill="#B0BEC5" rx="2"/>
|
||||
<rect class="bar-seg" x="775.9484404837683" y="130" width="24.1" height="24" fill="#E0E0E0" rx="2"/>
|
||||
<rect x="60" y="167" width="10" height="10" fill="#4CAF50" rx="2"/>
|
||||
<text x="74" y="176" class="legend-text" fill="#333">LVGL 库 85.7KB</text>
|
||||
<rect x="255" y="167" width="10" height="10" fill="#2196F3" rx="2"/>
|
||||
@@ -39,7 +39,7 @@
|
||||
<rect x="645" y="167" width="10" height="10" fill="#607D8B" rx="2"/>
|
||||
<text x="659" y="176" class="legend-text" fill="#333">HAL 驱动 17.5KB</text>
|
||||
<rect x="60" y="183" width="10" height="10" fill="#FF9800" rx="2"/>
|
||||
<text x="74" y="192" class="legend-text" fill="#333">dhara / FTL 10.7KB</text>
|
||||
<text x="74" y="192" class="legend-text" fill="#333">dhara / FTL 11.0KB</text>
|
||||
<rect x="255" y="183" width="10" height="10" fill="#00BCD4" rx="2"/>
|
||||
<text x="269" y="192" class="legend-text" fill="#333">FreeRTOS 内核 10.5KB</text>
|
||||
<rect x="450" y="183" width="10" height="10" fill="#9C27B0" rx="2"/>
|
||||
@@ -49,5 +49,5 @@
|
||||
<rect x="60" y="199" width="10" height="10" fill="#B0BEC5" rx="2"/>
|
||||
<text x="74" y="208" class="legend-text" fill="#333">startup 0.5KB</text>
|
||||
<rect x="255" y="199" width="10" height="10" fill="#E0E0E0" rx="2"/>
|
||||
<text x="269" y="208" class="legend-text" fill="#333">其他 (长尾) 7.3KB</text>
|
||||
<text x="269" y="208" class="legend-text" fill="#333">其他 (长尾) 7.4KB</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -474,6 +474,39 @@ cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法
|
||||
| **运行器** | `scripts/check_layer_deps.py`(纯标准库;`--verbose` 列全部边与判定、`--prune` 只报可删基线;退出码 0=通过 / 1=新增反向依赖) |
|
||||
| **实测自证** | 落地时做过一次"故意违规"验证:临时给 `Drivers/BSP/RS485/rs485.c` 加一行 `#include "cfg_file.h"` → 钩子当场 FAIL 并点名该文件与该头(退出码 1),删掉后转绿。**没有当场触发过的门禁等于没有门禁** |
|
||||
|
||||
### 2.27 bsp-err — BSP 错误码与超时命名约定(2026-10-23 加,报告 S-5)
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **为什么需要** | 报告原文说"无统一错误码",实测更准的说法是**六套并存**:0/−1(SD2506、NETCFG、nand_ftl)、`int32_t`;
|
||||
自研枚举(`CH395F_STATUS_*`)、芯片寄存器原生码(`CH395F_ERR_*`,与前一套同模块并存)、
|
||||
`HAL_StatusTypeDef`(`rs485.h` 曾直接暴露)、`bool`,外加第三方 dhara 的出参 `dhara_error_t`
|
||||
与 FatFs 的 `FRESULT`。超时侧更乱:**同形两义**的 `*_TIMEOUT` 有四个(毫秒 / 自旋圈数 /
|
||||
其实是返回码),`osWaitForever` 与 `HAL_MAX_DELAY` 又**同为 0xFFFFFFFFU**(值同域不同,
|
||||
跨层转手既不报错也不变慢)。**没有机械检查的约定等于没有约定**(分层方向那条的教训) |
|
||||
| **约定正文在哪** | `Drivers/BSP/bsp_err.h`(**header-only,无 `.c`** → 不占体积、不需双端注册)。
|
||||
`0 = BSP_OK`、负值失败、`BSP_ERR_CAN_RETRY(e)` 把"能不能重试"变成跨模块可判定(这是本契约
|
||||
真正值钱的一条:以前"可重试"只写在个别注释里) |
|
||||
| **三条规则** | **R1** 公开 API 不得用 `HAL_StatusTypeDef` / `bool` 当错误通道(`is_/has_/can_/valid`
|
||||
这类**谓词**白名单放行——第一版误判了 7 个正当谓词,我们把规则改窄,**不拿基线去洗规则**);
|
||||
**R2** `*_TIMEOUT` 宏不得取负值(名字叫 TIMEOUT 却是返回码 → 一律改 `BSP_ERR_TIMEOUT`);
|
||||
**R3** 非 void 的公开 API 必须有「返回值:」注释(约定要能被读到才谈得上审查) |
|
||||
| **范围** | 只扫 `git ls-files` 里 `Drivers/BSP/**/*.h`(对外 API 面);**lftpd 按第三方排除**——
|
||||
它的 `226/425/450/550` 是 FTP 协议码不是内部错误码;**`net_*` BSD 兼容层不纳入**(用户裁定
|
||||
2026-10-23):调用方要的就是"返回字节数 + 每任务 errno 槽"那个形状,模拟器的 Winsock
|
||||
后端也照同一形状实现 |
|
||||
| **增量拦截** | 存量 **17 条**(首清点 19,2026-10-23 C1 还掉 2)钉 `BASELINE`(一个 `bool ui_content_page_open()` + 16 个缺「返回值:」),**只减不增**、脚本没有 `--write`;修完跑 `--prune` 会点名"这条已消失,
|
||||
请从基线删除" |
|
||||
| **配置** | `files: ^Drivers/BSP/\|^scripts/check_bsp_err\.py$\|^\.pre-commit-config\.yaml$`、
|
||||
`pass_filenames: false`(恒扫全库,要按 git 清单建公开头索引) |
|
||||
| **实测负结果(顺手记一条)** | `__attribute__((warn_unused_result))` **在 ARMCC v5 上不生效**:
|
||||
属性挂上、调用点裸调不接返回值,构建仍 0 错 0 警告。所以"返回值必须被消费"只能靠
|
||||
本脚本 + 书写约定"故意丢弃必须写 `(void)`",评审时看到既没 `(void)` 也没接住的调用就是漏判嫌疑 |
|
||||
| **踩坑提醒** | pre-commit 会 **stash 未暂存改动**再跑钩子。所以"改代码 + 同时更新基线"要在
|
||||
一次提交里**把改动 `git add` 了再跑**,否则钩子看到的是没有你改动的树,会假报警 |
|
||||
| **运行器** | `scripts/check_bsp_err.py`(纯标准库;`--verbose` 列全部命中归类、`--prune` 只报
|
||||
可删基线、`--emit-baseline` 建基线用;退出码 0/1/2) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 日常工作流
|
||||
@@ -601,7 +634,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env
|
||||
|
||||
| 文件 | 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**38 个钩子**,2026-10-22 清点;本文 §2 详解非单测类——N-19 新增的两个单测钩子见 §2.16/§2.17,N-23 双端注册反向校验(含 U4 补强的 UI 页面描述符表接线 ④)见 §2.18,N-27 RAM 预算门禁见 §2.19,B4 IEC104 协议栈单测见 §2.20,B8 频率测量单测见 §2.21,B9 SOE 动作记录单测见 §2.22,N-21 dhara NAND 移植层单测见 §2.23,U4 残余字符集差集检查见 §2.24,编号门禁八项校验见 §2.25,分层方向门禁见 §2.26,其余主机侧单测见 AGENTS.md 门禁条;主机侧单测共 16 个;UI 门禁另有 `font-charset` 与 `ui-golden`) |
|
||||
| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**39 个钩子**,2026-10-23 清点;本文 §2 详解非单测类——N-19 新增的两个单测钩子见 §2.16/§2.17,N-23 双端注册反向校验(含 U4 补强的 UI 页面描述符表接线 ④)见 §2.18,N-27 RAM 预算门禁见 §2.19,B4 IEC104 协议栈单测见 §2.20,B8 频率测量单测见 §2.21,B9 SOE 动作记录单测见 §2.22,N-21 dhara NAND 移植层单测见 §2.23,U4 残余字符集差集检查见 §2.24,编号门禁八项校验见 §2.25,分层方向门禁见 §2.26,BSP 错误码与超时命名约定门禁见 §2.27,其余主机侧单测见 AGENTS.md 门禁条;主机侧单测共 16 个;UI 门禁另有 `font-charset` 与 `ui-golden`) |
|
||||
| `.gitlint` | 仓库根 | gitlint 规则(宽松,兼容中文) |
|
||||
| `.cubemx_survival_baseline.json` | 仓库根 | CubeMX 易失定制基线(哈希;`--write` 重建) |
|
||||
| `scripts/check_mojibake.py` | scripts/ | U+FFFD 检测脚本 |
|
||||
@@ -614,6 +647,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env
|
||||
| `scripts/check_test_suite.py` | scripts/ | 测试套件开关门禁(防测试固件静默提交) |
|
||||
| `scripts/check_font_charset.py` | scripts/ | UI 字符集差集检查(复用 `tools/font_gen/` 提取与 cmap 解析规则,防缺字白框) |
|
||||
| `scripts/check_layer_deps.py` | scripts/ | 分层方向门禁(BSP 反向 include App 的增量拦截,存量基线只减不增) |
|
||||
| `scripts/check_bsp_err.py` | scripts/ | BSP 错误码与超时命名门禁(三条规则的增量拦截,存量 19 条基线只减不增;`--emit-baseline` 建基线) |
|
||||
| `docs/代码规范/嵌入式C语言代码规范_V1.0.md` | docs/代码规范/ | 代码规范原文(CODE-RULE-01~CODE-RULE-19 规则来源) |
|
||||
| `docs/代码规范/cppcheck使用说明.md` | docs/代码规范/ | cppcheck 钩子详解(规则/配置/抑制) |
|
||||
| `docs/文档规范.md` | docs/ | 文档体系规范(链接形态/命名/门禁要求) |
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
> 版本:v1.3 日期:2026-10-22(v1.2 2026-09-21)
|
||||
> 变更:v1.3 §4 的阶段子项改为**层级编号**(见下"编号约定");同步 2026-10-22 现状数字与
|
||||
> `data/ram_report_20261022.md` / `data/stack_report_20261022.md` 两份新快照。
|
||||
> 变更:2026-10-23 补 `data/ram_report_20261023.md` 快照(S-5 试点:RS485 改报 `bsp_err_t`,
|
||||
> `Code` 213,436 → **213,648**(S-5 全波 +212:契约试点 +48、C3 SD2506 +44、C1 GD5F/TPAFE +36、
|
||||
C2 dhara 失败类 +84;RO 另 +208,全是 C2 的失败类名表)→ `ER_IROM1` **232,508 B / 22.2%**;
|
||||
> **RAM 全部字段零增量**;受管表已由
|
||||
> `check_mem_numbers.py --write` 同步,配图常量同步)。
|
||||
> 变更:v1.2 数据现状全量同步——§2.1/§2.2/§5/§6/§8.6 等"现状"数据全部更新为 2026-09-21 lftpd 静态化后的最新 map 与实测值;清除 2026-09-11 历史口径残留(历史快照保留于 `data/`)
|
||||
> 适用工程:STM32F4-Base(STM32F407ZGTx @168MHz,FreeRTOS V10.3.1 + CMSIS-RTOS2)
|
||||
> 数据来源:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(`@build` 重建)+ `scripts/ram_report.py`(静态台账)+ `scripts/stack_report.py`(静态调用图 × 运行时栈水位)+ 源码静态分析 + 网络调研
|
||||
@@ -38,10 +43,10 @@
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 232,088 B (226.65 KB) | 1,048,576 B (1024 KB) | 22.1% |
|
||||
| ER_IROM1 | 232,508 B (227.06 KB) | 1,048,576 B (1024 KB) | 22.2% |
|
||||
| RW_IRAM1 | 75,264 B (73.50 KB) | 131,072 B (128 KB) | 57.4% |
|
||||
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||
| **Flash (ROM 合计)** | **233,276 B (227.8 KB)** | 1,048,576 B (1024 KB) | **22.2%** |
|
||||
| **Flash (ROM 合计)** | **233,696 B (228.2 KB)** | 1,048,576 B (1024 KB) | **22.3%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
||||
|---|---|---|---|---|
|
||||
| ER_IROM1 (Flash) | 232,088 B | 1,048,576 B | 22.1% | 参考 |
|
||||
| ER_IROM1 (Flash) | 232,508 B | 1,048,576 B | 22.2% | 参考 |
|
||||
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
||||
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
||||
| RW_IRAM1 | 75,264 B | 131,072 B | 57.4% | PASS |
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
# STM32F4-Base RAM/Flash Usage Report
|
||||
|
||||
> Generated: 2026-10-23 01:35 Map: `STM32F407-Demo.map`
|
||||
|
||||
## 1. Grand Totals (全局总量)
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| Code (inc. data) | 213,648 B (208.6 KB) |
|
||||
| RO Data | 18,860 B (18.4 KB) |
|
||||
| RW Data (initialized) | 1,188 B (1.16 KB) |
|
||||
| ZI Data (zero-init) | 131,500 B (128.4 KB) |
|
||||
| **Total RO (Code + RO)** | **232,508 B (227.1 KB)** |
|
||||
| **Total RW (RW + ZI) = SRAM used** | **132,688 B (129.58 KB)** |
|
||||
| **Total ROM (Code + RO + RW init)** | **233,696 B (228.2 KB)** |
|
||||
|
||||
## 2. 执行区水位 (Execution Regions)
|
||||
|
||||
| Region | Base | Size | Max | Util% |
|
||||
|---|---|---|---|---|
|
||||
| ER_IROM1 | 0x08000000 | 232,508 B (227.06 KB) | 1,048,576 B (1024 KB) | 22.2% |
|
||||
| RW_CCM | 0x10000000 | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||
| UNINIT_CCM | 0x1000FF00 | 48 B (0.05 KB) | 256 B (0 KB) | 18.8% |
|
||||
| RW_IRAM1 | 0x20000000 | 75,264 B (73.50 KB) | 131,072 B (128 KB) | 57.4% |
|
||||
|
||||
## 3. RW_IRAM1 逐符号明细(ZI + RW,按大小降序)
|
||||
|
||||
| # | Address | Size (B) | Size (KB) | Type | Section | Object |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | 0x2000ADC8 | 16,384 | 16.00 | Zero | .bss | lv_mem_core_builtin.o |
|
||||
| 2 | 0x20007EB4 | 7,724 | 7.54 | Zero | .bss | lftpd.o |
|
||||
| 3 | 0x200107A4 | 6,746 | 6.59 | Zero | .bss | ui_net_sink.o |
|
||||
| 4 | 0x2000EDC8 | 6,408 | 6.26 | Zero | .bss | ui_port.o |
|
||||
| 5 | 0x2000667C | 6,200 | 6.05 | Zero | .bss | nand_ftl.o |
|
||||
| 6 | 0x20004E18 | 4,152 | 4.05 | Zero | .bss | ch395f.o |
|
||||
| 7 | 0x20002058 | 4,096 | 4.00 | Zero | .bss | console_log.o |
|
||||
| 8 | 0x20001278 | 3,552 | 3.47 | Zero | .bss | sys_log_mcu.o |
|
||||
| 9 | 0x20003644 | 2,724 | 2.66 | Zero | .bss | default_task.o |
|
||||
| 10 | 0x2000A524 | 1,720 | 1.68 | Zero | .bss | cmsis_os2.o |
|
||||
| 11 | 0x2000444C | 1,440 | 1.41 | Zero | .bss | sample_ring.o |
|
||||
| 12 | 0x20009F48 | 1,220 | 1.19 | Zero | .bss | tasks.o |
|
||||
| 13 | 0x20000D78 | 1,104 | 1.08 | Zero | .bss | cfg_file_store_mcu.o |
|
||||
| 14 | 0x20003058 | 1,064 | 1.04 | Zero | .bss | soe_store.o |
|
||||
| 15 | 0x20012200 | 1,024 | 1.00 | Zero | | startup_stm32f407xx.o |
|
||||
| 16 | 0x20005E50 | 800 | 0.78 | Zero | .bss | gd5f2gq5ue.o |
|
||||
| 17 | 0x200040E8 | 712 | 0.70 | Zero | .bss | iec104_serv.o |
|
||||
| 18 | 0x20000AB8 | 704 | 0.69 | Zero | .bss | cfg_file.o |
|
||||
| 19 | 0x200005D0 | 560 | 0.55 | Zero | .bss | spi.o |
|
||||
| 20 | 0x20000848 | 552 | 0.54 | Zero | .bss | usart.o |
|
||||
| 21 | 0x20009CE0 | 552 | 0.54 | Zero | .bss | lftpd_io.o |
|
||||
| 22 | 0x2000ABDC | 492 | 0.48 | Zero | .bss | lv_init.o |
|
||||
| 23 | 0x20004C38 | 480 | 0.47 | Zero | .bss | freq.o |
|
||||
| 24 | 0x2000629C | 416 | 0.41 | Zero | .bss | net_socket.o |
|
||||
| 25 | 0x200064DC | 416 | 0.41 | Zero | .bss | net_pending.o |
|
||||
| 26 | 0x200034C4 | 384 | 0.38 | Zero | .bss | task_prof.o |
|
||||
| 27 | 0x20004A8C | 320 | 0.31 | Zero | .bss | prot_set.o |
|
||||
| 28 | 0x2000A40C | 280 | 0.27 | Zero | .bss | timers.o |
|
||||
| 29 | 0x20006190 | 268 | 0.26 | Zero | .bss | rs485.o |
|
||||
| 30 | 0x200011C8 | 176 | 0.17 | Zero | .bss | prot_set_store.o |
|
||||
| 31 | 0x200004C8 | 168 | 0.16 | Zero | .bss | adc.o |
|
||||
| 32 | 0x200002B4 | 160 | 0.16 | Data | .data | lftpd.o |
|
||||
| 33 | 0x200049EC | 160 | 0.16 | Zero | .bss | phasor.o |
|
||||
| 34 | 0x200106D0 | 152 | 0.15 | Zero | .bss | ui_menu_view.o |
|
||||
| 35 | 0x2000643C | 128 | 0.12 | Zero | .bss | net_listener.o |
|
||||
| 36 | 0x20004BCC | 108 | 0.11 | Zero | .bss | prot_elm.o |
|
||||
| 37 | 0x20000570 | 84 | 0.08 | Zero | .bss | i2c.o |
|
||||
| 38 | 0x200043F8 | 84 | 0.08 | Zero | .bss | adc_input.o |
|
||||
| 39 | 0x20000250 | 83 | 0.08 | Data | .data | ftp_cfg.o |
|
||||
| 40 | 0x20000800 | 72 | 0.07 | Zero | .bss | tim.o |
|
||||
| 41 | 0x20000A70 | 72 | 0.07 | Zero | .bss | stm32f4xx_hal_timebase_tim.o |
|
||||
| 42 | 0x20003480 | 68 | 0.07 | Zero | .bss | task_watchdog.o |
|
||||
| 43 | 0x20000484 | 65 | 0.06 | Zero | .bss | freertos.o |
|
||||
| 44 | 0x20009F08 | 64 | 0.06 | Zero | .bss | queue.o |
|
||||
| 45 | 0x20000358 | 60 | 0.06 | Data | .data | tasks.o |
|
||||
| 46 | 0x20000410 | 60 | 0.06 | Data | .data | ui_page_selftest.o |
|
||||
| 47 | 0x20010768 | 60 | 0.06 | Zero | .bss | ui_page_runlog.o |
|
||||
| 48 | 0x20000150 | 48 | 0.05 | Data | .data | adc_health.o |
|
||||
| 49 | 0x200043C8 | 48 | 0.05 | Zero | .bss | iec104_pts.o |
|
||||
| 50 | 0x20000000 | 44 | 0.04 | Data | .data | freertos.o |
|
||||
| 51 | 0x2000009C | 44 | 0.04 | Data | .data | task_watchdog.o |
|
||||
| 52 | 0x20000044 | 36 | 0.04 | Data | .data | sys_log_mcu.o |
|
||||
| 53 | 0x2000022C | 36 | 0.04 | Data | .data | net_cfg.o |
|
||||
| 54 | 0x2000044C | 32 | 0.03 | Data | .data | ui_standby.o |
|
||||
| 55 | 0x20006170 | 32 | 0.03 | Zero | .bss | tpafe5160.o |
|
||||
| 56 | 0x200064BC | 32 | 0.03 | Zero | .bss | net_events.o |
|
||||
| 57 | 0x20000128 | 28 | 0.03 | Data | .data | adc_input.o |
|
||||
| 58 | 0x20000068 | 24 | 0.02 | Data | .data | console_log.o |
|
||||
| 59 | 0x200000E4 | 24 | 0.02 | Data | .data | adc_task.o |
|
||||
| 60 | 0x20000180 | 24 | 0.02 | Data | .data | prot_set.o |
|
||||
| 61 | 0x2000019C | 24 | 0.02 | Data | .data | freq.o |
|
||||
| 62 | 0x20000200 | 24 | 0.02 | Data | .data | net_listener.o |
|
||||
| 63 | 0x200043B0 | 24 | 0.02 | Zero | .bss | protect_task.o |
|
||||
| 64 | 0x20000030 | 20 | 0.02 | Data | .data | cfg_file.o |
|
||||
| 65 | 0x20000080 | 20 | 0.02 | Data | .data | soe_store.o |
|
||||
| 66 | 0x200001D0 | 20 | 0.02 | Data | .data | gd5f2gq5ue.o |
|
||||
| 67 | 0x20000394 | 20 | 0.02 | Data | .data | timers.o |
|
||||
| 68 | 0x2000046C | 20 | 0.02 | Data | .data | ui_net_sink.o |
|
||||
| 69 | 0x200003D4 | 18 | 0.02 | Data | .data | ui_menu.o |
|
||||
| 70 | 0x200000FC | 16 | 0.02 | Data | .data | iec104_serv.o |
|
||||
| 71 | 0x20000118 | 16 | 0.02 | Data | .data | iec104_pts.o |
|
||||
| 72 | 0x200001F0 | 16 | 0.02 | Data | .data | net_socket.o |
|
||||
| 73 | 0x20000218 | 16 | 0.02 | Data | .data | net_events.o |
|
||||
| 74 | 0x200002A4 | 16 | 0.02 | Data | .data | nand_ftl.o |
|
||||
| 75 | 0x200003F0 | 16 | 0.02 | Data | .data | ui_content_page.o |
|
||||
| 76 | 0x200000D4 | 12 | 0.01 | Data | .data | default_task.o |
|
||||
| 77 | 0x200001B4 | 12 | 0.01 | Data | .data | stm32f4xx_hal.o |
|
||||
| 78 | 0x200001E4 | 12 | 0.01 | Data | .data | tpafe5160.o |
|
||||
| 79 | 0x200003AC | 12 | 0.01 | Data | .data | port.o |
|
||||
| 80 | 0x200003C8 | 12 | 0.01 | Data | .data | ui_port.o |
|
||||
| 81 | 0x20000400 | 12 | 0.01 | Data | .data | ui_page_password.o |
|
||||
| 82 | 0x200005C4 | 12 | 0.01 | Zero | .bss | iwdg.o |
|
||||
| 83 | 0x200001C4 | 10 | 0.01 | Data | .data | ch395f.o |
|
||||
| 84 | 0x20000094 | 8 | 0.01 | Data | .data | sys_clock.o |
|
||||
| 85 | 0x200000C8 | 8 | 0.01 | Data | .data | task_prof.o |
|
||||
| 86 | 0x2000010C | 8 | 0.01 | Data | .data | protect_task.o |
|
||||
| 87 | 0x20000144 | 8 | 0.01 | Data | .data | sample_ring.o |
|
||||
| 88 | 0x200003B8 | 8 | 0.01 | Data | .data | ff.o |
|
||||
| 89 | 0x200003C0 | 8 | 0.01 | Data | .data | ffsystem.o |
|
||||
| 90 | 0x200003E8 | 8 | 0.01 | Data | .data | ui_menu_view.o |
|
||||
| 91 | 0x2000002C | 4 | 0.00 | Data | .data | app_main.o |
|
||||
| 92 | 0x200000D0 | 4 | 0.00 | Data | .data | vol_lock.o |
|
||||
| 93 | 0x200000E0 | 4 | 0.00 | Data | .data | net_task.o |
|
||||
| 94 | 0x200001C0 | 4 | 0.00 | Data | .data | system_stm32f4xx.o |
|
||||
| 95 | 0x20000228 | 4 | 0.00 | Data | .data | net_pending.o |
|
||||
| 96 | 0x20000354 | 4 | 0.00 | Data | .data | lftpd_io.o |
|
||||
| 97 | 0x200003A8 | 4 | 0.00 | Data | .data | cmsis_os2.o |
|
||||
| 98 | 0x20000480 | 4 | 0.00 | Data | .data | mc_w.l(errno.o) |
|
||||
| 99 | 0x2000014C | 2 | 0.00 | Data | .data | phasor.o |
|
||||
| 100 | 0x2000040C | 2 | 0.00 | Data | .data | ui_page_net_cfg.o |
|
||||
| 101 | 0x20000198 | 1 | 0.00 | Data | .data | prot_elm.o |
|
||||
| | | **75,243** | **73.48** | | | **Total** |
|
||||
|
||||
## 4. Image Component Sizes(RW + ZI > 0,按大小降序)
|
||||
|
||||
| # | Object | Code | RO | RW | ZI | RW+ZI |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | heap_4.o | 512 | 69 | 32 | 57,344 | 57,376 |
|
||||
| 2 | lv_mem_core_builtin.o | 410 | 0 | 0 | 16,384 | 16,384 |
|
||||
| 3 | lftpd.o | 6,352 | 509 | 160 | 7,724 | 7,884 |
|
||||
| 4 | ui_net_sink.o | 1,126 | 67 | 20 | 6,746 | 6,766 |
|
||||
| 5 | ui_port.o | 418 | 0 | 12 | 6,408 | 6,420 |
|
||||
| 6 | nand_ftl.o | 2,434 | 384 | 16 | 6,200 | 6,216 |
|
||||
| 7 | ch395f.o | 3,978 | 603 | 10 | 4,152 | 4,162 |
|
||||
| 8 | console_log.o | 980 | 55 | 24 | 4,096 | 4,120 |
|
||||
| 9 | sys_log_mcu.o | 3,074 | 83 | 36 | 3,552 | 3,588 |
|
||||
| 10 | default_task.o | 2,332 | 280 | 12 | 2,724 | 2,736 |
|
||||
| 11 | cmsis_os2.o | 1,496 | 0 | 4 | 1,720 | 1,724 |
|
||||
| 12 | sample_ring.o | 292 | 0 | 8 | 1,440 | 1,448 |
|
||||
| 13 | tasks.o | 4,342 | 0 | 60 | 1,220 | 1,280 |
|
||||
| 14 | cfg_file_store_mcu.o | 380 | 24 | 0 | 1,104 | 1,104 |
|
||||
| 15 | soe_store.o | 816 | 28 | 20 | 1,064 | 1,084 |
|
||||
| 16 | startup_stm32f407xx.o | 36 | 392 | 0 | 1,024 | 1,024 |
|
||||
| 17 | gd5f2gq5ue.o | 3,954 | 478 | 20 | 800 | 820 |
|
||||
| 18 | iec104_serv.o | 1,188 | 16 | 16 | 712 | 728 |
|
||||
| 19 | cfg_file.o | 5,970 | 810 | 20 | 704 | 724 |
|
||||
| 20 | spi.o | 584 | 0 | 0 | 560 | 560 |
|
||||
| 21 | lftpd_io.o | 1,198 | 0 | 4 | 552 | 556 |
|
||||
| 22 | usart.o | 884 | 0 | 0 | 552 | 552 |
|
||||
| 23 | freq.o | 764 | 8 | 24 | 480 | 504 |
|
||||
| 24 | lv_init.o | 156 | 0 | 0 | 492 | 492 |
|
||||
| 25 | net_socket.o | 4,944 | 443 | 16 | 416 | 432 |
|
||||
| 26 | net_pending.o | 1,400 | 0 | 4 | 416 | 420 |
|
||||
| 27 | task_prof.o | 638 | 185 | 8 | 384 | 392 |
|
||||
| 28 | prot_set.o | 844 | 640 | 24 | 320 | 344 |
|
||||
| 29 | timers.o | 1,232 | 0 | 20 | 280 | 300 |
|
||||
| 30 | rs485.o | 168 | 0 | 0 | 268 | 268 |
|
||||
| 31 | prot_set_store.o | 236 | 74 | 0 | 176 | 176 |
|
||||
| 32 | adc.o | 580 | 0 | 0 | 168 | 168 |
|
||||
| 33 | phasor.o | 420 | 258 | 2 | 160 | 162 |
|
||||
| 34 | task_watchdog.o | 1,110 | 137 | 44 | 116 | 160 |
|
||||
| 35 | ui_menu_view.o | 2,034 | 16 | 8 | 152 | 160 |
|
||||
| 36 | net_listener.o | 948 | 207 | 24 | 128 | 152 |
|
||||
| 37 | adc_input.o | 592 | 27 | 28 | 84 | 112 |
|
||||
| 38 | freertos.o | 716 | 592 | 44 | 65 | 109 |
|
||||
| 39 | prot_elm.o | 708 | 227 | 1 | 108 | 109 |
|
||||
| 40 | i2c.o | 168 | 0 | 0 | 84 | 84 |
|
||||
| 41 | ftp_cfg.o | 152 | 225 | 83 | 0 | 83 |
|
||||
| 42 | stm32f4xx_hal_timebase_tim.o | 152 | 0 | 0 | 72 | 72 |
|
||||
| 43 | tim.o | 168 | 0 | 0 | 72 | 72 |
|
||||
| 44 | iec104_pts.o | 1,082 | 132 | 16 | 48 | 64 |
|
||||
| 45 | queue.o | 2,840 | 0 | 0 | 64 | 64 |
|
||||
| 46 | ui_page_runlog.o | 28 | 105 | 0 | 60 | 60 |
|
||||
| 47 | ui_page_selftest.o | 28 | 109 | 60 | 0 | 60 |
|
||||
| 48 | adc_health.o | 432 | 0 | 48 | 0 | 48 |
|
||||
| 49 | net_events.o | 1,994 | 809 | 16 | 32 | 48 |
|
||||
| 50 | tpafe5160.o | 808 | 67 | 12 | 32 | 44 |
|
||||
| 51 | net_cfg.o | 832 | 103 | 36 | 0 | 36 |
|
||||
| 52 | protect_task.o | 840 | 68 | 8 | 24 | 32 |
|
||||
| 53 | ui_standby.o | 1,208 | 67 | 32 | 0 | 32 |
|
||||
| 54 | adc_task.o | 1,520 | 234 | 24 | 0 | 24 |
|
||||
| 55 | (incl. Padding) | 288 | 95 | 16 | 5 | 21 |
|
||||
| 56 | ui_menu.o | 760 | 137 | 18 | 0 | 18 |
|
||||
| 57 | ui_content_page.o | 1,664 | 81 | 16 | 0 | 16 |
|
||||
| 58 | iwdg.o | 28 | 0 | 0 | 12 | 12 |
|
||||
| 59 | port.o | 762 | 73 | 12 | 0 | 12 |
|
||||
| 60 | stm32f4xx_hal.o | 116 | 0 | 12 | 0 | 12 |
|
||||
| 61 | ui_page_password.o | 686 | 175 | 12 | 0 | 12 |
|
||||
| 62 | ff.o | 7,962 | 282 | 8 | 0 | 8 |
|
||||
| 63 | ffsystem.o | 204 | 20 | 8 | 0 | 8 |
|
||||
| 64 | sys_clock.o | 1,788 | 286 | 8 | 0 | 8 |
|
||||
| 65 | app_main.o | 2,308 | 65 | 4 | 0 | 4 |
|
||||
| 66 | net_task.o | 1,460 | 72 | 4 | 0 | 4 |
|
||||
| 67 | system_stm32f4xx.o | 16 | 24 | 4 | 0 | 4 |
|
||||
| 68 | vol_lock.o | 92 | 0 | 4 | 0 | 4 |
|
||||
| 69 | errno.o | 20 | 0 | 4 | 0 | 4 |
|
||||
| 70 | ui_page_net_cfg.o | 1,004 | 105 | 2 | 0 | 2 |
|
||||
| | **All .o files** | **213,648** | **18,860** | **1,188** | **131,500** | **132,688** |
|
||||
|
||||
## 5. Flash (Code + RO Data) Top-30(按大小降序)
|
||||
|
||||
| # | Object | Code | RO Data | Code+RO |
|
||||
|---|---|---|---|---|
|
||||
| 1 | ff.o | 7,962 | 282 | 8,244 |
|
||||
| 2 | lftpd.o | 6,352 | 509 | 6,861 |
|
||||
| 3 | cfg_file.o | 5,970 | 810 | 6,780 |
|
||||
| 4 | net_socket.o | 4,944 | 443 | 5,387 |
|
||||
| 5 | lv_indev.o | 4,928 | 0 | 4,928 |
|
||||
| 6 | ch395f.o | 3,978 | 603 | 4,581 |
|
||||
| 7 | ui_font_cn_12.o | 0 | 4,545 | 4,545 |
|
||||
| 8 | lv_obj_pos.o | 4,526 | 16 | 4,542 |
|
||||
| 9 | lv_refr.o | 4,486 | 0 | 4,486 |
|
||||
| 10 | gd5f2gq5ue.o | 3,954 | 478 | 4,432 |
|
||||
| 11 | tasks.o | 4,342 | 0 | 4,342 |
|
||||
| 12 | lv_obj_scroll.o | 4,126 | 0 | 4,126 |
|
||||
| 13 | lv_obj_style.o | 4,106 | 0 | 4,106 |
|
||||
| 14 | lv_draw_sw_mask.o | 3,862 | 0 | 3,862 |
|
||||
| 15 | stm32f4xx_hal_spi.o | 3,586 | 0 | 3,586 |
|
||||
| 16 | lv_draw_sw_box_shadow.o | 3,246 | 0 | 3,246 |
|
||||
| 17 | sys_log_mcu.o | 3,074 | 83 | 3,157 |
|
||||
| 18 | lv_indev_scroll.o | 3,068 | 0 | 3,068 |
|
||||
| 19 | lv_obj.o | 2,982 | 43 | 3,025 |
|
||||
| 20 | lv_draw_sw_img.o | 2,960 | 0 | 2,960 |
|
||||
| 21 | queue.o | 2,840 | 0 | 2,840 |
|
||||
| 22 | journal.o | 2,834 | 0 | 2,834 |
|
||||
| 23 | iec104_link.o | 2,824 | 0 | 2,824 |
|
||||
| 24 | nand_ftl.o | 2,434 | 384 | 2,818 |
|
||||
| 25 | net_events.o | 1,994 | 809 | 2,803 |
|
||||
| 26 | default_task.o | 2,332 | 280 | 2,612 |
|
||||
| 27 | app_main.o | 2,308 | 65 | 2,373 |
|
||||
| 28 | printfa.o | 2,312 | 0 | 2,312 |
|
||||
| 29 | stm32f4xx_hal_i2c.o | 2,294 | 0 | 2,294 |
|
||||
| 30 | lv_display.o | 2,284 | 0 | 2,284 |
|
||||
| | **All .o files** | **213,648** | **18,860** | **232,508** |
|
||||
|
||||
> 自洽校验:per-file 213,648/18,860/1,188/131,500 vs Grand Totals 213,648/18,860/1,188/131,500(CodeΔ=+0 / ROΔ=+0 / RWΔ=+0 / ZIΔ=+0;精确一致(含 '('incl. Generated)'-'(incl. Padding)' 台账行))→ **PASS**
|
||||
@@ -621,3 +621,26 @@ lftpd 原先把全部异常路径一律按 error 级打(串口上带 `[ERR]`
|
||||
- 排障信息量不减:peer 级照旧打印,且比原来更准(带 `ret=` / `sk=` / `timeout(ms)`,措辞从
|
||||
"error reading next command"改成"control channel closed by peer"——原先那句把正常收尾
|
||||
写成了故障,误导过排查方向。
|
||||
|
||||
### 10.4 dhara 失败类:ECC / 预算耗尽 / 完整性故障(2026-10-23,S-5 C2)
|
||||
|
||||
一次 NAND 读写要穿过 **SPI → `gd5f_*` → dhara(`dhara_error_t`)→ FTL glue → FatFs `RES_ERROR`**
|
||||
五段,而 glue 以前把 dhara 的所有失败**压成一个 `GD5F_ERROR`**——上层分不清"该重试"、
|
||||
"该标坏块"、还是"块池见底了,重格式化也没用"。现在 `nand_ftl.c` 在**最早同时知道
|
||||
LPN 与原始码的位置**就地分类并打印(`ftl_err_name()` 给可读名;**本仓 dhara 没有编
|
||||
`error.c`,所以没有 `dhara_strerror()`,用它直接链接失败**——表是自己的一张短表):
|
||||
|
||||
| dhara 错误 | 归类 | 处置上为什么不同 |
|
||||
|---|---|---|
|
||||
| `ECC` / `TOO_BAD` | `BSP_ERR_CRC` | 位翻转不可纠或页太坏:**是页级问题**,读重试 / 由 dhara 换页,不代表卷坏 |
|
||||
| `JOURNAL_FULL` / `MAP_FULL` | `BSP_ERR_NO_SPACE` | 介质还健康,是 **journal / 映射层块池见底**:该走容量恢复(`../测试说明/存储维护与容量恢复.md`),此时重格式化只是把问题推回去 |
|
||||
| `BAD_BLOCK` / `RECOVER` / `NOT_FOUND` / `CORRUPT_MAP` / 越界 | `BSP_ERR_IO` | 映射或介质**完整性**故障:保卷 + 上报,不许自愈重格式化 |
|
||||
|
||||
- 这条分类与 **E-31** 是同一条纪律的两端:E-31 规定"只有扫描全程 I/O 干净且无
|
||||
checkpoint(`DHARA_E_TOO_BAD`)才算真空白卷",本表则保证失败**可读**——两者合起来
|
||||
才是"别把瞬态毛刺当卷损坏去 `f_mkfs`"(旧行为:单次 I2C/SPI 抖动 → 清 map → 整卷
|
||||
重格式化 → 静默销毁全部用户数据)。
|
||||
- **仍未闭环**:分类目前**只进日志**;`disk_read`/`disk_write` 对 FatFs 仍只能回 `RES_ERROR`
|
||||
(那是 FatFs 的契约)。要让 defaultTask 的卷健康探针**按类分支**,还需要一个跨调用
|
||||
存活的失败类快照(`nand_ftl_last_error()`,8 B;代价是要先定多写者纪律——
|
||||
defaultTask 与 testTask 都会碰存储)→ 报告 S-5 的 C4 待还项。
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
> 详细芯片行为见 `docs/芯片手册/GD5F2GQ5UExxG.md`,已知陷阱见 `docs/问题记录/GD5F2GQ5UE_Trap_Records.md`。
|
||||
>
|
||||
> **2026-10-03 错误通道硬化**(E-10/E-42/E-53/E-54/E-56/E-57,见 `问题记录/GD5F2GQ5UE_Trap_Records.md` TRAP-GD5F-06):
|
||||
> `wait_busy` 区分"超时"(`GD5F_BUSY_TIMEOUT`)与"真故障"(`GD5F_ERROR`)、`bbt_scan` 失败**放弃持久化**、
|
||||
> `wait_busy` 区分"超时"(`GD5F_WAIT_EXPIRED`,旧名 `GD5F_BUSY_TIMEOUT`)与"真故障"(`GD5F_ERROR`)、`bbt_scan` 失败**放弃持久化**、
|
||||
> init 全链路检查返回值 + **回读 B0h 校验 ECC 使能**、`read` 的 raw 契约文档化、公开 API 统一 `int32_t`。
|
||||
> 套件回归 `=== GD5F2GQ5UE Tests: 16/16 PASSED (failed=0) ===`。
|
||||
> ⚠ **编排器注意**:`TC-GD5F-704` 会**预期**打出 `[ERR][NAND] RX DMA buffer not DMA-reachable`(回落轮询,用例 PASS)——
|
||||
|
||||
@@ -397,7 +397,7 @@ docs\内存管理\RAM使用分析与优化方案.md:585 [路径引用] docs/内
|
||||
|
||||
| 主题 | 去处 |
|
||||
|---|---|
|
||||
| 提交门禁 38 个钩子 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| 提交门禁 39 个钩子 | `docs/代码规范/pre-commit使用说明.md` |
|
||||
| 自动化测试与台架流程 | `docs/测试说明/自动化测试流程.md` |
|
||||
| 文档写作规范(编码/链接/登记) | `docs/文档规范.md` |
|
||||
| 工具路径环境变量(换机) | `docs/环境与部署/本地工具路径环境变量方案.md` |
|
||||
|
||||
@@ -504,7 +504,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
|
||||
|
||||
| 资源 | 已用 | 总量 | 利用率 | 余量 | 红线说明 |
|
||||
|---|---|---|---|---|---|
|
||||
| Flash(`ER_IROM1` = Code + RO) | 232,088 B(226.6 KB) | 1 MB | 22.1% | ≈804 KB | **充裕**:产品需求无 OTA / 双镜像预算,Flash 不是卡点;死代码裁剪簇已收口 → `docs/问题记录/项目审查与优化建议报告.md` §5.4 |
|
||||
| Flash(`ER_IROM1` = Code + RO) | 232,508 B(227.06 KB) | 1 MB | 22.2% | ≈804 KB | **充裕**:产品需求无 OTA / 双镜像预算,Flash 不是卡点;死代码裁剪簇已收口 → `docs/问题记录/项目审查与优化建议报告.md` §5.4 |
|
||||
| 主 SRAM1(静态区;FreeRTOS 堆已迁 CCM) | 75,264 B(73.5 KB) | 128 KB | 57.4% | 55,808 B | 已不紧张——2026-10-11 堆迁 CCM 与 2026-10-15 `LV_MEM_SIZE` 40→16 KB 两步的结果 |
|
||||
| CCM(`RW_CCM` 段) | 57,376 B(56.0 KB) | 65,280 B | **87.9%** | **7,904 B** | **当前真正的红线**:加任务 / 抬栈 / 加堆会先在这里撞墙;`mem-budget` 钩子在 92% 拦提交 |
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ if (ok) {
|
||||
- 公开 API 返回类型 `int` / `int32_t` 混用(码值均为小负数,无功能影响)。
|
||||
|
||||
**修复(2026-10-03)**:
|
||||
- `gd5f_wait_busy` 检查三次 HAL 调用,失败即返 `GD5F_ERROR`(与 `GD5F_BUSY_TIMEOUT` 区分)。
|
||||
- `gd5f_wait_busy` 检查三次 HAL 调用,失败即返 `GD5F_ERROR`(与 `GD5F_WAIT_EXPIRED` 区分;后者旧名 `GD5F_BUSY_TIMEOUT`,2026-10-23 按 S-5 R2 改名——返回码不许叫 `*_TIMEOUT`,因为同名形状里还有"毫秒数"和"自旋圈数"两种东西)。
|
||||
- `gd5f_bbt_scan` 返回错误码、每轮 `spare` 清零、读失败中止;`bbt_rebuild` 扫描失败
|
||||
**放弃持久化**(`gd5f_bbt_load()` 回滚 RAM 后直接返回,不 `bbt_save`)。
|
||||
- init 全链路检查返回值;init 后**回读 B0h 校验 ECC 使能(bit4)**,未使能即报错。
|
||||
|
||||
+71
-10
@@ -16,7 +16,7 @@
|
||||
| 维度 | 评级 | 今天的实测依据 | 相比 2026-10-15 的变化 |
|
||||
|---|---|---|---|
|
||||
| 结构与分层 | 🟢 良好 | 单向依赖未被破坏;双端注册反向校验 PASS;326 个编译单元全部可追溯 | 无退化 |
|
||||
| 文档与治理 | 🟢 良好 | 38 个提交钩子 + 16 套主机单测;工程 md 91 份;`doc-links` 全库 0 问题 | 新增 `编号门禁增量拦截` 与内存快照回填常态化 |
|
||||
| 文档与治理 | 🟢 良好 | 39 个提交钩子 + 16 套主机单测;工程 md 91 份;`doc-links` 全库 0 问题 | 新增 `编号门禁增量拦截` 与内存快照回填常态化 |
|
||||
| 内存与 Flash | 🟡 可控但已换位置 | SRAM1 **57.4%**(余 55,808 B)、Flash **22.1%**、**CCM 87.9%(余 7,904 B)** | **红线从 SRAM1 迁到 CCM**(堆 2026-10-11 迁 CCM + 池 40→16 KB 后 SRAM1 已宽松) |
|
||||
| 可靠性与自恢复 | 🟢 良好 | 4 个 Fault 全采集现场;IWDG 五面 `required_mask`;复位原因与 `[STACK]` 水位常开 | 台架复跑持续 PASS(今日真机 708 ms 起网、无 `[ERR]`/Fault) |
|
||||
| 测试与验证 | 🟡 半办 | 16 套主机单测 + `ui-golden`/`font-charset` 入门禁;TC 双向追溯 593∶271 交集 265 | **仍无覆盖率**、`coding-standard` **仍无 `malloc` 正则** |
|
||||
@@ -55,7 +55,7 @@
|
||||
`net_msg_queue_adopt()` 绑定(创建点不能外移——`osKernelInitialize()` 之前的创建类 API 会杀死启动;**故意不新建第二个队列**:那会白占 CCM 里 8×`net_msg_t`
|
||||
的堆空间,而 CCM 正是当前红线)。**两类从来不算债**:`stm32f4xx_it.c:241-256` 是 CubeMX「External variables」生成块;`:183/195/207/220` 是 `__asm` Handler
|
||||
里的汇编标签引用(ARMCC `__asm` 取不到 C 数据符号)。 | 无(后续由 `layer-deps` 与 `cubemx-survival` 两条门禁守着不回退:新增 `net-msgq-adopt` 存活项) |
|
||||
| 驱动规范 | S-5 ❌ | `bsp_err_t` 至今只出现在本报告里(无统一错误码/超时/重试约定) | 定义后进 `docs/代码规范/` |
|
||||
| 驱动规范 | **S-5 🟡 主体已落地**(2026-10-23:契约 + 门禁 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码) | 我原先写的"无统一错误码"**不准**,实测是 **六套并存**:0/−1(SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*`、**芯片寄存器原生码** `CH395F_ERR_*`(与前一套同模块并存)、`HAL_StatusTypeDef`(`rs485.h` 曾直接暴露)、`bool`,外加第三方 dhara 出参 `dhara_error_t` 与 FatFs `FRESULT`。超时侧是**同形两义**:`*_TIMEOUT` 里 100 是毫秒、`GD5F_SPI_TIMEOUT 100` 是**自旋圈数**、`GD5F/TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**;`osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU**(值同域不同,跨层转手不报错也不变慢)。真正值钱的不是码名,是"**能不能重试**"以前只写在个别注释里 | `Drivers/BSP/bsp_err.h`(header-only,含 `BSP_ERR_CAN_RETRY`)+ `bsp-err` 钩子(三条规则,基线 **19 → 17**);已收编 **RS485(HAL 枚举不再外泄)/ SD2506(11 个公共 API,I2C 超时终于归成可重试)/ TPAFE5160(含 E-44 的 FRSTDATA → `BSP_ERR_NO_DEVICE`)**,GD5F 的 `GD5F_BUSY_TIMEOUT` 改名 `GD5F_WAIT_EXPIRED` 消掉同形两义,FTL 坍缩点按 **ECC / 预算耗尽 / 完整性** 三类就地分类并打可读日志(§10.4)。`net_*` BSD 层与 `tpafe5160_snapshot()` 三态按裁定**不纳入**。**待还**:GD5F 私有码集整体收编(169 处、含板级套件 48 处,需真机 + 套件回归)、C4 失败类快照(让卷健康探针按类分支)、16 条缺「返回值:」注释 |
|
||||
| 网络 | N-09 / PHY ❌(N-25) | `NET_MAX_SOCKETS` 仍为 8(与单连接复用语义不符);PHY 强制 100M 无自协商回退;PASV 每次新建监听 | 见 §4.2;随 N-25 一并处置 |
|
||||
| 存储 | N-15 🟡(N-25) | `nand_ftl.o` 静态 6,216 B——三块 2 KB 级页缓冲并存未合并 | 与 R-3 同批评估 |
|
||||
| 存储/日志 | P2-10 ❌(N-25) | 卷锁现有 5 个使用者(配置原子写 / 日志 / SOE / lftpd_io / defaultTask),容量轮转删除与 UI 读并发仍未全覆盖 | 登记簿 S-5 面(卷锁覆盖面) |
|
||||
@@ -71,7 +71,7 @@
|
||||
- 体量:`MDK-ARM/STM32F407-Demo/STM32F407-Demo.map`(2026-10-22 `@build`)→ `data/ram_report_20261022.md`
|
||||
- 栈:`STM32F407-Demo.htm`(静态调用图)+ 同日真机 `[STACK]` 心跳 → `data/stack_report_20261022.md`
|
||||
- 仓库:`git ls-files` 逐个 `stat`(必须 `-z`,否则中文名被控制台代码页破坏)
|
||||
- 门禁:`.pre-commit-config.yaml`(38 个 `- id:`)+ 各钩子今日实跑输出
|
||||
- 门禁:`.pre-commit-config.yaml`(39 个 `- id:`)+ 各钩子今日实跑输出
|
||||
- 待办:`docs/任务规划/登记簿.md`(在策 18 条)与 `docs/任务规划/README.md` §2
|
||||
|
||||
### 1.2 复现命令(本报告所有数字可重跑)
|
||||
@@ -93,7 +93,7 @@ git ls-files -z | xargs -0 stat -c '%s'
|
||||
|
||||
| 名词 | 口径 |
|
||||
|---|---|
|
||||
| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **232,088 B(22.1%)**;`ROM 合计` 再加 `RW` 初始化表 = 233,276 B(22.2%) |
|
||||
| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **232,508 B(22.2%)**;`ROM 合计` 再加 `RW` 初始化表 = 233,696 B(22.3%) |
|
||||
| SRAM 占用 | `RW_IRAM1`(DMA 可达区)与 `RW_CCM`(仅 CPU)分开计,二者不可互换(规则 R2/R3) |
|
||||
| 最坏栈 | `max(armlink 静态调用图深度, 真机 [STACK] 高水位)`;判定线 `余量 ≥ max(1024 B ISR, 配置×30%)` |
|
||||
| 编译单元 | uvprojx 的 `<FilePath>` 计数(今日 **326 个 `.c`**,其中 LVGL 201);"自研 `.c`" 另按目录口径 114 个 |
|
||||
@@ -228,10 +228,10 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
|
||||
|
||||
### 5.1 实测基线
|
||||
|
||||
| 指标 | 2026-10-22 | 2026-10-15 | 说明 |
|
||||
| 指标 | 2026-10-23 | 2026-10-15 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `Code` | 213,436 B | 213,520 B | 当日累计:F-6 −332、SPI 回调 +16、消息队列 +32、FTP 日志级别 +28(RO 另 +72)、**LIST 满块续发 +172** |
|
||||
| `ER_IROM1`(Code+RO) | **232,088 B / 22.1%** | 232,280 B / 22.2% | 余 ≈803 KB |
|
||||
| `Code` | 213,648 B | 213,520 B | 10-23 S-5 全波 +212:契约试点 +48、C3 SD2506 迁移 +44、C1 GD5F/TPAFE 收编 +36、C2 dhara 失败类 +84(RO 另 +208 = 失败类名表);10-22 当日累计:F-6 −332、SPI 回调 +16、消息队列 +32、FTP 日志级别 +28(RO 另 +72)、LIST 满块续发 +172 |
|
||||
| `ER_IROM1`(Code+RO) | **232,508 B / 22.2%** | 232,280 B / 22.2% | 余 ≈803 KB;**RAM 三项 10-23 零增量** |
|
||||
| `RW_IRAM1` | **75,264 B / 57.4%**(余 55,808) | 73,568 B / 56.1% | 增量来自 B5/B8 功能码 |
|
||||
| `RW_CCM` | **57,376 B / 87.9%**(余 7,904) | 57,376 B / 87.9% | **当前唯一红线** |
|
||||
| FreeRTOS 堆 | 56 KB(57,344 B)@CCM;实测 `free=15,800` / `min_ever=8,464` | 同 | 2026-10-22 真机 |
|
||||
@@ -271,7 +271,7 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
|
||||
| **F-6** 双精度软浮点 | ≈1 KB | **归因错**:全库 0 处 `%f`;剩余 `dadd/dmul/ddiv/depilogue/dfixul` 1,018 B 锁在 MicroLIB `printfa.o` 的 `_printf_core → _fp_digits`,只要用 `snprintf` 就删不掉。但**根因侧确有可修点**:`App/algo/prot_set.h` 两个 Q10 宏写成 `double` | ✅ **已落地 2026-10-22**:改单精度 → `@build` −332 B、5 个 helper(300 B)消失;数值逐位等价(穷举 42 个字面量 + int32 全域 ÷1024 往返 4.49×10⁶ 例零差异);真机 `@test` PASS |
|
||||
|
||||
**当前可回收 Flash = 0 B**(F-4/F-6 已定论,图片链同理不打补丁)。LVGL 仍占镜像 **37.8%**
|
||||
(87,706 / 232,088 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。
|
||||
(87,706 / 232,508 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。
|
||||
|
||||
### 5.5 内存治理长效机制(常开,不受暂缓影响)
|
||||
|
||||
@@ -324,7 +324,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
|
||||
| 波次 | 本报告条目(今天状态) | 备注 |
|
||||
|---|---|---|
|
||||
| ~~W3 预算与内存~~ ⏸ | R-9 ✅ / R-2 后半、R-3、R-4、R-6 暂缓 | 暂缓期间无验收动作,观测照常 |
|
||||
| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;`bsp_err_t` 仍未进规范 |
|
||||
| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;S-5 契约/门禁已落地并收编三模块(基线 19→17,剩 GD5F 码集 + C4 快照) |
|
||||
| W5 验证常态 | 覆盖率、`malloc` 正则、TC 差异 13 处升门禁 | 逢窗口顺手做 |
|
||||
| W6 UI 扩展 | U2 残余、U5 页量 | 随产品需求,不再单列专项 |
|
||||
| W7 仓库决策 | D-1(N-29) | 已决策暂不处理;对外交付前重评 |
|
||||
@@ -383,7 +383,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
|
||||
| **S-2** | 跨模块裸 `extern` | ✅ **已闭环 2026-10-22**(24 处手写全消;两类本就不算债见 §0.4) | 本文变更记录 2026-10-22 三行;契约见 `App/task/task_handles.h`、`gd5f_spi1_notify_*`、`net_msg_queue_adopt` |
|
||||
| **S-4** | `app_cfg.h` 未接线占位开关 | ✅ **已闭环 2026-10-22**(4 个零消费点宏删除) | `App/app/app_cfg.h` 修改记录 v1.2;口径同步见 `../环境与部署/项目部署环境要求.md` §3.2/§3.5、`../产品需求/产品需求设计文档.md`、`../任务规划/排期详述.md` C5 |
|
||||
| **S-3** | `.ioc` 侧 USART1 配置 | ❌ 在策 | 登记簿 S-3 |
|
||||
| **S-5** | 统一 `bsp_err_t` 错误码约定 | ❌ 未做 | 本文 §0.4、`docs/代码规范/`(待建) |
|
||||
| **S-5** | 统一 `bsp_err_t` 错误码与超时命名约定 | 🟡 主体已落地(2026-10-23:契约 + `bsp-err` 钩子 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码;基线 19 → 17) | 本文 §0.4、`pre-commit使用说明.md` §2.27、`STM32F4-Base存储架构说明.md` §10.4、`Drivers/BSP/bsp_err.h` |
|
||||
| R-1 ~ R-9 | 内存优化项(R-9 已落地、R-5 判定不可行、其余随簇暂缓) | 见 §5.3 | 本文 §5.3、`../内存管理/RAM预算台账.md` |
|
||||
| F-1 / F-2 / F-3 | LVGL 死代码裁剪,省 64,576 B | ✅ 已落地 2026-10-11 | `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
|
||||
| **F-4 / F-5 / F-6** | Flash 双精度与死代码簇 | ✅/🟡/❌ 见 §5.4 | 本文 §5.4、`App/algo/prot_set.h` 注释 |
|
||||
@@ -442,6 +442,67 @@ admin/admin(SHA-256 `pwd_hash`)→ 该脚本**必然登录失败**;它是
|
||||
| | 数字联动照做:两份 `data/` 快照重跑、`ram_baseline --update/--write`、`check_mem_numbers --write`、FLASH 与
|
||||
任务栈两张配图重算重生成(逐文本复查越界 = 0)、本文 §1.3/§5.1/§5.4 与部署文档 §3.3 同步;§0.4 的 S-2 行按
|
||||
「完成即销项」改写、§9 拆出 S-2 行标 ✅。 |
|
||||
| **2026-10-23** | **S-5 第二波:C3 SD2506 整模块收编 + C1 GD5F/TPAFE 同形两义 + C2 dhara 失败类**(用户指定顺序 C3 → C1 → C2) | **C3**:`sd2506.h` 三枚私有宏(`SD2506_OK/ERROR/I2C_ERROR`)**删除而不是留别名**(留别名 = 新代码还能继续用旧形状,等于把迁移洗掉),11 个公共 API + 6 个内部 helper 改 `bsp_err_t`;新增 `sd2506_err_from_hal()`,**把 I2C 超时从"和没人应答同一个 −2"里拆出来归成 `BSP_ERR_TIMEOUT`(可重试)**——SD2506 是启动链第一站、I2C1 上电时序敏感,这是本波最实在的一处语义改善;
|
||||
`sd2506_init` 的 charge 寄存器**回读校验不符**原返 `SD2506_ERROR`(参数类)→ 改归 `BSP_ERR_IO`。连带迁移 `App`(`sys_clock.c`、`app_main.c`)、板级 RTC 套件、
|
||||
主机单测(`test/host/sys_clock/` 桩 + `run_unit_test.py` 补 `Drivers/BSP` include 路径)→ **单测 100/100**。 |
|
||||
| | **C1**:GD5F 的 `GD5F_BUSY_TIMEOUT` 改名 `GD5F_WAIT_EXPIRED`(**值 −2 不变**,纯消"名字叫 TIMEOUT 其实是返回码"),GD5F 私有码集**整体**收编**没做**——169 处引用里 48 处在板级套件、
|
||||
还要动 Trap 记录用词,得单独一波配真机回归,不夹带。TPAFE5160 收编(23 处):`BUSY_TIMEOUT → BSP_ERR_TIMEOUT`、`ERROR → BSP_ERR_PARAM`(**同值 −1,行为零变化**)、
|
||||
`FRSTDATA_ERR → BSP_ERR_NO_DEVICE`(E-44 的本义就是"芯片没驱动 DB 总线",归到**器件无响应**比留在私有 −3 有用,且避开与 `BSP_ERR_TIMEOUT` 撞值)。
|
||||
**`tpafe5160_snapshot()` 刻意不套成败码**:它 1=有新帧 / 0=无新帧 / −1=参数错,**0 不是错误**,是"这一拍没有新数据",强行收编会把慢消费者的正常路径变成失败。 |
|
||||
| | **也纠我第二次写错的一条**:我在 `bsp_err.h` 里断言"本层只有 `net_*` 一族把数量与错误挤在同一返回值"——`snapshot`/`adc_input_poll` 就是第二族,已把两族都写明并各注原因。 |
|
||||
| | **C2**:FTL 坍缩点(`ftl_flush_cache`/`ftl_read_page`)不再把 dhara 失败压成 `GD5F_ERROR`,按处置差异分三类并**就地**打日志(最早同时知道 LPN 与原始码的位置):`ECC/TOO_BAD →
|
||||
BSP_ERR_CRC`(页级,重试/换页)、`JOURNAL_FULL/MAP_FULL → BSP_ERR_NO_SPACE`(**块池见底,重格式化只是把问题推回去**)、其余 `→ BSP_ERR_IO`(完整性,保卷 +
|
||||
上报,不许自愈重格式化)——与 **E-31** 是同一条纪律的两端。可读名用自建短表:**本仓 dhara 没有编 `error.c`,`dhara_strerror()` 会直接链接失败**。`disk_read`/`disk_write`
|
||||
仍回 `RES_ERROR`(那是 FatFs 契约),要让 defaultTask 卷健康探针**按类分支**还差一个跨调用存活的快照 → **C4 待还**。口径 → `存储架构说明.md` §10.4。 |
|
||||
| | **又一次"凭猜测就会改错代码"的自查**:我一度认定 `DBG_ERROR("... err=%d", err)` 是把结构体塞进 varargs 的格式 bug,准备顺手修——**核过 `Lib/dhara/error.h`:`dhara_error_t`
|
||||
是 `enum`(标量)**,那行日志本来就合法。若没核实,我会把一条正常日志"修"成访问 `err.type` 而编译失败或语义错。 |
|
||||
| | **验收(这一波有真验证面,和 RS485 试点不同)**:`@build` **0 错 0 警告**(`Code` 213,648、RO 18,860、`ER_IROM1` **232,508 B / 22.2%**、**RAM 三项零增量**);`@test`
|
||||
PASS(681 ms)且逐条读到被改模块的锚点:`SD2506 init: OK (ret=0)` + ID/时间/温度/电池四条读回正常、`ADC init: OK`、`NAND init: OK`、`Map resumed: 14094/97504`、
|
||||
`adc=20803→28875` 帧计数持续递增(**证明 snapshot 三态语义没被改坏**)、`[ERR]/Fault/FAIL` 0 条;主机单测 100/100;`bsp-err` 基线 **19 → 17**、`--prune` 干净;
|
||||
`doc-links --all --sections-strict` 147 篇 0 问题;`layer-deps`、`id-check --strict`、`mem-numbers`、`mem-budget`、`task-table`、`ui-golden`、`registration-check` 全过。 |
|
||||
| | 口径与数字联动:`TPAFE5160/README.md`、`GD5F2GQ5UE_Test_Guide.md`、`GD5F2GQ5UE_Trap_Records.md`(旧名均留痕,便于对旧日志);`存储架构说明.md` **新增 §10.4**;
|
||||
`AGENTS.md` 与 `pre-commit使用说明.md` §2.27 的基线计数 19 → 17;配图桶按 .map 对象级增量大改(**dhara/FTL 10,931 → 11,222 = +291 全在 `nand_ftl.o`**、应用代码/UI
|
||||
+36 在 `app_main.o`、其余散量入"其他"7,557,桶和 = `Code+RO` 断言通过);`ram_baseline`/`check_mem_numbers --write`/`ram_report_20261023.md` 快照与本文
|
||||
§1.3/§5.1/§5.4、部署文档 §3.3 数字全部同步。**TPAFE5160/GD5F 两个对象的 Code 增量实测为 0**(纯改名,不产生一条指令)——这也是"改名不该被当成性能风险"的实证。 |
|
||||
| **2026-10-23** | **S-5 起步:BSP 错误码契约 + `bsp-err` 门禁 + RS485 试点**(钩子 38 → **39**) | 先纠本报告自己:原文「无统一错误码/超时/重试约定」**不准**,实测是**六套并存**——0/−1(SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*`、芯片寄存器原生码 `CH395F_ERR_*`(与前一套同模块并存)、`HAL_StatusTypeDef`(`rs485.h` 曾直接外泄)、`bool`,
|
||||
再加第三方 dhara 的出参 `dhara_error_t` 与 FatFs 的 `FRESULT`。超时侧是**同形两义**:`SD2506_I2C_TIMEOUT_MS 100`/`CH395F_SPI_BYTE_TIMEOUT_MS 100U` 是毫秒,`GD5F_SPI_TIMEOUT 100` 是
|
||||
**自旋圈数**(改 SPI 分频或换主频,时长跟着变且无人报警),`GD5F_BUSY_TIMEOUT` / `TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**;`osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU**
|
||||
(值同域不同 → 跨层转手既不报错也不变慢)。**契约真正值钱的是把「能不能重试」提到公共层**:
|
||||
以前它只写在个别注释里(`gd5f2gq5ue.c`:「GD5F_BUSY_TIMEOUT(-2) - 超时(可重试)」),
|
||||
语义在、跨模块不可判定。 |
|
||||
| | 新增 `Drivers/BSP/bsp_err.h`——**header-only、无 `.c`**(不占体积、不需双端注册):`0=BSP_OK`、
|
||||
11 个负值码、`BSP_ERR_CAN_RETRY(e)`、`BSP_WAIT_NONE/FOREVER/MS` 与边界纪律。边界纪律明确三件事:
|
||||
第三方码**就地映射不改名**(改名等于伪造语义);**`net_*` BSD 兼容层不纳入**(用户裁定:调用方
|
||||
要的就是「返回字节数 + 每任务 errno 槽」那个形状,模拟器 Winsock 后端同形状);**lftpd 按第三方
|
||||
排除**(它的 226/425/450/550 是 FTP 协议码,不是内部错误码)。 |
|
||||
| | **也纠我自己写错的一条**:我一度在 `bsp_err.h` 里把 `gd5f2gq5ue_read/write/erase` 当「计数或错误挤
|
||||
同一返回值」的反例——**核过实现与头注释,它们是「0=成功、其它=错误码」**,本层真正的 count-or-error
|
||||
只有 `net_*`(已裁定不纳入)。原文已改正,并把「数量走出参」降级为**面向新 API 的原则**。 |
|
||||
| | **试点 = RS485**:`rs485_transmit` / `rs485_receive_start` 不再外泄 `HAL_StatusTypeDef`,改 `bsp_err_t`
|
||||
+ 模块内 `static rs485_err_from_hal()` 映射(HAL 码不出模块),顺带补 `p_handle` 判空。
|
||||
`@build` **0 错 0 警告**(`Code` 213,436 → **213,484,+48 B**;RO 不变、**RAM 一字未动**)、`@test`
|
||||
PASS(702 ms)。**诚实标注**:这两个函数当前**没有生产调用点**(B2 冻结未接线),所以 `@test`
|
||||
只证明「没破坏启动」,**不证明新错误码在运行时被正确使用**——下一个试点该挑在启动链上的模块
|
||||
(建议 SD2506,41 处调用点)。 |
|
||||
| | **门禁 `bsp-err`**(`scripts/check_bsp_err.py`,三条规则:R1 不得拿 `HAL_StatusTypeDef`/`bool` 当错误
|
||||
通道、R2 `*_TIMEOUT` 宏不得取负值、R3 非 void 公开 API 必须有「返回值:」)。**规则改窄而不是
|
||||
拿基线洗规则**:第一版把 7 个正当谓词(`ui_..._is_active` / `has_children`)误判成违规,遂加
|
||||
`is_/has_/can_/valid` 白名单。存量 **19 条**钉基线、只减不增(两个 `*_BUSY_TIMEOUT = -2`、一个
|
||||
`bool ui_content_page_open()`、16 个缺注释)。**当场自证**:临时埋 3 条违规 → `命中 22(在册 19 /
|
||||
新增 3)`、**RC=1**,三条规则各自点名并给出路;删掉探针 → `19/19/新增 0`、RC=0。 |
|
||||
| | **一条实测负结果**(值得记):`__attribute__((warn_unused_result))` **在 ARMCC v5 上不生效**——
|
||||
属性挂上、调用点裸调不接返回值,构建仍 0 错 0 警告(第一次实验我误用 `(void)`,那本就是「故意
|
||||
丢弃」的写法,重做了一次)。所以「返回值必须被消费」只能靠门禁脚本 + 书写约定「故意丢弃必须
|
||||
写 `(void)`」。 |
|
||||
| | **pre-commit 的坑**:钩子跑之前会 **stash 未暂存改动**——我先把配置 `git add` 了但没加 rs485,
|
||||
钩子看到的树里 rs485 还是 `HAL_StatusTypeDef`,于是**假报警 2 条**。口径:改代码 + 同时更新基线
|
||||
时,**先把改动 `git add` 再跑钩子**。已写进 `pre-commit使用说明.md` §2.27。 |
|
||||
| | 口径同步:`AGENTS.md`(钩子数 + 枚举 + 索引行)、`pre-commit使用说明.md`(新增 §2.27 + 计数 +
|
||||
脚本清单行)、`并行任务工作树方案.md`、本文 §0.1/§0.4/§7 与 §9 的 S-5 行。数字联动(`ram_baseline
|
||||
--update/--write`、`check_mem_numbers --write`、FLASH 配图常量、本文 §1.3/§5.1/§5.4 与部署文档 §3.3)
|
||||
随本波提交一并做。**待还**:两个 `*_BUSY_TIMEOUT = -2` 改名(波及板级套件与 Trap 记录用词,需真机
|
||||
+ 套件回归)、FTL 坍缩点拆三类码(ECC / 介质 / 映射层坏块预算,让上层能区分「重试 / 标坏块 /
|
||||
别再 mkfs」)、其余模块逐个迁移。 |
|
||||
| **2026-10-22** | **S-1 只做「防扩大」那一半:新增 `layer-deps` 分层方向门禁**(推荐清单第 5 步拆分执行,钩子 37 → **38**) | 上一轮的结论是「反转暂缓、门禁现在就该上」,这次照做。
|
||||
为什么门禁比搬迁值钱:AGENTS 与分层图早就写着「六层单向依赖」,可从来没有机械检查——于是同一件事的记载从 2026-09 的
|
||||
「NETCFG 一处」漂到今天的「8 文件 9 处」,全靠人读代码才发现。边界的静默腐蚀只能靠门禁止住。 |
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""BSP 错误码与超时命名约定检查(报告 S-5 的机械检查那一半)。
|
||||
|
||||
为什么要有这个脚本:约定写在 docs 里不会自己生效。同一层的驱动今天并存六套返回约定,
|
||||
而"能不能重试"只写在个别注释里;同形两义的 *_TIMEOUT 已经出现三类(毫秒 / 自旋圈数 /
|
||||
其实是返回码)。没有机械检查,新代码只会继续按各自旧习惯长。
|
||||
|
||||
三条规则(只看 Drivers/BSP 下的公开头文件 = 对外 API 面;lftpd 按第三方排除):
|
||||
R1 公开 API 不得返回 HAL_StatusTypeDef / bool
|
||||
bool 把"为什么失败"整条丢掉;HAL 枚举把 HAL 焊进调用方的 include 链。
|
||||
R2 *_TIMEOUT 宏不得取负值
|
||||
名字叫 TIMEOUT 却是返回码,读代码的人会当成时长;超时应返回 BSP_ERR_TIMEOUT。
|
||||
R3 非 void 的公开 API 必须在紧邻注释块里写「返回值:」
|
||||
约定要能被读到才算约定,也才谈得上审查。
|
||||
|
||||
增量拦截(与 layer-deps / id-check 同构):BASELINE 钉住存量,只减不增,没有 --write。
|
||||
修完一条跑 --prune,它会点名"这条已消失,请从 BASELINE 删除"。
|
||||
|
||||
退出码:0 = 通过;1 = 有新增违规;2 = 用法或扫描错误。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
EXCLUDE = ("lftpd/",)
|
||||
|
||||
# 存量基线(2026-10-23 清点):"文件|规则|名字"。修掉一条删一行,不要往里加。
|
||||
BASELINE = frozenset(
|
||||
line.strip() for line in """
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_block_erase
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_check_ecc
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_page_read
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_program_exec
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_program_load
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_read_from_cache
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_read_status
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_wait_busy
|
||||
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h|R3|gd5f_write_enable
|
||||
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h|R3|nand_ftl_format
|
||||
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h|R3|nand_ftl_init
|
||||
Drivers/BSP/UI/ui_content_page.h|R1|ui_content_page_open
|
||||
Drivers/BSP/UI/ui_menu_tab.h|R3|ui_menu_tab_children
|
||||
Drivers/BSP/UI/ui_menu_tab.h|R3|ui_menu_tab_has_children
|
||||
Drivers/BSP/UI/ui_menu_tab.h|R3|ui_menu_tab_has_pwd
|
||||
Drivers/BSP/UI/ui_menu_tab.h|R3|ui_menu_tab_name
|
||||
Drivers/BSP/UI/ui_menu_tab.h|R3|ui_menu_tab_valid
|
||||
""".splitlines() if line.strip()
|
||||
)
|
||||
|
||||
DEFINE_RE = re.compile(r"^\s*#define\s+([A-Z][A-Z_0-9]*TIMEOUT[A-Z_0-9]*)\s+(-[0-9]+)",
|
||||
re.MULTILINE);
|
||||
# bool 用在"提问"上是合法的(is_editable / has_children / is_active…);只有拿 bool 当
|
||||
# **错误通道**才是问题。用名字形状区分,避免把 7 个正当谓词误判成违规、再拿基线去洗规则。
|
||||
PREDICATE_RE = re.compile(r"(^|_)(is|has|can|should|need|valid|active|open|empty)_|" +
|
||||
r"_(is|has|can|valid)$")
|
||||
DECL_RE = re.compile(r"^\s*(?:extern\s+)?([A-Za-z_][A-Za-z_0-9_ ]*[ ]+\**)"
|
||||
r"([a-z_][a-z_0-9]*)\s*\([^;{]*\)\s*;", re.MULTILINE);
|
||||
|
||||
|
||||
def tracked_headers():
|
||||
"""git 跟踪的 BSP 公开头文件(排除第三方 lftpd)。"""
|
||||
out = subprocess.run(["git", "ls-files", "--", "Drivers/BSP"],
|
||||
cwd=str(ROOT), capture_output=True, text=True)
|
||||
if out.returncode != 0:
|
||||
print("git ls-files 失败:", out.stderr.strip())
|
||||
raise SystemExit(2)
|
||||
files = []
|
||||
for rel in out.stdout.split():
|
||||
rel = rel.replace("\\", "/")
|
||||
if not rel.endswith(".h"):
|
||||
continue
|
||||
if any(part in rel for part in EXCLUDE):
|
||||
continue
|
||||
files.append(rel)
|
||||
return sorted(files)
|
||||
|
||||
|
||||
def doc_block_before(lines, idx):
|
||||
"""取声明上方紧邻的注释块文本(向上直到 /* 或空行为止)。"""
|
||||
buf = []
|
||||
j = idx - 1
|
||||
while j >= 0:
|
||||
t = lines[j].strip()
|
||||
if t.endswith("*/") or t.startswith("*") or t.startswith("/*"):
|
||||
buf.append(t)
|
||||
if t.startswith("/*"):
|
||||
break
|
||||
j -= 1
|
||||
continue
|
||||
break
|
||||
return "\n".join(buf)
|
||||
|
||||
|
||||
def scan():
|
||||
hits = []
|
||||
for rel in tracked_headers():
|
||||
try:
|
||||
text = (ROOT / rel).read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeDecodeError) as exc:
|
||||
print("读不了", rel, exc)
|
||||
raise SystemExit(2)
|
||||
lines = text.splitlines()
|
||||
for m in DEFINE_RE.finditer(text):
|
||||
hits.append((rel, "R2", m.group(1)))
|
||||
for m in DECL_RE.finditer(text):
|
||||
ret = m.group(1).replace("const", "").strip()
|
||||
name = m.group(2)
|
||||
flat = ret.replace(" ", "");
|
||||
if name in ("void", "return", "if", "while", "sizeof", "typedef", "static"):
|
||||
continue
|
||||
is_bool = flat.endswith("bool") or flat.endswith("_Bool")
|
||||
if flat.endswith("HAL_StatusTypeDef") or (is_bool and not PREDICATE_RE.search(name)):
|
||||
hits.append((rel, "R1", name))
|
||||
if flat == "void":
|
||||
continue
|
||||
idx = text[:m.start()].count("\n")
|
||||
if "返回值" not in doc_block_before(lines, idx):
|
||||
hits.append((rel, "R3", name))
|
||||
return hits
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="BSP 错误码/超时命名约定(增量拦截)")
|
||||
ap.add_argument("--verbose", action="store_true", help="列出全部命中与归类")
|
||||
ap.add_argument("--prune", action="store_true", help="只报已消失、可从 BASELINE 删除的条目")
|
||||
ap.add_argument("--emit-baseline", action="store_true",
|
||||
help="打印当前全部命中的键(建基线用)")
|
||||
args = ap.parse_args()
|
||||
|
||||
keys = {"%s|%s|%s" % h for h in scan()}
|
||||
|
||||
if args.emit_baseline:
|
||||
for key in sorted(keys):
|
||||
print(key)
|
||||
print("# 共 %d 条" % len(keys))
|
||||
return 0
|
||||
|
||||
gone = sorted(set(BASELINE) - keys)
|
||||
fresh = sorted(keys - set(BASELINE))
|
||||
|
||||
if args.prune:
|
||||
if not gone:
|
||||
print("[prune] 基线 %d 条全部仍在" % len(BASELINE))
|
||||
for key in gone:
|
||||
print("[prune] 这条已消失,请从 BASELINE 删除:", key)
|
||||
return 0
|
||||
|
||||
if args.verbose:
|
||||
for key in sorted(keys):
|
||||
print(" [%s] %s" % ("基线" if key in BASELINE else "新增", key))
|
||||
|
||||
print("BSP 约定:命中 %d(在册 %d / 新增 %d),基线共 %d"
|
||||
% (len(keys), len(keys & set(BASELINE)), len(fresh), len(BASELINE)))
|
||||
if fresh:
|
||||
print("[FAIL] 新增违反 BSP 约定的条目:")
|
||||
for key in fresh:
|
||||
print(" -", key)
|
||||
print(' 出路:① 返回 bsp_err_t(Drivers/BSP/bsp_err.h,0=成功、负值=失败,')
|
||||
print(' 可重试判定用 BSP_ERR_CAN_RETRY);② 时长宏一律 *_MS 且必须是时间,')
|
||||
print(' 自旋圈数改名 *_LOOP 并注释写明非时间量;③ 返回码不许叫 *_TIMEOUT。')
|
||||
return 1
|
||||
if gone:
|
||||
print("[提醒] 有 %d 条基线已还清,请用 --prune 列出并从 BASELINE 删除" % len(gone))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -5,3 +5,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
|
||||
2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
|
||||
2026-10-20 213356 18580 1184 131480 231936 22.1 75240 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
|
||||
2026-10-22 213436 18652 1188 131500 232088 22.1 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
|
||||
2026-10-23 213648 18860 1188 131500 232508 22.2 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
|
||||
|
||||
|
@@ -36,13 +36,13 @@ static void sd2506_phase_basic(void)
|
||||
DBG_INFO("=== SD2506 Phase 1: basic read/write (public API) ===");
|
||||
|
||||
/* TC-RTC-001: init + 读取当前时间 */
|
||||
int ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
bsp_err_t ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
|
||||
sd2506_time_t r;
|
||||
ret = sd2506_get_time(&r);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_get_time() ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
DBG_INFO(" current RTC: %04u-%02u-%02u %02u:%02u:%02u wk=%u",
|
||||
(unsigned int)r.year, (unsigned int)r.month, (unsigned int)r.day,
|
||||
(unsigned int)r.hour, (unsigned int)r.minute,
|
||||
@@ -59,11 +59,11 @@ static void sd2506_phase_basic(void)
|
||||
|
||||
t.year = 2026; t.month = 8; t.day = 28; t.hour = 12; t.minute = 0; t.second = 0; t.week = 4;
|
||||
ret = sd2506_set_time(&t);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_time() ret=%d (expect 0)", ret);
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_set_time() ret=%d (expect 0)", ret);
|
||||
|
||||
ret = sd2506_get_time(&r);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_get_time() after set ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
DBG_INFO(" set : %04u-%02u-%02u %02u:%02u:%02u",
|
||||
(unsigned int)t.year, (unsigned int)t.month, (unsigned int)t.day,
|
||||
(unsigned int)t.hour, (unsigned int)t.minute, (unsigned int)t.second);
|
||||
@@ -81,11 +81,11 @@ static void sd2506_phase_basic(void)
|
||||
#endif
|
||||
|
||||
/* TC-RTC-003: 走时。写后等 3s,应前进约 3 秒 */
|
||||
if (ret == SD2506_OK) {
|
||||
if (ret == BSP_OK) {
|
||||
osDelay(3000);
|
||||
sd2506_time_t r2;
|
||||
int ret2 = sd2506_get_time(&r2);
|
||||
if (ret2 == SD2506_OK) {
|
||||
bsp_err_t ret2 = sd2506_get_time(&r2);
|
||||
if (ret2 == BSP_OK) {
|
||||
int adv = (r2.second + 60 * (r2.minute + 60 * (r2.hour + 24 * r2.day)))
|
||||
- (r.second + 60 * (r.minute + 60 * (r.hour + 24 * r.day)));
|
||||
if (adv < 0) adv += 60 * 60 * 24 * 32;
|
||||
@@ -128,9 +128,9 @@ static void sd2506_phase_api(void)
|
||||
|
||||
/* TC-RTC-011: 内部温度读取 */
|
||||
int8_t temp = 0;
|
||||
int ret = sd2506_get_temperature(&temp);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_temperature() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
bsp_err_t ret = sd2506_get_temperature(&temp);
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_get_temperature() ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
DBG_INFO(" temperature: %d C", (int)temp);
|
||||
SD2506_TEST_CHECK(temp >= -40 && temp <= 85,
|
||||
"temperature in range [-40,85]: %d", (int)temp);
|
||||
@@ -139,8 +139,8 @@ static void sd2506_phase_api(void)
|
||||
/* TC-RTC-012: 电池电压(毫伏) */
|
||||
uint16_t volt = 0U;
|
||||
ret = sd2506_get_battery_voltage(&volt);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_battery_voltage() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_get_battery_voltage() ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
DBG_INFO(" battery: %u mV", (unsigned int)volt);
|
||||
SD2506_TEST_CHECK(volt > 2000U && volt < 5000U,
|
||||
"battery voltage plausible: %u mV", (unsigned int)volt);
|
||||
@@ -149,8 +149,8 @@ static void sd2506_phase_api(void)
|
||||
/* TC-RTC-013: 芯片 8 字节 ID */
|
||||
uint8_t id[8] = {0};
|
||||
ret = sd2506_get_id(id);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_get_id() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_get_id() ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
DBG_INFO(" id: %02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]);
|
||||
}
|
||||
@@ -161,10 +161,10 @@ static void sd2506_phase_api(void)
|
||||
uint8_t wbuf[8] = {0xA1U, 0xB2U, 0xC3U, 0xD4U, 0xE5U, 0xF6U, 0x07U, 0x18U};
|
||||
uint8_t rbuf[8] = {0};
|
||||
ret = sd2506_write_sram(0U, wbuf, 8U);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_write_sram() ret=%d (expect 0)", ret);
|
||||
if (ret == SD2506_OK) {
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_write_sram() ret=%d (expect 0)", ret);
|
||||
if (ret == BSP_OK) {
|
||||
ret = sd2506_read_sram(0U, rbuf, 8U);
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_read_sram() ret=%d (expect 0)", ret);
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_read_sram() ret=%d (expect 0)", ret);
|
||||
int sram_ok = (memcmp(wbuf, rbuf, 8U) == 0);
|
||||
SD2506_TEST_CHECK(sram_ok, "SRAM write==readback");
|
||||
}
|
||||
@@ -179,9 +179,9 @@ static void sd2506_phase_api(void)
|
||||
sd2506_time_t at;
|
||||
at.year = 2026; at.month = 8; at.day = 28; at.hour = 12; at.minute = 0; at.second = 30; at.week = 4;
|
||||
ret = sd2506_set_alarm(&at, (uint8_t)(SD2506_AL_EN_EAS | SD2506_AL_EN_EAMN));
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_set_alarm() ret=%d (expect 0)", ret);
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_set_alarm() ret=%d (expect 0)", ret);
|
||||
ret = sd2506_clear_alarm();
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_clear_alarm() ret=%d (expect 0)", ret);
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_clear_alarm() ret=%d (expect 0)", ret);
|
||||
}
|
||||
#else
|
||||
DBG_INFO("TC-RTC-015 skipped: destructive tests disabled (RTC alarm write)");
|
||||
@@ -202,12 +202,12 @@ void sd2506_test_run(void)
|
||||
"ENABLE_DESTRUCTIVE_TESTS in test_config.h to run them)");
|
||||
#endif
|
||||
|
||||
int ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == SD2506_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
bsp_err_t ret = sd2506_init();
|
||||
SD2506_TEST_CHECK(ret == BSP_OK, "sd2506_init() ret=%d (expect 0)", ret);
|
||||
|
||||
/* 信息:读取 CTR1 默认状态(确认 WRTC 初始/可写能力,纯诊断) */
|
||||
uint8_t c1 = 0x00U;
|
||||
if (sd2506_read_ctr1(&c1) == SD2506_OK) {
|
||||
if (sd2506_read_ctr1(&c1) == BSP_OK) {
|
||||
DBG_INFO(" CTR1=0x%02X (WRTC2=CTR1.2=%u WRTC3=CTR1.7=%u)",
|
||||
(unsigned int)c1,
|
||||
(unsigned int)((c1 >> 2) & 1U),
|
||||
@@ -217,7 +217,7 @@ void sd2506_test_run(void)
|
||||
#ifdef ENABLE_DESTRUCTIVE_TESTS
|
||||
/* 记录原始时间,测试结束恢复,避免把板子时间留在测试值 */
|
||||
sd2506_time_t orig;
|
||||
int have_orig = (sd2506_get_time(&orig) == SD2506_OK);
|
||||
int have_orig = (sd2506_get_time(&orig) == BSP_OK);
|
||||
if (!have_orig) {
|
||||
DBG_INFO(" WARN: cannot read original time; restore step will be skipped");
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ BUILD = TestBuild(
|
||||
src=os.path.join(HERE, "nand_port_test.c"),
|
||||
include_dirs=[
|
||||
os.path.join(HERE, "stub"),
|
||||
os.path.join(ROOT, "Drivers", "BSP"), # bsp_err.h(nand_ftl.c 依赖,2026-10-23 S-5)
|
||||
os.path.join(ROOT, "Drivers", "BSP", "GD5F2GQ5UE"),
|
||||
os.path.join(ROOT, "Lib", "dhara"),
|
||||
os.path.join(ROOT, "Lib", "FatFs"),
|
||||
|
||||
@@ -39,13 +39,14 @@ from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
|
||||
|
||||
# 顺序敏感:stub 目录必须**先于**生产目录,才能覆盖 main.h / FreeRTOS.h / task.h /
|
||||
# dbg_log.h(使 sys_clock.c 可在 PC 上编译)。sd2506.h 用真实头(其内部 #include
|
||||
# "main.h" 命中 stub,故无需桩),保留原汁的 sd2506_time_t 与 SD2506_OK。
|
||||
# "main.h" 命中 stub,故无需桩),保留原汁的 sd2506_time_t 与 BSP_OK(sd2506.h 现直接 include "bsp_err.h")。
|
||||
BUILD = TestBuild(
|
||||
name="System Clock Tests",
|
||||
src=os.path.join(HERE, "sys_clock_test.c"),
|
||||
include_dirs=[
|
||||
os.path.join(HERE, "stub"),
|
||||
os.path.join(ROOT, "App", "util"),
|
||||
os.path.join(ROOT, "Drivers", "BSP"), # bsp_err.h(sd2506.h 依赖,2026-10-23 S-5)
|
||||
os.path.join(ROOT, "Drivers", "BSP", "SD2506"),
|
||||
HERE,
|
||||
],
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
#include "dbg_log.h"
|
||||
|
||||
/*
|
||||
* sd2506_time_t 与 SD2506_OK 必须先可见(桩函数签名 / 阴影寄存器用它)。
|
||||
* sd2506_time_t 与 BSP_OK 必须先可见(桩函数签名 / 阴影寄存器用它)。
|
||||
* 真实 sd2506.h 内部 #include "main.h" 命中本目录 stub/main.h,故可安全使用;
|
||||
* sys_clock.h 提供被测 API 原型(与 sys_clock.c 内部 include 走同一 guard)。
|
||||
*/
|
||||
@@ -85,27 +85,27 @@ uint32_t HAL_GetTick(void) { return s_tick; }
|
||||
* - s_rtc_set_calls :写次数(观测 init/set 是否写回)
|
||||
*/
|
||||
static sd2506_time_t s_rtc;
|
||||
static int s_rtc_read_rc = SD2506_OK;
|
||||
static int s_rtc_write_rc = SD2506_OK;
|
||||
static bsp_err_t s_rtc_read_rc = BSP_OK;
|
||||
static bsp_err_t s_rtc_write_rc = BSP_OK;
|
||||
static int s_rtc_get_calls = 0;
|
||||
static int s_rtc_set_calls = 0;
|
||||
|
||||
int sd2506_get_time(sd2506_time_t *p_time) {
|
||||
s_rtc_get_calls++;
|
||||
if (s_rtc_read_rc != SD2506_OK) {
|
||||
if (s_rtc_read_rc != BSP_OK) {
|
||||
return s_rtc_read_rc; /* 故障注入:不动 p_time */
|
||||
}
|
||||
*p_time = s_rtc;
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int sd2506_set_time(const sd2506_time_t *p_time) {
|
||||
s_rtc_set_calls++;
|
||||
if (s_rtc_write_rc != SD2506_OK) {
|
||||
if (s_rtc_write_rc != BSP_OK) {
|
||||
return s_rtc_write_rc;
|
||||
}
|
||||
s_rtc = *p_time; /* 成功则阴影寄存器被更新(复读可见) */
|
||||
return SD2506_OK;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/* DBG_ERROR 计数 */
|
||||
@@ -203,8 +203,8 @@ static void tc_001_init_ok(void) {
|
||||
int rc;
|
||||
printf("TC-SYSCLK-001 init 正常(RTC 合法)\n");
|
||||
rtc_set(2026, 8, 24, 0, 0, 0);
|
||||
s_rtc_read_rc = SD2506_OK;
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_read_rc = BSP_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
clear_obs();
|
||||
s_tick = 12345u;
|
||||
|
||||
@@ -219,7 +219,7 @@ static void tc_002_init_leap(void) {
|
||||
printf("TC-SYSCLK-002 init 闰年换算\n");
|
||||
/* 2024 是闰年:2/29 存在 */
|
||||
rtc_set(2024, 2, 29, 12, 0, 0);
|
||||
s_rtc_read_rc = SD2506_OK;
|
||||
s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "2024-02-29 合法");
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(2024, 2, 29, 12, 0, 0),
|
||||
"2024-02-29 12:00:00 换算正确");
|
||||
@@ -241,15 +241,15 @@ static void tc_002_init_leap(void) {
|
||||
|
||||
static void tc_003_init_month_end(void) {
|
||||
printf("TC-SYSCLK-003 init 月末边界\n");
|
||||
rtc_set(2026, 1, 31, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
|
||||
rtc_set(2026, 1, 31, 0, 0, 0); s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "1/31 合法");
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 1, 31, 0, 0, 0), "1/31 换算");
|
||||
|
||||
rtc_set(2026, 2, 28, 23, 59, 59); s_rtc_read_rc = SD2506_OK;
|
||||
rtc_set(2026, 2, 28, 23, 59, 59); s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "2/28 合法(平年)");
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 2, 28, 23, 59, 59), "2/28 末端换算");
|
||||
|
||||
rtc_set(2026, 4, 30, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
|
||||
rtc_set(2026, 4, 30, 0, 0, 0); s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "4/30 合法(30 天月)");
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(2026, 4, 30, 0, 0, 0), "4/30 换算");
|
||||
|
||||
@@ -269,12 +269,12 @@ static void tc_004_init_epoch_bounds(void) {
|
||||
* Unix 0 经由 set 路径可达,见 tc_017(base_at(0) 等价)。
|
||||
* 这里验证 init 可接受的最早年月,以及 uint32 上限。
|
||||
*/
|
||||
rtc_set(2000, 1, 1, 0, 0, 0); s_rtc_read_rc = SD2506_OK;
|
||||
rtc_set(2000, 1, 1, 0, 0, 0); s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "2000-01-01 合法(可接受最早年)");
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(2000, 1, 1, 0, 0, 0), "2000-01-01 换算");
|
||||
|
||||
/* 2038 上限:2038-01-19 03:14:07 = 0x7FFFFFFF */
|
||||
rtc_set(2038, 1, 19, 3, 14, 7); s_rtc_read_rc = SD2506_OK;
|
||||
rtc_set(2038, 1, 19, 3, 14, 7); s_rtc_read_rc = BSP_OK;
|
||||
CHECK(sys_clock_init() == 0, "2038-01-19 合法(uint32 上限内)");
|
||||
CHECK_EQ_U32(sys_clock_get(), 0x7FFFFFFFu, "2038-01-19 03:14:07 → 0x7FFFFFFF");
|
||||
|
||||
@@ -292,8 +292,8 @@ static void tc_004_init_epoch_bounds(void) {
|
||||
|
||||
static void tc_005_init_rtc_read_fail(void) {
|
||||
printf("TC-SYSCLK-005 init RTC 读失败 → 降级默认时间并返回 -1\n");
|
||||
s_rtc_read_rc = SD2506_ERROR; /* 注入读故障 */
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_read_rc = BSP_ERR_IO; /* 注入读故障 */
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
clear_obs();
|
||||
s_tick = 777u;
|
||||
|
||||
@@ -305,7 +305,7 @@ static void tc_005_init_rtc_read_fail(void) {
|
||||
RTC_DEFAULT_MINUTE, RTC_DEFAULT_SECOND),
|
||||
"降级基准 = 编译期默认时间(墙钟仍可用)");
|
||||
CHECK(g_sclk_err_count >= 1, "打了 DBG_ERROR(RTC 异常)");
|
||||
s_rtc_read_rc = SD2506_OK; /* 复位注入 */
|
||||
s_rtc_read_rc = BSP_OK; /* 复位注入 */
|
||||
}
|
||||
|
||||
static void tc_006_init_out_of_range_selfheal(void) {
|
||||
@@ -316,8 +316,8 @@ static void tc_006_init_out_of_range_selfheal(void) {
|
||||
* (只有写回/复读仍失败才走降级 -1,见 tc_005 与 tc_020。)
|
||||
*/
|
||||
rtc_set(1999, 5, 5, 0, 0, 0); /* year < 2000 → 越界 */
|
||||
s_rtc_read_rc = SD2506_OK;
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_read_rc = BSP_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
clear_obs();
|
||||
CHECK(sys_clock_init() == 0, "year=1999 → 自愈写回成功 → 返回 0");
|
||||
CHECK(s_rtc_set_calls == 1, "越界触发一次写回");
|
||||
@@ -341,8 +341,8 @@ static void tc_020_init_selfheal_fail_degrades(void) {
|
||||
* rtc_ok 清 0 → 走降级分支(启用默认值时建基准 + 返回 -1)。
|
||||
*/
|
||||
rtc_set(1999, 5, 5, 0, 0, 0);
|
||||
s_rtc_read_rc = SD2506_OK;
|
||||
s_rtc_write_rc = SD2506_ERROR; /* 写回失败 */
|
||||
s_rtc_read_rc = BSP_OK;
|
||||
s_rtc_write_rc = BSP_ERR_IO; /* 写回失败 */
|
||||
clear_obs();
|
||||
s_tick = 42u;
|
||||
int rc = sys_clock_init();
|
||||
@@ -352,14 +352,14 @@ static void tc_020_init_selfheal_fail_degrades(void) {
|
||||
CHECK_EQ_U32(sys_clock_get(), ref_unix(RTC_DEFAULT_YEAR, RTC_DEFAULT_MONTH,
|
||||
RTC_DEFAULT_DAY, 0, 0, 0),
|
||||
"降级基准 = 编译期默认时间(墙钟仍可用)");
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
}
|
||||
|
||||
static void tc_007_init_corrects_rtc(void) {
|
||||
printf("TC-SYSCLK-007 init 越界 → 自动写回默认时间并复验\n");
|
||||
rtc_set(1999, 1, 1, 0, 0, 0); /* 越界,触发校时写回 */
|
||||
s_rtc_read_rc = SD2506_OK;
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_read_rc = BSP_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
clear_obs();
|
||||
int rc = sys_clock_init();
|
||||
CHECK(s_rtc_set_calls == 1, "越界 → 写回 RTC 一次");
|
||||
@@ -477,7 +477,7 @@ static void tc_016_set_writes_rtc(void) {
|
||||
sd.year = 2027; sd.month = 3; sd.day = 15;
|
||||
sd.hour = 6; sd.minute = 7; sd.second = 8; sd.week = 1;
|
||||
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
clear_obs();
|
||||
s_tick = 100u;
|
||||
CHECK(sys_clock_set(7000000u, &sd) == 0, "set 返回 0");
|
||||
@@ -489,11 +489,11 @@ static void tc_016_set_writes_rtc(void) {
|
||||
|
||||
/* 写失败:基准仍更新,但打错误日志 */
|
||||
clear_obs();
|
||||
s_rtc_write_rc = SD2506_ERROR;
|
||||
s_rtc_write_rc = BSP_ERR_IO;
|
||||
sys_clock_set(7000001u, &sd);
|
||||
CHECK_EQ_U32(sys_clock_get(), 7000001u, "RTC 写失败仍更新基准");
|
||||
CHECK(g_sclk_err_count >= 1, "RTC 写失败打 DBG_ERROR");
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
}
|
||||
|
||||
static void tc_017_set_no_rtc(void) {
|
||||
@@ -509,7 +509,7 @@ static void tc_018_set_unix_fields(void) {
|
||||
sd2506_time_t expect_week;
|
||||
uint32_t u = ref_unix(2026, 10, 2, 15, 30, 45);
|
||||
clear_obs();
|
||||
s_rtc_write_rc = SD2506_OK;
|
||||
s_rtc_write_rc = BSP_OK;
|
||||
s_tick = 0u;
|
||||
CHECK(sys_clock_set_unix(u) == 0, "set_unix 返回 0");
|
||||
CHECK(s_rtc_set_calls == 1, "写回 RTC 一次");
|
||||
|
||||
@@ -45,13 +45,13 @@ RAM2_CATEGORIES = [
|
||||
("free", CCM_FREE, "#E0E0E0"),
|
||||
]
|
||||
|
||||
# ── Flash breakdown (1MB = 1,048,576 B; 2026-10-22 @build) ──────────────────
|
||||
# ── Flash breakdown (1MB = 1,048,576 B; 2026-10-23 @build) ──────────────────
|
||||
FLASH_TOTAL = 1048576
|
||||
FLASH_CODE = 213436
|
||||
FLASH_RO = 18652
|
||||
FLASH_CODE = 213648
|
||||
FLASH_RO = 18860
|
||||
FLASH_RW = 1188
|
||||
FLASH_USED = FLASH_CODE + FLASH_RO + FLASH_RW # ROM 合计(含 RW 初始化表)
|
||||
FLASH_IROM = FLASH_CODE + FLASH_RO # ER_IROM1 区用量 = 232,088 B(22.1%)
|
||||
FLASH_IROM = FLASH_CODE + FLASH_RO # ER_IROM1 区用量 = 232,508 B(22.2%)
|
||||
FLASH_FREE = FLASH_TOTAL - FLASH_USED
|
||||
|
||||
FLASH_CATEGORIES = [
|
||||
@@ -61,19 +61,19 @@ FLASH_CATEGORIES = [
|
||||
("Flash 空闲", FLASH_FREE, "#E0E0E0"),
|
||||
]
|
||||
|
||||
# Flash Code+RO 分布(= ER_IROM1 232,088 B;2026-10-22 按 .map 对象表逐对象归类,
|
||||
# 口径同 data/ram_report_20261022.md;"其他"含链接器填充)
|
||||
# Flash Code+RO 分布(= ER_IROM1 232,508 B;2026-10-23 按 .map 对象表逐对象归类,
|
||||
# 口径同 data/ram_report_20261023.md;"其他"含链接器填充)
|
||||
FLASH_CODE_SUB = [
|
||||
("LVGL 库", 87706, "#4CAF50"),
|
||||
("应用代码/UI", 57011, "#2196F3"),
|
||||
("应用代码/UI", 57047, "#2196F3"),
|
||||
("CH395F+FTP", 25916, "#F44336"),
|
||||
("HAL 驱动", 17876, "#607D8B"),
|
||||
("dhara / FTL", 10931, "#FF9800"),
|
||||
("dhara / FTL", 11222, "#FF9800"),
|
||||
("FreeRTOS 内核", 10762, "#00BCD4"),
|
||||
("FatFS", 8468, "#9C27B0"),
|
||||
("C 库 (microlib)", 5486, "#795548"),
|
||||
("startup", 468, "#B0BEC5"),
|
||||
("其他 (长尾)", 7464, "#E0E0E0"), # remainder
|
||||
("其他 (长尾)", 7557, "#E0E0E0"), # remainder(2026-10-23 S-5 C3/C1/C2 的零散增量)
|
||||
]
|
||||
|
||||
# ── Task stack budgets (2026-10-22 @build + 同日真机 [STACK] 实测; data/stack_report_20261022.md) ──
|
||||
|
||||
Reference in New Issue
Block a user