2026-09-27 底板上板(未插刀片)发现: - HUB_RST 只在上电复位(ESP_RST_POWERON)时脉冲。原先每次重启都脉冲, esptool 经 USB 复位芯片即把自己的 COM 口断掉,只能走 J33 手动下载。 实测:USB 复位(原因 11)后日志「不动 HUB_RST#」,COM 口全程不掉。 - t_safety 空槽不走总线。原先五路无条件读,空槽 NAK 每次十几 ms, 槽 1 就吃光 10 ms 整圈预算,槽 2~5 与主干输入侧 0x43 一圈都没读到。 新用例 test_empty_slots_are_never_touched_and_the_input_still_reads(去掉修复即红)。 - 关掉 i2c.master 驱动日志:NAK 日志在持锁期间同步打印,拖垮 20 ms 节拍。 失败仍计入 t_safety 的 i2c_err。 - 上板结果:空机 skipped=0 i2c_err=0 过载 0 最迟 0 ms。 tools/i2c_scan:独立的只读扫描固件(主干 + mux 8 通道,读身份寄存器,不写任何输出)。 实测主干 0x22/0x23/0x43/0x4A/0x4B/0x70 全在,另有 0x47 应答(推测 DAC63204 广播地址)。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.3 KiB
6.3 KiB
交接
更新:2026-09-16 完成 R-0 ~ R-6(整个 R2 迁移,计划 v2.0 §6.2)
一句话现状
固件已经整体从 R1(单板 / 六片不同地址 INA226 / 74HC595 键链 / VBUS_EN 是 GPIO)
迁到 R2(底板 + 5 片同构刀片 / 全是 0x40 与 0x20 / TCA9548A 分通道 /
VBUS_EN 在刀片 PCA9555 的 P0.4 上)。19 个 ctest、351 个用例全绿,idf.py build 通过。
器件层与拓扑层全部换掉,判据阈值与时间常量一个没动(它们来自电路计算,R2 没改电路)。
2026-09-27 首次上板(底板,未插刀片)修的三处
app_main.cHUB_RST 只在上电复位(ESP_RST_POWERON)时脉冲。原先每次重启都脉冲 ⇒ esptool 经 USB 复位即断口,只能走 J33 手动下载。现在 USB 复位(原因 11)不碰 Hub,可直接经 USB 反复烧。t_safety.c空槽不走总线:原先五路无条件读,空槽 NAK 每次十几 ms,槽 1 就吃光 10 ms 预算, 槽 2~5 与主干 0x43 一圈都没读到(输入侧监控全瞎)。新用例test_empty_slots_are_never_touched_and_the_input_still_reads(去掉修复即红)。app_main.c关掉i2c.master驱动日志:它的 NAK 日志在持锁期间同步打印,拖垮节拍。- 上板实测:空机
skipped=0 i2c_err=0 过载 0 最迟 0 ms;I²C 扫描主干 0x22/0x23/0x43/0x4A/0x4B/0x70 全在, 另有 0x47 应答(推测 DAC63204 广播地址)。扫描固件在tools/i2c_scan/(独立工程,只读、不写任何输出)。
下一步
A12:命令处理器(语义 + 序号判重 + 限幅)。R2 多一条验收:
空槽(PRSNT# 为高)的命令一律拒(slot_present() 已备好)。
另外 A15 开工前先把启动序列从 app_main.c 拆成宿主机可编的函数 —— 见下面「还没有拦网的」。
本次新增/修改的文件
main/i2cbus.{h,c}新 — mux 原子事务:拿锁 → 选通道 → 跑回调 → 放锁;通道影子 + 失败作废main/slot.{h,c}新 — 一片刀片的 I/O 门面:路号 → mux 通道 → 影子下标只在这里换算一次components/drv/tca9548a.{h,c}/ch224a.{h,c}新 — I²C 八路多路器 / PD 受电(只读)main/t_thermal.{h,c}新 — 风扇闭环(一路 PWM 两只风扇 +FAN_EN总开关 + 两路 TACH)main/board_pins.h— 整表重写(R2 的 GPIO 与 R1 一个都不一样)components/drv/pca9555.{h,c}— 重写:P0.0~4 是输出、五实例影子、初始化顺序是安全本身components/drv/ina226.{h,c}— 五路同址 0x40 + 输入侧 0x43main/rail_cfg.{h,c}— 换列:+mux_ch+prsnt_n,−vbus_en;新增RAIL_SELFTEST_MUXmain/t_safety.{h,c}— 一圈改成「五次xact+ 主干一次」;+整圈锁预算;+mux 失联/在位两条判据main/t_actuator.{h,c}— 595 链 → PCA9555 Port0;本文件现在要拿 I²C 锁main/app_main.c— R2 启动顺序(含HUB_RST只脉冲一次、ina226_set_reversed(0x43,true))main/evlog.h— 末尾追加LOG_EV_MUX_LOST=14 / SLOT_EMPTY=15 / PD_LIMIT=16- sim:假 TCA9548A(关着的支路一律 NAK)、五实例假 PCA9555、假风扇
host/qtest.sh新 — 单个测试的快编快跑,迁移期并行开工用(DoD 仍认host/test.sh)components/drv/include/sr595.h— 顶部加「⛔ R2 已退役」横幅,代码与测试保留不动
验收命令与结果
$ bash host/test.sh
100% tests passed, 0 tests failed out of 19 # 351 个用例
$ .\build_idf.ps1
Project build complete. # phonerail.bin 0x3fa90,分区剩 75%
踩到的坑(只写会再犯的)
- ⛔⛔ PCA9555 的初始化顺序就是安全本身。 Output Port 复位值是 0xFF,
所以必须
OUTPUT=0 → POLINV=0 → CONFIG1 → CONFIG0——方向最后改。 反过来的那一刻 P0.0~P0.4 一起输出 1 = 三个键全按下 + EDL 短接 + VBUS 上电。 注错实测:把顺序调过来,33 条用例里只有 1 条变红,终态完全正确 —— 这就是为什么它在真板上查不出来。拦网是sim_pca9555_unsafe_enable()。 - ⛔
check_mux()的两段式不能拿「复位计数器和自己的快照比」做状态: 复位之后把后者同步成前者,两个数永远相等 ⇒ 每圈都走「先复位」分支,MUX_RST#每 20 ms 脉冲一次而MUX_LOST一次也不出。状态要单独存(mux_step)。 - ⛔ R2 一圈要拿六次锁(R1 只拿一次)。每次 5 ms 超时看着没问题,
六次加起来 30 ms > 20 ms 周期 ⇒ 总线繁忙时巡检成片丢拍,而每次
xact都按时返回了。 现在有T_SAFETY_LOCK_BUDGET_MS(10 ms)这条整圈的闸。 - ⚠️ 「断 VBUS 在锁内、关 eFuse 在锁外」这条,光断言「写的时候持着锁、通道对」拦不住:
把它挪到锁外之后
slot_vbus_enable()自己也会拿锁、也会选对通道,两条断言照样过。 真正的硬凭据是**「这一写发生在本圈第几次拿锁里」**(对 = 第 rail 次,错 = 第 7 次)。 - ⚠️
slot_present()读的是缓存,t_safety每圈要先slot_prsnt_poll()。 不 poll 的现象是「拔掉刀片永远判不出来」,而快照里那五个 bool 看着完全正常。 - ⚠️ 假总线上
hal_i2c_write_read()会让n_write和n_read各 +1, 所以「巡检有没有写刀片」不能看n_write,要看最后一次写的寄存器号。 - ⚠️
i2cbus_init()必定留下一次MUX_RST#低脉冲(先 config(OUTPUT) 再写 1), 测试里所有复位断言都要走增量。
还没有拦网的(不是遗漏,是已知边界)
- 启动序列一行也测不到:
app_main.c只进 IDF 目标。像ina226_set_reversed(0x43,true)这种「漏了就把正常供电判成反灌」的单行,现在唯一的拦网是人眼。A15 先拆函数。 - mux 通道整体循环错位(0→1→2→3→4→0)自检全过:五路互不重复、都不越界、都不等于 5。 现象是「每一路读到的都是隔壁那台手机」。唯一判据是上板 B1c 五槽逐一单插。
- 选通道「写下去不报错但没生效」:假器件写了就生效,宿主机造不出来。见计划 §11。
- 真并发:sim 单线程,
i2cbus_xact把「拿锁 + 选通道」绑成原子的价值只能间接证明。