/* 五路一张表(需求 §10.4)—— **禁止用通道号算任何东西**。 板上和「第几路」有关的编号有六组,**没有一组是顺排的,也没有两组是一致的**: 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 六组编号各有各的来历(I²C 地址按布线难度、595 位号按 PCB 几何、GPIO 按出线 方向),三条都在设计文档里:§8.2 / §17.2 + §26.18 C / §8.1。 ⚠️ **这一片写错了,板子照样正常工作** —— 只是「重启 CH1」重启了 CH4、 「读 CH3 电流」读的是 CH5。功能测试测不出来,五台手机跑起来也看不出来, 要等到某一路真出故障、而你关掉了另一路的时候才暴露。所以: **除了本文件里这张表,任何地方都不许出现 `0x40 + rail`、`rail - 1` 这类由通道号算出来的器件编号。** ---- 这张表和四个驱动里那四张表是什么关系 ---------------------------------- A3~A6 四个驱动各自带着自己那一张表(`ina226_rail_addr()` 等),因为驱动要能 单独测。本表是**把它们横着拼起来**的那一张,多出来的只有两列 GPIO (`EFUSE_EN` / `VBUS_EN` 是 GPIO 直连,没有驱动可挂)。 两处写同一个数就有分家的风险,所以 `rail_selftest()` 会**逐格**拿本表和四个 驱动的取值函数对账,对不上直接判失败(`RAIL_SELFTEST_TABLE`)。 宿主机 `ctest` 每次都跑,上板 `app_main()` 开机也跑一次。 —— 之所以不做成「开机时从驱动现算」,是因为那样**按定义就查不出分家**, 而且表就不是 const 的了,用之前得先记着初始化。 */ #ifndef RAIL_CFG_H #define RAIL_CFG_H #include #include #include "hal_err.h" #include "phonerail.h" #include "sr595.h" /* sr595_key_t:按键位那三格的下标就是它 */ #ifdef __cplusplus extern "C" { #endif typedef struct { uint8_t ina_addr; /* 0x40 0x41 0x44 0x45 0x42 —— 查表,不能算 */ uint8_t dac_addr; /* 0x4A: CH1/2/4 0x4B: CH3/5 —— 也不是「前三路在 U6」 */ uint8_t dac_ch; /* 该片上的 OUT 号(0~3) */ /* GPIO 直连,安全关键(§8.1「任何情况下都要能关电」)。 类型是 `int`,因为 `hal_gpio_*()` 收的就是 int;本表是唯一的出处。 */ int efuse_en; int vbus_en; /* 24 位移位链上的位号。下标用 `SR595_KEY_PWR/VUP/VDN`,别用 0/1/2。 */ uint8_t key_bit[SR595_KEY_COUNT]; uint8_t edl_bit; uint8_t flt_bit; /* PCA9555 **Port0** bit0~4 */ uint8_t pg_bit; /* PCA9555 **Port1** bit0~4 —— 两个端口长得一模一样,别搞反 */ } rail_cfg_t; /* rail 是 **1~5**(不是 0~4)。越界直接 abort:那是调用方算错了路号, 悄悄返回 CH1 的配置比崩掉危险得多。 */ const rail_cfg_t *rail_cfg(int rail); /* ---- 启动自检(需求 §10.4 的验收项) ------------------------------------ 全部是纯计算,不碰总线也不碰引脚,什么时候调都行。 返回 0 = 全过;非 0 是下面这些位的或。 */ typedef enum { RAIL_SELFTEST_INA = 1u << 0, /* ina226_addr_table_ok() 不过 */ RAIL_SELFTEST_DAC = 1u << 1, /* dac63204_map_ok() 不过 */ RAIL_SELFTEST_SR595 = 1u << 2, /* sr595_map_ok() 不过 */ RAIL_SELFTEST_PCA9555 = 1u << 3, /* pca9555_map_ok() / _addr_ok() 不过 */ RAIL_SELFTEST_TABLE = 1u << 4, /* 本表和四个驱动的表分家了 */ RAIL_SELFTEST_PINS = 1u << 5, /* EN 引脚重复 / 越界 / 撞上别的信号 */ } rail_selftest_bit_t; uint32_t rail_selftest(void); /* 只查**一张给定的**五路表(`RAIL_SELFTEST_TABLE` / `RAIL_SELFTEST_PINS` 两位; 另外四位是四个驱动自己的表,和 t 无关)。`t` 要指向 PHONERAIL_RAIL_COUNT 行。 存在的理由:**自检逻辑本身也得被测。** 拿真表跑一遍只能证明「现在没事」, 证明不了「真有事的时候查得出来」—— 而真表是 const 的,测试没法把它改坏。 所以判据单拎出来,测试喂几张故意写坏的表进去看它认不认。 */ uint32_t rail_selftest_table(const rail_cfg_t *t); /* 单个失败位的人话名字,用来打日志。传 0 或者多于一位返回 "?"。 */ const char *rail_selftest_str(uint32_t bit); /* ---- 两根 EN 线(表驱动遍历) -------------------------------------------- `EFUSE_EN` / `VBUS_EN` 都是**高有效**,而且都在板上带 100 kΩ 下拉 (R114 / R701~705)—— ESP32 复位期间 GPIO 高阻,五路输出全关、VBUS 全断。 极性只在 rail_cfg.c 的 `en_write()` 里出现这一次,上层一律传 bool。 */ /* 十个脚配成输出并**全部拉低**(= 五路全关、VBUS 全断),然后才谈别的。 ⚠️ 必须在 `rail_volt_init()` 之前调:DAC 上电那一刻输出就有电压了, eFuse 的 EN 这时得已经确定是低的。任何一个脚配失败就返回 HAL_ERR_ARG。 */ hal_err_t rail_en_init(void); /* rail 越界 → HAL_ERR_ARG;没 `rail_en_init()` 过 → HAL_ERR_ARG(HAL 会拦)。 */ hal_err_t rail_efuse_enable(int rail, bool on); hal_err_t rail_vbus_enable(int rail, bool on); /* 五路全关:**先断 VBUS 再关 eFuse**,中间不留「只靠 USB 供着电」的窗口。 十个脚全部试一遍,不因为某一个失败就提前返回 —— 关电路径上「剩下九个也别关了」 是最坏的选择。返回第一个错误码(全成功就是 HAL_OK)。 ⚠️ 这是复位 / 收尾路径。A10 的故障快路径要直接调 `rail_efuse_enable(rail,false)`, 别绕这里:过压时该立刻断的是出事那一路,不是等着遍历五路。 */ hal_err_t rail_all_off(void); #ifdef __cplusplus } #endif #endif /* RAIL_CFG_H */