A7:五路配置表 + 表驱动遍历 + 启动自检

六组编号(INA 地址 / DAC 片通道 / 三个按键位 / EDL 位 / 两根 EN 的 GPIO)拼成
一张 const 表 rail_cfg(1..5),没有一列是顺排的。开机 rail_selftest() 把它和
A3~A6 四个驱动的表逐格对账,再查全板引脚重复 / 越界,不过就 abort ——
那四个自检谓词至此全部有了调用点。

判据拎成 rail_selftest_table(t) 单独可调:真表是 const 的,不这么做就没法证明
「真有事的时候查得出来」。测试复制一张表出来逐样改坏,六种坏法都验过。

顺带修一个真 bug:HAL 两侧 GPIO 边界写的是 pin < 48,而 EFUSE_EN2 就在 IO48
上 —— CH2 的 eFuse 既开不了也关不掉。sim 与 ESP 两份都改成 0~48 含。

9 条 ctest(131 个用例)全绿,idf.py build 通过,16 条注错 15 条变红
(唯一没红的那条与原因记在 HANDOFF.md)。

Co-Authored-By: Claude Opus 5 <[email protected]>
这个提交包含在:
zhou yang
2026-09-07 07:13:04 +08:00
共同撰写人 Claude Opus 5
父节点 eb3df9ff49
当前提交 45de84ed3c
共修改 13 个文件,包含 1249 行新增和 63 行删除
+39
查看文件
@@ -433,6 +433,45 @@ typedef struct {
> 「有故障」会被读成「电源良好」,方向正好相反,而且**静息态两个端口读回来都是
> 全 1,单看一帧完全看不出来**。`test_one_rail_at_a_time` 专门拦这一手。
> **A7 已落地**(2026-09-07):那张 `rail_cfg_t` 表在 `firmware/main/rail_cfg.h` /
> `rail_cfg.c`,取值只走 `rail_cfg(rail)`(1~5,越界 abort)。六列的样子:
>
> | CH | INA226 | DAC | 按键位 PWR/VUP/VDN | EDL 位 | EFUSE_EN | VBUS_EN |
> |---|---|---|---|---|---|---|
> | 1 | 0x40 | U6.OUT2 | 1 / 2 / 3 | 16 | IO39 | IO40 |
> | 2 | 0x41 | U6.OUT0 | 19 / 17 / 18 | 7 | **IO48** | IO10 |
> | 3 | 0x44 | **U7**.OUT0 | 22 / 21 / 20 | 6 | IO6 | IO11 |
> | 4 | 0x45 | U6.OUT1 | 9 / 10 / 11 | 0 | IO41 | IO42 |
> | 5 | 0x42 | U7.OUT1 | 15 / 14 / 13 | 5 | IO8 | IO17 |
>
> 落地时定下来的五条:
> ① **本表与四个驱动那四张表是「两处写同一个数」,所以要对账而不是现算。**
> 开机自检 `rail_selftest()` 逐格拿本表和 `ina226_rail_addr()` 等取值函数比一遍,
> 对不上就报 `RAIL_SELFTEST_TABLE`。做成「开机从驱动现算」的话按定义就查不出分家,
> 而且表不再是 const 的、用之前得先记着初始化。
> ② **判据本身也要被测**:真表是 const 的,测试改不动它,所以判据单拎成
> `rail_selftest_table(t)`,测试喂几张故意写坏的表进去看它认不认
> (分家 / 引脚重复 / 撞 SDA / strapping / 不存在的号 / 越界,六种都验过)。
> ③ **两列 GPIO 是全固件唯一一处出处**,没有第二张表能对账 —— 所以
> 「`EFUSE_EN1` 和 `VBUS_EN1` 写反」这种错自检**查不出来**(两个都是合法空闲脚、
> 不重复、不越界)。唯一的拦网是 `test_en_pins_match_the_pcb_netlist` 里钉死的
> 十个值(出处:`.claude/pcb1-parts.json` 的 U20 焊盘网络)和 B1 上板实测。
> ④ **「不是顺排」要看整列,不能逐格撞算式**:CH4 的按键位正好是 9/10/11、
> CH1 的 EDL 位正好是 16、CH3 的两根 EN 正好等于「IO4 起 / IO9 起」那个算式 ——
> 单看任何一路都「像顺排」。测试改成断言整列**不是等差数列**。
> ⑤ ⚠️ **HAL 两侧的 GPIO 上限原来是 `pin < 48`,把 IO48 关在了门外**,
> 而 `EFUSE_EN2` 就在 IO48 上 —— 现象是 CH2 的 eFuse 既开不了也**关不掉**。
> 已改成 0~48(含)。ESP32-S3 的编号是连续的但芯片不是:IO22~IO25 不存在、
> IO26~IO32 是模组内部 flash/PSRAM,写进配置表 `gpio_config()` 不报错也不干活,
> 所以自检把这些号也当成越界。
>
> 同处还落地了**全板引脚表** `firmware/main/board_pins.h`(§8.1 那张表里
> 「全板独一份」的那些脚)与两根 EN 线的表驱动遍历
> (`rail_en_init()` / `rail_efuse_enable()` / `rail_vbus_enable()` / `rail_all_off()`)。
> `rail_all_off()` **先断五路 VBUS 再关五路 eFuse**,中间不留「手机只靠 USB
> 供着电」的窗口;十个脚全部试一遍,不因为某一个失败就提前返回。
> 高有效这件事只在 `en_write()` 里出现一次。
### 10.5 电压通道:DAC 只做微调,而且是反向的
| 事实 | 含义 |