文件
phonerail/firmware/HANDOFF.md
T
zhou yang和Claude Opus 5 ab37f9dc49 A5:74HC595 24 位链 + 影子寄存器单一写入点
15 路侧键 + 5 路 EDL_EN + HUB_RST 在一条 24 位链上,内存里只有一份影子,
唯一写入点是 sr595_flush()。发送顺序 U33 → U22 → U21,每字节 QH 先发。

位表是从 .claude/pcb1-parts.json 的真实焊盘网络逐脚核回来的,不是照文档抄 ——
设计文档 §17.2 那张是 09-04 的两片版,§26.18 重排后 15 路里有 12 路对不上。
顺手把 §17.2 与 §8.2 里过期的两张表都改了。

sim 侧新增按 datasheet 建模的假移位链(SRCLK 上升沿移一格、RCLK 上升沿锁存),
只看线上真实发生的边 —— 对着驱动自己的影子变量断言等于自己证明自己。
四个注错进去的 bug(字节序反、OE# 先使能、安全影子写 0、位表写成算式)全被拦住。

上电顺序按「先锁存安全值、再使能 OE#」写死:三片 MR# 接死在 +3V3,
上电时移位寄存器内容不确定,先拉低 OE# 就是把随机值放到 15 个栅极上。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
2026-09-06 19:33:51 +08:00

77 行
5.5 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 交接
更新: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 只保证「写下去的那一帧是对的」。