A6:PCA9555 驱动 + 「接地脚永远是输入」的硬约束

U23 @ 0x20,16 个脚全部是输入:5×EFUSE_FLT# + 5×EFUSE_PGOOD + BUCK_PG
+ INA_ALERT + 4 个焊死在地上的备用脚(P0_7 / P1_5~7)。位表与地址从
.claude/pcb1-parts.json 的真实焊盘网络逐脚核回来,与设计文档 §17.2 一致。

驱动根本不提供「把某个脚配成输出」的接口(和 sr595.h 不提供「抬 OE#」同一条
规矩),方向字一律过 pca9555_dir_safe(),不安全直接 abort。原因是 PCA9555 的
Output Port 寄存器复位值是 0xFF —— 把接地脚配成输出的那一刻它就输出高电平,
而真器件既不报错也不 NAK,只是发烫。

三种极性(FLT# 低有效 / PGOOD 高有效 / INA_ALERT 低有效)各自只在一个函数里
出现一次;上层问「有没有故障」,不许自己去 & 位。

sim 侧新增按 datasheet 建模的假 PCA9555:Output 复位 0xFF、输入按位合成、
接地脚被驱动时记短路、INT# 只被「读输入端口」清除。为此给假 I²C 总线加了一个
窃听钩子(POST_WRITE / PRE_READ)—— 通用寄存器表模拟不了「配成输出就对地
短路」这类事,以后再有这种器件照这个挂。

验收:8 条 ctest(81 用例)全绿;idf.py build 通过,pca9555.c.obj 在。
12 个「一定会被写出来的错」逐个注进去跑过,全部变红。

Co-Authored-By: Claude Opus 5 <[email protected]>
这个提交包含在:
zhou yang
2026-09-07 06:28:11 +08:00
共同撰写人 Claude Opus 5
父节点 ab37f9dc49
当前提交 eb3df9ff49
共修改 12 个文件,包含 962 行新增和 59 行删除
@@ -28,7 +28,17 @@ void sim_hal_reset(void);
#define SIM_I2C_REGS 256
#define SIM_I2C_LOG_BYTES 8
typedef struct {
/* 假器件的窃听事件。通用寄存器表模拟不了「配成输出就对地短路」这种事,
所以按 datasheet 建模的假器件(sim_pca9555)挂一个钩子进来。 */
typedef enum {
SIM_I2C_EV_POST_WRITE = 0, /* 一次写事务刚落进寄存器表 */
SIM_I2C_EV_PRE_READ, /* 一次读事务即将取寄存器表 —— 在这里合成输入 */
} sim_i2c_ev_t;
struct sim_i2c_dev;
typedef void (*sim_i2c_hook_t)(struct sim_i2c_dev *d, sim_i2c_ev_t ev);
typedef struct sim_i2c_dev {
uint8_t addr;
uint8_t reg_width; /* 1 或 2 字节。2 字节按**大端**——INA226 / DAC63204 / LM75 都是 */
bool present; /* false = 器件失联(A15 降级矩阵用),事务一律 NAK */
@@ -39,6 +49,7 @@ typedef struct {
uint32_t n_read;
uint8_t last_write[SIM_I2C_LOG_BYTES]; /* 最后一次写进去的原始字节 */
uint8_t last_write_len;
sim_i2c_hook_t hook; /* NULL = 就是一张寄存器表 */
} sim_i2c_dev_t;
/* 往假总线上挂一个器件。地址重复或超出 16 个会 abort(测试写错了,不该悄悄过)。 */
@@ -91,6 +102,32 @@ uint32_t sim_sr595_enable_outputs(void);
/* 某一位在栅极上的实际电平;OE# 为高时返回 -1(高阻,不是 0)。 */
int sim_sr595_pin(int bit);
/* ---------------- PCA9555(按 datasheet 建模的假器件) ---------------- */
/* 通用寄存器表证明不了 A6 的核心判据。这个模型只多做三件真器件会做的事:
① **Output Port 复位值 0xFF** —— 所以把一个脚配成输出,它下一刻就输出高;
② 输入寄存器按位合成:配成输入的位读**外部电平**,配成输出的位读**自己的输出**;
③ 四个备用脚 P0_7 / P1_5~7 **焊死在地上** —— 读永远是 0,被驱动成高就是短路。
③ 是这块板子上「配错方向」唯一会留下痕迹的地方:真板子上它既不报错也不 NAK。 */
void sim_pca9555_attach(uint8_t addr);
/* 外部世界把某个端口的 8 根线驱动成 level(接地脚不受影响,恒 0)。
FLT#/PGOOD 都是开漏上拉,所以「什么都没发生」的静息值是 0xFF。 */
void sim_pca9555_drive(int port, uint8_t level);
uint8_t sim_pca9555_config(int port); /* 方向寄存器现值,1 = 输入 */
uint8_t sim_pca9555_output(int port); /* 输出锁存器现值(复位 0xFF) */
uint8_t sim_pca9555_polinv(int port);
/* 接地脚被配成输出过几次(不管输出高低,配了就算 —— 那已经是在对地灌电流)。 */
uint32_t sim_pca9555_gnd_driven(void);
/* 其中真的输出了高电平、也就是**直接对地短路**过几次。 */
uint32_t sim_pca9555_shorted(void);
/* INT# 现在是不是被拉低(低有效)。输入变化拉低,**读走输入端口才释放**。 */
bool sim_pca9555_int_asserted(void);
uint32_t sim_pca9555_int_asserts(void); /* 累计拉低次数 */
/* ---------------- PWM ---------------- */
int sim_pwm_pin(int ch); /* 未配置返回 -1 */