/* PCA9555(U23,地址 0x20)—— 板上唯一的 I/O 扩展器,**16 个脚全部是输入**。 挂的是五路 eFuse 的 FLT#/PGOOD、Buck 的线或 PWRGD、六片 INA226 的线或 ALERT, 共 12 个信号 + 4 个直接接地的备用脚(设计文档 §17.2)。 ⚠️ **本驱动不提供「把某个脚配成输出」的接口,一个都不提供。** 四个备用脚 P0_7 / P1_5~7 在板上**直接接地、不串限流电阻**(§17.2 的注, 图纸上已标)。而 PCA9555 的 Output Port 寄存器**复位值是 0xFF**—— 所以只要把其中一个配成输出,它下一刻就输出高电平,**直接对地短路**。 配错既不会报错也不会 NAK,只会发烫。所以方向字一律走 pca9555_dir_safe(), 不安全直接 abort。这跟 sr595.h 不提供「抬 OE#」是同一条规矩。 §10.10 的降级行为:这片不应答 = 失去 FAULT / PG 可见性,**不关输出**, 改用 INA226 的电压判据兜底并持续告警。所以本驱动的读失败一律只是返回错误码, 不做任何动作。 本驱动只经 hal_i2c_*,宿主机上由 sim 的假 PCA9555(`sim_pca9555_*`)全部测完。 */ #ifndef PCA9555_H #define PCA9555_H #include #include #include "hal_err.h" #ifdef __cplusplus extern "C" { #endif /* ---- 地址与引脚 ---------------------------------------------------------- */ /* A0 = A1 = A2 = GND(U23 的 pin21 / pin2 / pin3,已按 PCB 焊盘网络核过)。 */ #define PCA9555_ADDR 0x20 /* `IOEXP_INT#`:开漏、低有效、板上 R608 10 kΩ 上拉,直连 ESP32 IO38(§8.1)。 ⚠️ **中断只是加速路径,不是主路径**:§10.3 的 `t_safety` 每 20 ms 无条件轮询 读一次两个输入端口。原因是 PCA9555 的 INT 要**读走输入端口才会释放**,而且 输入自己变回原值时它也会释放 —— 只靠中断的话,漏掉一次读就再也等不到下一个沿。 A6 只留这个宏,读它是 A8/A10 的活。 */ #define PCA9555_PIN_INT_N 38 /* ---- 寄存器(全部 8 位) ------------------------------------------------- */ #define PCA9555_REG_INPUT0 0x00 #define PCA9555_REG_INPUT1 0x01 #define PCA9555_REG_OUTPUT0 0x02 #define PCA9555_REG_OUTPUT1 0x03 #define PCA9555_REG_POLINV0 0x04 #define PCA9555_REG_POLINV1 0x05 #define PCA9555_REG_CONFIG0 0x06 #define PCA9555_REG_CONFIG1 0x07 /* 方向寄存器:**1 = 输入**(复位值 0xFF)。本板全输入,所以只会写这一个值。 */ #define PCA9555_DIR_ALL_INPUT 0xFFu /* 极性反转寄存器必须是 0。写成非 0 的现象是「五路同时报故障」或者更糟的 「五路故障全都看不见」,而 I²C 一切正常 —— init 会写 0 并回读校验。 */ #define PCA9555_POLINV_NONE 0x00u /* ---- 位分配(从 `.claude/pcb1-parts.json` 的真实焊盘网络逐脚核对) ------- Port0 bit0~4 = EFUSE_FLT1~5 bit5 = BUCK_PG bit6 = INA_ALERT bit7 = 接地 Port1 bit0~4 = EFUSE_PG1~5 bit5~7 = 接地 这一次两张表**确实是顺排的**(`bit = rail - 1`),和 INA226 地址、595 位号 那两张表不一样。所以这里最容易写出来的错不是「算式」,而是 **把 FLT 和 PG 的端口搞反** —— 两个端口的 bit0~4 长得一模一样, 搞反之后「有故障」会被读成「电源良好」,方向正好相反。 所以取位号一律走下面两个函数,不要在别处写 `1 << (rail - 1)`。 */ #define PCA9555_RAIL_COUNT 5 #define PCA9555_BIT_BUCK_PG 5 /* Port0,五路 Buck 的 PWRGD 线或(都是开漏) */ #define PCA9555_BIT_INA_ALERT 6 /* Port0,六片 INA226 的 ALERT 线或 */ /* 接地的备用脚。**永远、永远是输入。** */ #define PCA9555_P0_GND_MASK 0x80u /* P0_7 */ #define PCA9555_P1_GND_MASK 0xE0u /* P1_5 P1_6 P1_7 */ /* rail 是 **1~5**,越界 abort。返回的是**位号**(0~4),不是掩码。 */ int pca9555_flt_bit(int rail); /* Port0 */ int pca9555_pg_bit(int rail); /* Port1 */ /* 12 个信号位互不重复、都没落到接地脚上、FLT 与 PG 分属两个端口。 启动自检调一次(A7,和 ina226_addr_table_ok() / sr595_map_ok() 一起)。 */ bool pca9555_map_ok(void); /* 0x20 不与 INA226(0x40/41/44/45/42/43) / DAC63204(0x4A/0x4B) / LM75(0x48) 撞车。 */ bool pca9555_addr_ok(void); /* dir 里 1 = 输入。返回 false = 这个方向字会把某个**接地脚**配成输出。 驱动内部走到 false 直接 abort:悄悄放过去的后果是板子发烫,不是功能不好用。 */ bool pca9555_dir_safe(int port, uint8_t dir); /* ---- 采样 ---------------------------------------------------------------- */ typedef struct { uint8_t port0; uint8_t port1; } pca9555_in_t; /* 认件:读回两个方向寄存器,全 0xFF(复位值)才算是它。 不应答 → HAL_ERR_NAK;应答了但方向寄存器不是复位值 → HAL_ERR_BUS (这个地址上有东西,但不是一片刚上电的 PCA9555)。 */ hal_err_t pca9555_probe(void); /* 写方向(全输入)与极性反转(全 0)并**逐个回读校验**,回读不一致 → HAL_ERR_BUS。 ⚠️ Output Port 一个字节都不写:写它没有意义(没有输出脚),而写错方向的 后果见文件头。 */ hal_err_t pca9555_init(void); /* 巡检主路径:**一次事务**读两个输入端口(§10.3 的 0.12 ms 预算就是按一次算的; PCA9555 的读指针在 0x00 / 0x01 之间 toggle,这两个字节是 datasheet 保证的)。 */ hal_err_t pca9555_read_inputs(pca9555_in_t *out); /* ---- 语义判读 ------------------------------------------------------------ 四个信号**三种极性**,这是本文件第二个容易写反的地方。极性只在下面四个函数里 各出现一次(和 sr595_set_hub_reset() 里那个反相同一个道理:多一处就多一处写反 的机会)。上层一律问「有没有故障」,不许自己去 & 位。 */ /* TPS25940A 的 `FLT#`:开漏、板上 R113 47 kΩ 上拉 ⇒ **低 = 有故障**。 */ bool pca9555_rail_fault(const pca9555_in_t *in, int rail); /* TPS25940A 的 `PGOOD`:开漏、R115 47 kΩ 上拉,输出 2.97 V 以上才释放 ⇒ **高 = 电源良好**。 */ bool pca9555_rail_pg(const pca9555_in_t *in, int rail); /* 五路 Buck 的 `PWRGD` 线或(开漏)⇒ **高 = 五路都正常**;低只说明「有某一路 不正常」,看不出是哪一路(§16.1)。 */ bool pca9555_buck_pg(const pca9555_in_t *in); /* 六片 INA226 的 `ALERT` 线或:开漏、低有效 ⇒ **低 = 有片子在告警**。 */ bool pca9555_ina_alert(const pca9555_in_t *in); #ifdef __cplusplus } #endif #endif /* PCA9555_H */