# 交接 更新:2026-09-06 完成任务包 **A5**(74HC595 24 位链 + 影子寄存器单一写入点) ## 一句话现状 15 路侧键 + 5 路 `EDL_EN` + `HUB_RST` 全在一条 24 位链上,内存里只有一份影子, 唯一写入点是 `sr595_flush()`。位表**从 PCB 的真实焊盘网络逐脚核回来**(不是照文档抄), 上电顺序按「先锁存安全值、再使能 `OE#`」写死。7 条 ctest(61 个用例)全绿, `idf.py build` 通过。 ## 下一步 **A6**:PCA9555 驱动(需求 §10.4 末段)。验收断言:**方向寄存器永远不会把 Port0[7] / Port1[7:5] 配成输出**(配错直接 abort)—— 那 4 个备用脚在板上**直接接地** (设计文档 §17.2 的注),配成输出并输出高就是对地短路。 A3/A4/A5/A6 四个驱动包彼此独立,做完 A6 就能进 A7(通道配置表)。 ## 本次新增/修改的文件 - `components/drv/include/sr595.h` — 24 位链的位表、引脚、影子契约(唯一对外契约) - `components/drv/sr595.c` — 驱动本体(≈200 行,只经 `hal_gpio_*`) - `components/phonerail_hal/sim/sim_sr595.c` — **按 datasheet 建模的假移位链** (SRCLK 上升沿移一格、RCLK 上升沿锁存),加上 `sim_hal.h` 的一节声明 - `components/phonerail_hal/sim/hal_gpio_sim.c` — 加了一个窃听钩子给上面那个模型; `hal_gpio_config(OUTPUT)` 也会触发(真芯片配成输出就开始驱动,`OE#` 靠这一条) - `test/test_sr595.c`(15 个用例) - 三处 CMakeLists + `main/phonerail.h`(版本 `0.1.0-a5`) - `../需求文档.md` §10.6、`../五路直供电源板设计.md` §17.2 — 记落地 + **修掉过期的位表** (§8.2 那行过期的 DAC 通道分配也顺手改了) ## 验收命令与结果 ``` $ bash host/test.sh 100% tests passed, 0 tests failed out of 7 # +test_sr595(15) $ .\build_idf.ps1 # PowerShell phonerail.bin binary size 0x32550 bytes. ... 0xcdab0 bytes (80%) free. Project build complete. # sr595.c.obj 在;bin 与 A4 同样大是正常的(还没人调它) # 四个「一定会被写出来的错」逐个改进去跑过,确认测试拦得住: # 字节顺序发反(U21 先发) -> test_byte_order 红 # 先使能 OE# 再锁存安全值 -> test_outputs_are_enabled_only_after_a_safe_latch 红 # 安全影子写成 0(HUB_RST 摁死) -> 三条红,其中一条直接说「上电后 Hub 不能被摁在复位里」 # 位表改成 3(rail−1)+key 的算式 -> sr595_modify 的 assert 当场 abort(碰到空脚 bit4) ``` ## 踩到的坑(只写会再犯的) - **位表不能照 §17.2 抄** —— 那张表是 09-04 的两片版,§26.18 重排之后 15 路里 **12 路对不上**。这次是从 `.claude/pcb1-parts.json` 的真实焊盘网络逐脚核出来的 (顺手把 §17.2 和 §8.2 里过期的两张表都改了)。和 A4 的 DAC 通道分配同一类坑。 - ⚠️ **CH4 的三个键碰巧就是 bit 9/10/11**,和「顺排公式 `3(rail−1)+key`」一模一样。 所以「断言位表不是公式」只能**整体**断言(15 位里只有 3 位重合), 逐路写 `NOT_EQUAL` 到 CH4 就自己红了 —— 第一版正是这么写的。 - **`HUB_RST` 是低有效**(USB2517I 的 `RESET_N`,§21.7)。全 0 的上电影子会把 Hub 一直摁在复位里,而五路手机的电压电流全都正常 —— 看起来跟固件一点关系都没有。 - **上电顺序不能换**:三片 `MR#` 接死在 +3V3,没有复位线 ⇒ 上电时移位寄存器内容 不确定。所以 `KEY_OE#` 在安全值锁存完之前**根本不配成输出**(ESP-IDF 一配成输出 就开始驱动,输出寄存器复位值 0,等于当场使能)。板上 R609 100 kΩ 上拉扛着这一段。 - **假移位链值得写**:A5 的两条验收断言如果只对着驱动自己的影子变量断言,那是自己 证明自己 —— 字节顺序发反、每帧多打一个时钟、RCLK 打早了,影子变量一律看不出来。 `sim_sr595_*` 按 datasheet 建模,attach 时把移位寄存器塞成 `0xA5A5A5`(真板子上电 就是不确定值),四个注错进去的 bug 全被它照出来了。 - **cygwin 的偶发 `Subprocess aborted` 又来了一次**:这次是三条测试同时在 0.03 s 里挂, 其中两条根本不碰 595。重跑就正常。**别去查代码。** ## 这个驱动 / 这一层的几条约定(写 A9 / A12 之前看一眼) - **除 `sr595.c` 外,任何地方都不许自己去打 SER/SRCLK/RCLK。** 改按键或 EDL 一律是 「`sr595_modify()` 改影子 → `sr595_flush()`」。多处直接操作移位链的症状是 **另一台手机的按键被顺手清掉**,而且要两个动作正好撞在一起才复现。 - **组合键必须 `sr595_write(mask, value)` 一次给两位**(VOL− + PWR 落在同一帧)。 分两次写会先单按一个键。`RCLK` 之前输出不变,所以整帧是原子的。 - **不许拿 `OE#` 当「临时全松开」用**(需求 §10.6):那是复位路径不是控制路径, 所以 `sr595.h` 根本没提供抬 `OE#` 的接口。 - **rail 是 1~5**,`sr595_key_bit()` / `sr595_edl_bit()` 越界 abort; `sr595_modify()` 的 mask 碰到三个空脚(bit 4/8/12)也 abort —— 悄悄忽略的话那一位永远是 0,而「按键没反应」要到接上真手机才发现。 - **`sr595_map_ok()` 还没人调**:A7 的启动自检要把它和 `dac63204_map_ok()` 一起调上。 - **按键的保持时长(100 ms / 2 s / 10 s)与序列互斥还没做**,那是 A9 的活。 A5 只保证「写下去的那一帧是对的」。