原理图/PCB 已在 EasyEDA 里做到 12 页跨页 0 漏接,固件刚完成骨架包 A1。 这次是把已有的一切一次性纳入版本控制,没有改动任何设计内容。 纳入: - 设计与需求文档、PROGRESS.md、小米8 参考原理图 PDF 与追脚脚本 - .claude/ 下的 EasyEDA 桥接工具链与几何/网表缓存(eda-parts.json、 pcb-parts.json、pcb-pads.json、p2-ch1.json 等,是重画各页的唯一事实来源) - 117 张验证截图(本项目所有方向问题都是靠人眼看截图发现的,属于证据不是垃圾) - firmware/ 的 A1 骨架与 site/ 文档站源码 .gitignore 排除:构建产物(firmware/build、host/build、sdkconfig)、 site/node_modules、__pycache__、桥接日志 _bridge.log、脚本自标的 *.stale。 .gitattributes 默认 `* -text`:现有 .py/.json 大多是 CRLF,原样存, 不让 git 重写行尾。唯一例外是 *.sh 强制 LF —— 带 \r 的 shell 脚本 bash 跑不了。 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
191 KiB
进度与交接
更新:2026-09-06 第二十四轮(I²C 干线改走 Inner2)。 详见设计文档 §26.20(上一轮是 §26.17 / §26.18 / §26.19)。
⭐ 新会话从这里开始
一句话现状
原理图 11 页全部验证通过。
下板 PCB1:摆位 342 件、扇出、15 块铺铜、五路功率级 195 段全部下发并回读过。
这一轮把 I²C 的跨板长干线挪到了 Inner2(302 mm 内层,§26.20)——
目的是腾外层通道给布不通的线,然后立刻跑了第三遍信号布线(--ripup)验证效果:
| 挪之前 | 挪之后 + 第三遍布线 | |
|---|---|---|
| 已连通网络 | 148 | 163 |
| 还欠连接 | 87 条 / 74 网络 | 68 条 / 59 网络 |
| DRC 连接性错误 | 1044 | 855 |
| 板上铜 | 3196 线 / 1000 孔 | 3516 线 / 1084 孔 |
铺铜已重铺回读(Inner2 浮铜从 1312.8 mm² 降到 0.2 mm²,剩的那点是新布线蹭出来的碎屑)。
⬅️ 下一步
⛔ 那 74 条 DRC 间距错误的真因已查清(§26.21):漏跑 pcb1_parts_dump.py。
§26.18 重排的 44 个焊盘导进 PCB 了,但缓存没重抓 ⇒ 后面两轮布线里
33 个网络的铜连的是错信号。已拆掉那 530 段 / 2482 mm / 138 孔并重布(跑完对一遍 DRC)。
⚠️ 规矩:pcb1_import.py --apply 之后下一条命令就是 pcb1_parts_dump.py。
中间插别的操作,后面全部几何和布线都建在沙子上,而六道自检一条都不会响
(它们读的是同一份缓存,自洽得很)。
期间走过两条弯路,都记在 §26.21 末尾:多边形焊盘不许按包围盒判 (QFN 散热盘的盒子会套住邻脚,凭空报 21 处假「重叠」); DRC 的「间距」那一支不陈旧(重载前后逐字相同,不能当旧数据糊弄)。
剩下 68 条连接性(59 个网络,大多各欠 1~2 条)。还能腾外层的两个大头:
| 候选 | 现状 | 说明 |
|---|---|---|
BUCK_PG |
已连通,顶层 234 mm、跨 121 mm | 剩下最大的一块外层占用,挪进 Inner2 能再腾一大片 |
INA_ALERT |
欠 2 条 | 试过,没跑通:铜整片在顶层且挤在控制块,主碎片周围一个合法孔位都没有 |
USB_DP/DM |
各欠 2 条 | 不许进内层(90 Ω 以 L2 为参考,§10.1),只能在外层解 |
常规循环(一步都不能省):
/c/Python314/python -u .claude/pcb1_route_sig.py --refresh --ripup --push
/c/Python314/python .claude/pcb1_pour.py --push --refresh
/c/Python314/python .claude/drc_read.py
⚠️ pcb1_route_sig.py --ripup 认「哪些铜是自己画的」靠覆盖判据,不是坐标比对 ——
pcb1_i2c_l3.py 画的 Inner2 干线和 pcb1_gnd_stitch.py 的 6 个缝合孔它都不认得。
2026-09-06 实测跑完一轮 --ripup 之后 55 段内层线一段没丢,但这不是保证 ——
跑完对一遍 _i2c_l3_snapshot.json(或 pcb1_i2c_l3.py 不带参数跑一遍数碎片)。
⚠️ 引擎的层数已经不是写死的 2 了(2026-09-06 改):pcb2_route_sig.Field 现在按
障碍表里真正出现过的层建数组、viafree 取全层最小值。不这么改的话,板上一有
layer 16 的走线就 KeyError: 16 当场崩;就算滤掉,它打的新过孔也不会对内层干线
做净距判断(旧的 viafree 只看 Top/Bottom),孔会直接压在内层线上。
⚠️ pcb1_route_pwr.py --push --verify 是只 verify,不下发 ——
main() 里 --verify 在 --push 之前 return,而它照样打「✅ 全部校核通过」,
只在最后一行回读里露馅(「195 段里没被实测铜盖住的 195 条」)。要下发就只写 --push。
⚠️ pcb_fanout.py --clean 会把板上全部走线删光,包括 pcb1_route_pwr 的 195 段。
所以「改了扇出」= 扇出三步 → 重铺铜 → 重跑 route_pwr → 再 route_sig,一步都不能省。
⏱ 实测速率:route_sig 前两个网络(SDA/SCL,各挂 14 个焊盘横跨整板)要 1516 s,
之后 5 个网络只要 68 s。别拿头几个网络外推总时长;全程约 60~90 分钟。
它是跑完才一次性下发的 —— 中途停掉板上一根铜都不会变,但也等于全白跑。
✅ 第二十四轮(2026-09-06,用户拍板):I²C 干线改走 Inner2(§26.20)
SDA/SCL 是全板最长的两个网络(各 14 个焊盘、横跨整板),
既吃布线时间(1516 s)又占着外层最好的通道。用户拍板:走内层,只挪跨板长干线。
新工具 .claude/pcb1_i2c_l3.py(三层迷宫布线 Top / Bottom / Inner2)。
| 改之前 | 改之后 | |
|---|---|---|
| I²C 总铜长 | 596 mm,内层 0 | 609 mm |
| Inner2 | — | 302 mm |
| 顶层 / 底层 | 596 mm | 157 / 150 mm |
| 连通性 | SCL 3 片 / SDA 4 片 | 一模一样 |
中桥那 13 mm 是唯一留在外层的一段:12V-SW+BR 的中桥(x 61.5~72.6,喂 3.6 A)
是 Inner2 上东西两半唯一不能割的隔断,I²C 过去就必须横着割 ⇒ 只能外层跳。
(上一轮 route_sig 给 SDA 布的就是这个跳法,殊途同归。)
三条这一轮踩到的坑(细节全在 §26.20):
PEN_3V3决定的不是长度,是平面会不会被切出孤岛。 1.8 的时候两根线在3V3-SW里围出 7.2 mm² 孤岛,里面有 R415.2 的 +3V3 焊盘 —— 浮铜检查 / 焊盘覆盖检查 / 连接性 DRC 三样全都不响,只有数连通域看得见。 堵孤岛重绕不收敛(7.2 → 25.4 → 26.8 → 32.5 mm²)。改成 4.0: 干线走 GND 底填,只在器件旁边扎盲端进平面。expand()会静默丢掉最后一点上的过孔 —— 引擎_end_via的注释早写着,又踩一遍。own(本网铜放行)要按半线宽内缩,过孔则根本不吃这条放行。
顺带修掉一块和这次无关的浮铜:板子南沿 y 0.59 那条 GND 底填(1312.8 mm²)
在 Inner2 上是孤立的,一个落脚点都没有 —— 它原本靠 x 115116 那条 1.0 mm 缝接出去,
被上一轮信号布线的三个过孔的反焊盘掐断了。新脚本 .claude/pcb1_gnd_stitch.py
在南沿补了 6 个 GND 缝合孔。⚠️ 这一条只有铺铜下发后 verify() 回读实测铜才量得出来,
下发前的 report() 栅格不含别的铺铜,算出来 GND 是一整块。
验收:净距/拐角/孔距 ✅ · 平面完整性 ✅(+12V 1 块 / +3V3 2 块)·
pcb1_pour.py --push --verify ✅ 实测和计划对得上 · DRC 里没有一条牵涉 SDA/SCL。
✅ 这一轮 C(2026-09-06):8 A 的源只有 3 个过孔(§26.19)
第一次跑信号布线跑到 35/196 中止 —— 不是布线器的问题,是它报出来的失败把两个上游问题照了出来。
Rs6.2→ +12V 平面只有 3 个 Ø0.3 过孔 ≈ 3~4.5 A,而它要过 8 A。 它是全板 +12V 平面唯一的源。这一条四道验证一条都不管:铺铜断面只量平面本身、 扇出只报「孔放下了没有」、连接性 DRC 只看「连上没有」、逃逸审计只看「出得去吗」。 ⇒pcb_fanout.py新增FORCE_ARRAY(整片阵列钉死,逐个仍走全套净距校核), Rs6.2 钉 12 个孔(≈12 A),摆在焊盘正南那片空地。 规矩:>4 A 的「焊盘 → 平面」转换点,过孔数必须手写。- 顺带把 U13 的 I²C 放了出来。 原来那 3 个孔有两个甩到西边,两根 1 mm 引线
(端帽再往西探 0.5)压死了 U13 南排五个脚唯一的通道;
VINSW-TOP北柱又只差 0.379 mm。 12 个孔改朝南 + 北柱在 y>87 收窄到 x 1.55.3(3.8 mm → 8.4 A), U13 南边变成一整片 x 5.514 / y 91.6~96 的空地。 - 扇出不对称又发作了一次:
C%06.2的 GND 孔在 rot 0 的三路往东落到局部 (10.02,27.10)、 在 rot 180 的 CH2/CH3 往西落到 (6.75,27.10),正压在 eFuse 脚 14/15 的出线口上。 已加进_CH_FORCE_LOCAL。 - 剩下 10 个「被围死」不是板上的铜 —— 报出来的挡路物去
pcb-copper.json里查不到, 是审计自己给邻脚预留的出线段。判据就是这个:查得到才是真障碍。
✅ 这一轮 A(2026-09-06):v11 的两行排法把 8 A 输入链卡死了,已修(§26.17)
§26.16-E 换 XT30 之后把 N-L 块改成两行,摆位的本地检查六项全过,
可是回去重画 §26.11 的顶层宽铜时第一刀就下不去:
VIN_RAW 从 J1 出来根本没有 3.49 mm 的路 —— XT30 rot 270 之后 GND 在北针 (13.5,74.5)、
VIN 在南针 (13.5,69.5),而 F1 卧着放和 GND 针同一行,中间又被 D1(17.5,69.5) 堵住,
实测通道只剩 1.674 mm(1 oz 外层约 4.5 A,这里要 8 A)。
这一类错误几何检查一条都报不出来 —— 不重叠、不越框、高度也够。 抓到它的是「照新坐标把顶层宽铜的多边形重推一遍」这个动作本身。 ⇒ 摆位改完不能只看本地检查,必须把 §26.11 那三块多边形重推一次。
三处改动(pcb1_place.py 的 N-L 表):
| # | 改动 | 拿回了什么 |
|---|---|---|
| ① | F1 卧 → 竖(rot 0 → 90),(17.5,74.5) 不动 | F1.1 落到 (17.5, 73.06),正对 J1.2 那片空地 |
| ② | D1 (17.5,69.5) → (31.0,74.5) rot 270 | 通道里最后一个障碍搬走,TVS 贴到漏极铜东缘 |
| ③ | C4/C5/C6 x 25 → 30.5,rot 0 → 180 | 让出 y 78.85~83.5 那条 4.64 mm 的 VIN_SW 西行干道 |
顶层三块宽铜重画后实测断面全过:RAW 颈 3.50(要 3.49)、FUSED 5.25、SW 干道 4.75、北柱 5.25 mm。
面积 59 / 30 / 263 mm²,各 1 个连通域,check_top_clearance() 零侵入。
⚠️ 顺带抓到一个静默失效:Rs6.2 一个过孔都没打。
12V-N 为了让开 U13 的 Kelvin 脚,在西边咬了个缺口 (0.5,93.0)-(12.5,101.5),
而 Rs6.2 的焊盘中心 (9.57,93.50) 正落在缺口里 ⇒ 扇出判成「不在铜面上」,
全板唯一的 8 A 源不接电源平面。--push 照样报「失败 0 个」,连接性 DRC 也不响
(+12V 靠别的 55 个焊盘照样连通,只有源那一端是断的)。缺口收成 (0.5,95.6)-(12.5,102.4)。
✅ 这一轮 B(2026-09-06,用户拍板):三组引脚按几何重排(§26.18)
「哪个信号落在哪个物理脚」有三处是自由的,而原来全是按逻辑顺序排的, 和它们在板上要去哪儿一点关系都没有。目标函数用绕开芯片本体的直角最短路 (HPWL 量不出「东边的脚要去西边」有多贵:同一批改动 HPWL 说省 7%,绕障路长说省 15%)。
| 组 | 结果 | 挪了几个 |
|---|---|---|
| ESP32-S3 U20 22 个信号 | 1195 → 1020 mm(−15%),绕模块的线 3 根 → 0 | 12 |
| DAC63204 U6/U7 5 个通道 | 422 → 352 mm(−17%),一根线都不用新画 | 4 |
| 74HC595 U21/U22/U33 21 个位 | 1286 → 1141 mm(−11%),每路三键成束 | 17 |
合计省 约 390 mm。三处关键决定:
- 放开 IO39~IO42(原留给引脚 JTAG)—— 西边从 4 个坑变 8 个。
调试本来就走原生 USB-Serial-JTAG(IO19/IO20 → J22,出厂 eFuse 默认就是它),
板上没有 JTAG 排针。⚠️ 永远别烧
DIS_USB_JTAG,烧了两边都没有。 让出去的只是「USB 起不来时还能进去」这个兜底。EFUSE_EN1 → IO39、VBUS_EN1 → IO40、EFUSE_EN4 → IO41、VBUS_EN4 → IO42。 空出来的是 IO4 / IO5 / IO7(三个都带 ADC1)+ RXD0/IO44。 - DAC 只在已画好线的 5 个通道里置换(U6 管去西半板的 VDAC1/2/4,U7 管去东半板的 VDAC3/5)。 动用 U6.OUT3 / U7.OUT2/3 还能再省 6 mm,但要新画 OUT/FB 两根线,不值。
- 595 加了「每路 3 个按键必须落在同一片的连续三个南脚上」的约束 —— 比自由指派多 40 mm, 换来三根线出芯片就是一束。U33(y≈70)从「EDL 专用片」变成「北边那一片」, 接管 CH2/CH3 的六个按键 + HUB_RST,因为它正对 y 87~94 的北排两路。
⛔ U33 现在在 P6-FAN-EDL 那一页上带着 CH2/CH3 的按键网络 —— 页面语义乱了一点,
但三片本来就是一条 24 位链,功能上没区别。
落地时踩到的两条 API
- ⚠️⚠️
setState_Net对 netport 是坏的:调用之后网络名变成空字符串,标签也没了 (和 skill §3b 的setState_X/Y同一个毛病)。⇒ 端口只能 删掉重建。 - ✅ skill §8b 那句「端口的 dir 读不回来」是错的:
getState_Component().name就是Netport-BI/Netport-IN/Netport-OUT。 重建前读它原样传回去即可 ——p1_add.py当年「端口只增不重建」的限制可以解除了。
顺手治了:图面文字叠了好几代
P3 有 44 条 sch_PrimitiveText、P3B 64 条、P6 76 条,大量是同坐标同内容的完全重复
(历代脚本只 create 不 delete),还有互相矛盾的旧说明叠在一起。
四道验证(verify_sch / xpage / port_audit / DRC)一条都不管文字,所以它烂了很久没人发现。
.claude/sch_text.py 整页重写:44→13 / 64→14 / 76→16,顺带改掉换脚后变成假话的几条。
这一轮新增的脚本
| 文件 | 用途 |
|---|---|
pinmap.py |
⭐ 三组「逻辑信号 → 物理脚」映射的事实来源 + 落地 + --check 回读对账 |
sch_text.py |
三页图面文字整页重写(删光旧的再写一套) |
⚠️ 还欠人眼确认
PCB1-v11-NL.png(北左输入链改动前)与改动后的截图 —— 还没截改动后的P3-CONTROL-v2.png/P3B-EXPAND-v2.png/P6-FAN-EDL-v2.png三张换脚后的原理图- 摆位总图
PCB1-v11-place.png(v11 那一版,D1/C4/C5/C6 的新位置还没进图)
上一轮(2026-09-06 第二十二轮):拆线重布 + 两处换件 + 控制块按几何重排(§26.16)
| # | 改动 | 依据 |
|---|---|---|
| 1 | J1:KF128-5.08-2P → XT30PW-M(C53373776),(8.5, 72.0) rot 270 |
用户要插拔式;12V 是 8 A,DC-002 那类只有 1 A |
| 2 | J26~J30:KF2510-3A(弯针) → 2510-3A(直针)(C2803349) |
用户要立式;进深 12.01 → 5.81 |
| 3 | N-L 块重排 | XT30 比 KF128 宽一圈,原位放不下 ⇒ 这一版被第二十三轮推翻,见上面 A |
| 4 | 控制块:affine() → CTRL 表(退火求解,pcb1_ctrl_place.py) |
原来 U21/U22 中心只隔 8.04 mm,中间塞不进一根线 |
| 5 | 22 个附属 R/C:affine() → SAT 表(逐个贴对端引脚) |
R602/R603 是 USB-C 的 CC 下拉,离 J22 有 77 mm |
全板 HPWL 9416 → 8935 mm。摆位大改之后 pcb1_wipe.py --push 把板上全部铜清空过一次
(走线 / 过孔 / 铺铜,元件 / 板框 / 丝印不动),13 个旧账本改名成 .stale。
这一轮沉淀的脚本(仍然有效):
| 文件 | 用途 |
|---|---|
fp_body.py |
⭐ 从器件的 3D Model Transform 直接读本体框(--scan / --check)。⚠️ 只给 XY,Z 读不出来 |
p1p6_conn.py |
J1 / J26~J30 换件(做到一根导线不动) |
pcb1_ctrl_place.py |
控制块五颗芯片退火求位置(目标 HPWL)。⚠️ 它写的 _pcb1_ctrl_pos.json 只是参考,pcb1_place.CTRL 是硬编码的事实来源 |
pcb1_wipe.py |
清空板上全部铜(铜层白名单 1/2/15/16) |
⛔ 三个要记住的地雷(仍然有效)
pcb1_route_sig.py的--net/--limit试水以前会把计划缓存清成{}(已修)。clean()以前只删得掉 59% 的自己的铜(已修,改成覆盖判据)—— EasyEDA 会合并共线段、在 T 形接点劈线,坐标精确比对认不出来。- 「两个端点钉死 ⇒ 连通性不变」是破的 —— 链会 T 在别的链中段上。
现在先按搭接点切开(409 → 797 条),拆完还要用
pcb1_netlist.analyze()实算碎片数兜底。
⚠️ 已知偏差(是决定,不是遗漏)
- 立式风扇座把装配顺序锁死了(用户 2026-09-06 拍板接受):立式插口朝上正对载板底面, 座 + 胶壳约 11.5 mm,13 mm 净空里插拔只剩 1.5 mm ⇒ 必须先插好五根风扇线再合载板。
H_FP里 XT30 的 11.0 和直针座的 9.0 是估计值 ——3D Model Transform给不出 Z, 打样前拿实物量。控制块的通道分配还没改→ 第二十三轮已经做完(§26.18)。POGO2_VUP/POGO2_VDN离 SW 节点 0.54 / 0.74 mm(§10.3 要「远离」),sw_keepaway()会一直报,当提示不当错误。
✅ 这一轮(2026-09-06 第二十一轮):五路母版三处整改 + 全链重跑(用户拍板 A+B+C)
设计文档 §26.15。§26.14 抓出的 22 个「几何上没有出路」的引脚,这一轮动手改掉, 改的全是母版(= 五路一起改),改完整条链重跑。
| # | 改了什么 | 结果 |
|---|---|---|
| A | SW 不再往南潜:C%05 北移 0.6,SW-run 平着走 y 12.500(1.2 宽),删掉 SW-down |
五路 EN 全部解放;SW 节点 9.6 → 8.9 mm / 铜面积 10.9 → 9.4 mm² |
| B | U%8 + Rs%01 + C%13 整体北移 0.8 |
eFuse 北排那条缝 0.33 → 1.13 mm |
| C | 跟着 B 改的三处:_CH_FORCE_LOCAL['U%__8','1']、F-trunk 北端、K-IN± |
相对几何一格没变 |
| — | 全链重跑:place → clear → dump → fanout → pour → route_pwr | 342 件 / 498 孔 / 15 铜面,功率级 195 段回读 0 条对不上 |
「几何上出不去」的焊盘:22 → 8(pcb1_escape_audit.py 实测)。
剩下 8 个是和邻脚预留出线段抢地方,不是硬几何 —— 单独布都能通。
⭐ 这一轮踩到的三条
1. 「端帽比端点往外探半个线宽」这一条,一轮里踩了两次。
先是 SW-run 1.5 mm 的西端帽把 EN 封死(在 y=11.5 那一行往西探到 x 11.007,
比端点多探 0.743);改完起点又发现同一个端帽离 U%1.13(BOOT 引脚) 只有 0.080。
⇒ 凡是宽线的端点,心里都要按「端点 ± w/2 的圆」去想,§26.11 已经写过一次了。
2. 挪 0.1 mm 解决不了拓扑问题。 A 组先试过「SW-run 起点东移 0.35」和「收窄到 0.9」,
两次都卡在 5~20 µm 上 —— 因为 COMP 和 EN 两个脚要并排出来,缺的不是余量是车道。
把 C%05 北移 0.6、让 SW 整条不再下潜,才是解。
3. verify() 只认「同宽」的线会报假阳性。
一段窄线整个落在宽线的圆头端帽里时 EasyEDA 会直接吞掉它 —— 铜一点没少,
回读却报 5 条对不上。判据改成「实测线不窄于计划线,且中心距 ≤ (o_w − p_w)/2」。
4. chamfer_routes(smooth=True) 会挪线(最多 0.15 mm),不只是拿掉铜。
而它的净距校核拿的是切之前的自身路径 ⇒ 两条本轮新画的线会被各自抹到对方身上
(实测 $1N227 vs POGO1_PWR 压成 −0.199,2255 段里 2 段)。
⇒ 切完再跑一遍 check(),把牵涉到违规的整条退回未切的版本,最多退 4 轮。
✅ 这一轮(2026-09-06 第二十轮):下板信号布线 —— 工具全套建好,⛔ 没下发
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 新增 pcb1_netlist.py:按实测铜(含回读的铺铜)算全板连通性 |
还欠 200 个网络 / 384 条连接;顺带发现 GND 其实早就通了 |
| 2 | 新增 pcb1_route_sig.py:下板信号布线器(复用载板栅格引擎,只加不拆) |
379 条里布通 284 条(75%),净距 / 钻孔 / 铺铜 三样全 0 |
| 3 | 新增 pcb1_escape_audit.py:逐个焊盘算「能不能走得出去」 |
563 个待接焊盘里 22 个几何上出不去 |
| 4 | Kelvin 三条(U13.8/9/10 ↔ Rs6)单独钉死,不许就近扎平面 |
3 / 3 条,w 0.30 |
| 5 | USB 差分对「一整束布线 + 平行偏移」 | 束长 58.1 mm,W 0.28 / S 0.18,对内偏斜 0.00 mm |
| 6 | 修掉 4 个真 bug(见下) | — |
⭐⭐ 这一轮最值得记住的四条
1. POLYGON 焊盘的包围盒不是它的形状 —— 又一次。
§26.13 抓的是它的 hole 是假的;这一次是 pad[1], pad[2] 那对宽高。
TPS54622 的散热盘是个带四个缺口的多边形(缺口正是让给 pin 1/7/8/14 的),
按 2.032 × 4.320 的包围盒当障碍,这四个脚整个泡在 GND 铜里:
entry_stub 一个出线方向都找不到,BUCK_PG 报「五路全布不通」。
⇒ POLYGON 焊盘的任何标量属性都不能直接当几何用,只能读 pad[1] 那串点。
现在 pcb1_route_sig.poly_pads() 把 11 个多边形盘按真实轮廓光栅化成矩形条。
2. 网名里带 # 的网络会被引擎的 netof() 拆坏。
引擎用 key.split('#')[0] 从「路径 key」还原网名,而下板有 KEY_OE# 和 IOEXP_INT#。
'IOEXP_INT#' + '#0' 拆回来是 IOEXP_INT ⇒ 校核时它和自己的焊盘被当成异网,
报「线-焊盘 净距 −0.100」,看上去像压线,其实两者本来就该连在一起。
⇒ 分隔符改成 chr(0)。凡是「用某个字符拼 key」的地方,都要先问一句这个字符会不会出现在数据里。
3. 45° 切角会切进铺铜里 —— seg_slack() 不认铺铜。
切角是往拐角内侧补铜(不是只拿掉铜),而引擎的切角校核只看焊盘 / 过孔 / 走线。
实测 SDA 在 (6.10, 65.50) 被切进 VO1_P 小岛,只剩 0.043。
⇒ 把铺铜矩形条包装成「假焊盘」一起喂给 chamfer_routes() 和 check()。
4. 0.5 脚距的脚,「放得下」和「栅格上走得通」是两回事。
U8.3(INA226 的 ALERT):西边 1.00 mm 是 GND 扇出孔,东边 1.00 mm 是邻脚的出线段。
0.20 的线需要 1.005 mm(差 5 µm 过不去),0.15 的线只要 0.955 mm ——
可 0.1 mm 栅格上判据要各加一个 QERR 0.0707,变成 1.096 mm,整片不可达。
0.025 mm 栅格的 QERR 只有 0.0177,同一条缝就通了。
⇒ 新增「细栅格逃逸」:0.1 栅格上出不来的脚,先在 4 mm 内用 0.025 栅格单独走出去。
实测 47 个脚里救回 25 个。
⛔ 剩下 22 个脚是几何上就没有出路,必须动板子
| 组 | 焊盘 | 卡在哪 | 差多少 |
|---|---|---|---|
| A | U1.10 U2.10 U3.10 U4.10 U5.10(Buck 的 EN) |
pcb1_route_pwr 的 SW-run 1.5 mm 铜,圆头端帽从 (20.05,27.10) 往西探到 x 19.30 |
脚东边净空 0.376,一根 0.2 的线要 0.6 |
| B | U14.2/3 U15.2/3/14/15 U16.2/3/14/15 U17.2/3 U18.2/3(eFuse 北排) |
上面 INA226 的焊盘边 只隔 0.33 mm,下面自己的散热盘 0.25 mm | 要 ≥ 0.6,缺 ~0.7 mm |
| C | U10.3(ALERT)U13.4(SDA) U13.5(SCL) |
隔壁 GND 扇出孔正对着出线走廊 | 5 µm |
A 组的解:EN 是每路都有的信号,本来就该进 §10.2 母版 —— 在 pcb1_route_pwr.R 里
加一条 EN 路径,并把 SW-run 的起点往东挪 ~0.35 mm(或前 2 mm 收窄到 1.2)。
收窄反而符合 §10.2-2「SW 铜箔面积压到刚好够载流」。
B 组的解:CH1 那一列从下往上是 C106(顶 43.46) → U14(43.85~47.75) → U8(48.08~53.92) → Rs101(54.27~57.73),缝一律 0.330.39 mm,一点余量都没有。
唯一的空地在 Rs101 顶到通道北界 y 59 之间的 1.27 mm
⇒ 把 0.8 mm**(母版改一处 = 五路一起改)。
⚠️ 这会连带作废 扇出 + 铺铜 + 五路功率级布线(U8 + Rs101 整体北移 **0.7U*.9 → Rs*01.1 的 Kelvin 线也在母版里)。
C 组的解:给那两三个 GND 扇出孔在 pcb_fanout._CH_FORCE_LOCAL 里钉一个偏 0.1~0.2 mm 的位置。
⚠️ 另外 95 条是拥挤,要拆线重布才能再往前
失败集中在控制块(U21U23,x 75107 / y 43~58)和 J24/J25 排针周边;
按网络族数:$14N 9 / $8N 7 / $12N 7 / EFUSE_PG 6 / $10N 6 / INA_ALERT 5 …
现在这个布线器是一遍过、只加不拆(这是有意的:板上已有 691 条线不能被抹掉),
所以先布的把后布的堵死了 —— 这正是商业布线器用 rip-up & reroute(协商式拥塞)解决的那一类。
⇒ 下一轮要么加拆线重布,要么等 A/B/C 三组改完再看还剩多少。
顺带记两条
POGO2_VUP/POGO2_VDN离 SW 节点只有 0.54 / 0.64 mm(§10.3 要求「远离」)。 已经有sw_keepaway()在报,不当错误、只列出来。- 8 个 45° 切不动的拐角(转向 89~90°),都在细缝里,
chamfer45试到 0.05 半径仍撞墙。
这一轮的产出(都在 .claude/ 下,没有一个动过板子)
| 文件 | 用途 |
|---|---|
pcb1_netlist.py |
按实测铜算连通性:焊盘 / 过孔 / 走线 / 回读的铺铜当图的节点,--todo 列还欠几条,--json 出任务表。⚠️ 判据不能只测焊盘中心那一格:同网通孔盘在回读的环里是个洞 |
pcb1_route_sig.py |
下板信号布线器。--net / --limit / --push / --clean --push / --verify / --refresh。内含 Kelvin 强制表 FORCE、USB 束 + 偏移、细栅格逃逸、铺铜障碍、sw_keepaway() |
pcb1_escape_audit.py |
逐焊盘的逃逸可达性:两层泛洪 + via_ok() 打通,走不到 4 mm 外就报。--why <焊盘> 列出挡路的铜。⚠️ 判据环不能取自由区最外圈(那一圈 free() 永远是假的) |
✅ 这一轮(2026-09-06 第十九轮):五路功率级精调(§26.13)
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 修 POLYGON 焊盘的假 hole(dump_pads / pcb_fanout / pcb1_parts_dump 三处) |
七个散热盘回到扇出名单 |
| 2 | 盘内阵列改成按真实多边形撒点 + 自适应间距 + 补中心孔 | U1~U5 0 → 5 个孔;U6/U7 0 → 4;eFuse 2 → 6 |
| 3 | 摆位 v9:L*01 / U*14 / Rs*01 各转 180°,C*05 与三颗输出电容挪位 |
四条回路的朝向全部理顺 |
| 4 | 新增 pcb1_route_pwr.py:母版 27 条路径 × 5 路 = 210 段,全部顶层 |
一路 74.8 mm,SW 13.1 → 9.7 mm |
| 5 | 新增 pcb_fanout._CH_FORCE_LOCAL:5 个焊盘 × 5 路 = 25 个 FORCE_VIA |
五路的扇出终于真的一样 |
| 6 | 重跑全链:place → 扇出三步 → 铺铜 → 布线 → DRC | 495 孔 / 691 线 / 间距 0 / 物理 0 / 连接性 594 |
⭐⭐ 这一轮最值得记住的四条
1. 「有孔 = 通孔盘」是错的判据 —— POLYGON 盘的 hole 是假的。
板上 11 个 POLYGON 盘无一例外满足 hole = 0.600 × min(w,h)(U1 的 2.032×4.320 → 1.2192,
U6 的 1.70×1.70 → 1.02,J22 的 1.30×0.60 → 0.36),EasyEDA 渲染出来是实心的。
判据应该是层号:layer == 12 才是多层通孔盘。
2. 五路的扇出结果并不相同 —— 明明摆位一模一样。
扇出规划器的候选角度按板坐标排,而 CH2/CH3 整路转了 180°,
同一个焊盘在 CH1 往东扇、在 CH2 就往西扇。母版里算好的走廊于是在 CH2/CH3 上被堵死,
而报出来的是「CH2 的 F-trunk 撞到一个 GND 过孔」——看上去像布线画错了。
⇒ 关键焊盘按 CH1 的解钉死(FORCE_VIA),钉死之后照样走全套净距校核。
3. DRC 的「物理错误」那一支平时是空的,容易漏看。
0.28/0.25 的线下发之后:间距错误 0、连接性还降了 125 条 ——
而 物理错误·导线 里躺着 50 条「线宽 11mil,应该 ≥ 11.8mil」
(SWNODE/PWR4A 的 strokeWidthMin 都是 0.30)。加宽到 0.30 清零。
4. 回读走线不能按端点比:EasyEDA 会在 T 形接点上把已有的线劈成两条。
西侧那条 +12V 母线下发之后在板上是两条(支线接在中间)。
按端点比报「210 段里 40 段对不上」,而铜一点没少。
⇒ verify() 用覆盖判据:每 0.2 mm 取一个点,要求被同网同层同线宽的实测线盖住。
⚠️ Kelvin 的那一条:U*8.9 不许有过孔
U*8.9(INA226 的 IN−)已经写进 pcb1_pour.ROUTED_PADS。
让它走 VO*_P 小岛的话,取样点在岛上而不是分流电阻的焊盘上,中间隔着
Rs*01 那三个扇出孔(并联约 0.5 mΩ)。Rs 只有 5 mΩ、满载才 20 mV,
0.5 mΩ × 4 A = 2 mV = 10% 的读数误差,而 DRC 和连通性检查都看不出来
(IN− 和 VO*_P 本来就是同一个网)。pin 8 是 VBUS,照旧进岛。
✅ 上一轮(2026-09-06 第十八轮):F1 → 12 A / R9 → 5 × 2Ω 2512
两件都是用户拍板的选型改动,走完整链路:改原理图 → 导入变更 → 重推摆位 → 重扇出 → 重铺铜 → DRC。详见设计文档 §26.12。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | F1 F1206HC10A0TM → F1206HC12A0TM(C310990) |
同是 1206,摆位一格没动;封装对象换了(F1206)但焊盘几乎一样 |
| 2 | R9 立式水泥电阻 → R9A~R9E 5 × 2Ω 2512 串联 | 10Ω/10W,高 22 → 0.8 mm |
| 3 | pcb1_import.py 加 --bom |
原来 _p1_sch_bom.json 在仓库里没有生产者 |
| 4 | pcb_fanout.py 加 guard_not_pushed_twice() |
我推重了一次,888 个过孔(见下) |
| 5 | P1-INPUT 文字收拾 | 66 → 14 条,清掉一整代旧说明 |
| 6 | 结果 | 342 件 / 444 过孔(重复 0)/ 15 块铜面 / DRC 间距 0 + 连接性 725 |
⚠️ 先纠一条我上一轮写错的
「F1 封装是 1206 但 BOM 写 10A、1206 做不到 10A」—— 错的。
F1206HC10A0TM 本来就是 1206/10 A,BOM 和封装从来没对不上。
换 12 A 的真实理由是降额:8/10 = 80% 超过保险丝常规的 75% 线,且 I²t 只有 15 A²s。
⭐⭐ 这一轮最值得记住的两条
1. pcb_fanout.py --net all --push 不幂等 —— 我连推了两遍。
板上变成 888 个过孔(每个都有同坐标同网的副本),而短线数没变(444):
EasyEDA 把共线重叠的新线并进了旧线。连接性 DRC 一个字不报(副本同网,电气上「没错」)。
唯一判据是回读过孔数。现在 --push 前会硬拦;重推必须
--clean → dump_pads.py → --push 一次跑完。
2. H_FP 里那个 9.5 是照封装名 W9.5 抄的,而 R9 是立式件。
RES-TH_L13.5-W9.5-P5.80-D0.7:本体 13.5 长、脚距只有 5.80 ⇒ 它站着
(同族 3D 模型标 H52.0)。那一片 hmax 只有 12.0,
所以「高度预算全过」在它身上假了整整一轮,是用户看出来的,不是检查看出来的。
⇒ 往 H_FP 加直插件的判据:脚距和本体长度对不上就是立式,
高度不是封装名里的 W,是那个对不上的方向。
⛔ 新增的两个地雷标记
p1_add.py已过期,底稿p1-base.json停在 2026-09-04。再推一次会把 R9A~R9E 之间那 6 段导线静默回退。已加硬闸门(要跑得带--i-know-it-is-stale)。 ⇒ 改完一页就问一遍:还有哪个脚本会重画这一页。- 改过原理图必须
pcb1_import.py --bom再--verify,否则是拿新 PCB 和旧原理图对账。
截图:PCB1-v8-r9f1.png(R9A~R9E 的 1+2+2 蛇形)/ P1-INPUT-v2.png(原理图)。
还没等到用户回话。
✅ 这一轮(2026-09-06 第十七轮):8 A 顶层宽铜
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | pcb1_pour.REGIONS 加三块顶层铜面 |
VINRAW-TOP 46 / VINFUSED-TOP 19 / VINSW-TOP 284 mm²(实测) |
| 2 | 网名用 pcb1_rules.resolve_auto() 当场反查 |
$1N223 / $1N227 / $1N236,一个字没写死 |
| 3 | Grid 分内层 / 外层两套障碍模型 |
外层连贴片焊盘一起挡;新增 obstacles() 把 438 个过孔 + 441 条走线算进去 |
| 4 | 新校核 check_top_clearance()(不过格子) |
当场抓出两处侵入,其中一处栅格看不见 |
| 5 | 新校核 poly_vs_rects() |
手写的 24 个点 vs 它该等于的 8 个矩形,0.05 mm 逐格对照 |
| 6 | 下发 + 回读实测铜 | 15 块铜面全部铺出来,三段各 1 个连通域 |
| 7 | DRC | 间距 0 / 连接性 719(铺顶层铜前是 733) |
⭐⭐ 这一轮最值得记住的:线的端点不是铜的端点
北柱东缘写在 x 6.8,Rs6.2 那条 +12V 出线的端点在 x 7.401 —— 看起来隔 0.6 mm。 但那条线 1.0 mm 宽、圆头端帽往外再探 0.5 mm,实际铜到 x 6.901, 真实净距只有 0.101 mm。而 0.25 mm 的栅格上这 0.1 mm 一个格心都落不进去 ⇒ 栅格全绿。真下发下去 EDA 自己会往回缩一点点铜,板子不会报 DRC, 但板上那块铜就不再是计划里那块铜,后面所有断面数字全部作废。
⇒ 凡是手画的铜,都要过一遍 check_top_clearance() 这种真实几何的校核,
栅格只能用来算连通域和断面。
顶层三段长什么样(实测断面,顶层 1 oz)
最窄两处:干道 x=12 处 4.75 mm(10.0 A)、北柱 y=92 处 5.00 mm(10.4 A), 8 A 下 ΔT 分别约 12 / 11 °C。其余各点 8.75~21.5 mm。
截图:PCB1-v7-top8A.png(只开顶层 + 焊盘 + 顶丝印)。还没等到用户回话。
⚠️ 顺带看出来的一件:F1 的封装是 1206,但 BOM 写的是 10 A
FUSE-SMD_L3.2-W1.6-1 = 1206,而 §7 的 BOM 写「保险丝 10A 慢断」。
1206 的贴片保险丝一般做到 5 A 左右就到顶了。8 A 的铜宽本身没问题,
是器件选型和封装对不上。换大封装会动 J1→F1 那一段的摆位
⇒ 要重推摆位 + 重扇出 + 重铺铜。这一条留给用户拍板。
✅ 这一轮(2026-09-06 第十六轮)做完的
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 修去耦电容摆位:新增 pcb1_place.DECAP 表 + place_decaps() |
7 颗跟着本尊走,最远的储能 3.31 mm,其余 0.76~2.24 mm |
| 2 | 修 pcb1_parts_dump.py 漏了焊盘自身旋转 |
8 个元件的 bb 各长 0.26 |
| 3 | 新增 pad_clearance_errs():焊盘到焊盘净距 0.2 mm |
全板 1171 个焊盘一条不报 |
| 4 | 修 pcb_fanout.TARGET(还停在 §26.8 之前) |
删掉 VOn / +5V / 8 A 三段,它们不上 Inner2 |
| 5 | 修 _pour_filter()(指着单板时代的 pcb_pour.py) |
改指 pcb1_pour.REGIONS;实测滤掉 17 个铜面外的 +3V3 焊盘 |
| 6 | 扇出规划器补五条(见 §26.9) | GND/+12V/+3V3/VOnP 全部失败 0 个 |
| 7 | 扇出下发 | 438 过孔 + 441 短线,DRC 间距 0 |
| 8 | 实现 pcb1_pour.py --push / --verify |
12 块铜面已下发;回读实测铜 24 项全 OK |
| 9 | 下发前 / 下发后共用同一段 grid_checks() |
分两套写只能证明两套代码各自自洽 |
| 10 | 新校核:每块碎片都要有本网过孔落脚(浮铜) | 9 组 (网络,层) 的 12 块碎片全部有落脚 |
⭐⭐ 第 1 条是这一轮最重要的发现:ESP32 的储能电容在 48 mm 外
追「U6.15 为什么扇不出去」追出来的。全板 20 个 IC 的 +3V3 电源脚里
只有 1 个的去耦在 3 mm 以内;C601(22 µF「ESP32 储能」)离 U20.2 48.2 mm,
C602(100 nF)45 mm,U20 电源脚周围 12 mm 内一个元件都没有。
根因:控制块是 affine(['P3-CONTROL','P3B-EXPAND'], ...) 按原理图坐标仿射映射摆的,
它保「原理图上挨着的板上也挨着」,保不住「去耦贴着电源脚」;
而 U20 是上面手工单独摆的 —— 芯片被拎出了仿射集合,去耦却留在原地。
⇒ 动控制块布局时先看 DECAP 表,它是这件事的事实来源,check() 里有硬校核。
表的顺序 = 抢位置的顺序,小的高频那颗必须排在大的储能之前(理由见 §26.9)。
⭐⭐ 第 2/3 条:本地检查曾经「全绿而板子是错的」
改完摆位本地六项全过,DRC 却报 4 条间距错误:C607_2 到 U6_13 只有 0.096 mm。
两个原因叠在一起 ——
pcb1-parts.json 的包围盒漏了焊盘自身的旋转(QFN 侧边脚是长条焊盘转 90° 用的,
U6 的 bb 在 y 上短 0.26 mm);而 check() 只查「包围盒重不重叠」,不重叠不等于够净距。
现在两条都修了,新检查直接照 DRC 那条 0.2 mm 的规则算,量焊盘不量包围盒。
⚠️ 同一套 API 里
getAllPinsByPrimitiveId的 rotation 是度,pcb_PrimitivePad.getAll()的是弧度。
下板 PCB1 接下来的顺序
-
让用户人眼确认摆位 v9 + 功率级布线(截图已出,见上面「一句话现状」)。 不算硬阻塞,但这一轮动过五路的摆位,值得看一眼。
-
设计规则✅ §26.7 2.定铺铜几何✅ §26.8 -
扇出✅ §26.9 / §26.13 4.铺铜✅ §26.10,已下发并回读复核 -
8 A 输入链顶层宽铜✅ §26.11,已下发并回读复核 -
五路功率级精调✅ §26.13,已下发并回读复核 -
写下板信号布线器✅pcb1_route_sig.py已建好并跑通 75%(第二十轮),pcb1_route_pwr.py那 210 段一根没动(这个布线器只加不拆,删除只按坐标删自己画过的)。 -
A/B/C 三组几何整改✅ §26.15,用户拍板 A+B+C,已全链重跑并下发。 全链的正确顺序(动过摆位就照这个走,一步都不能省):pcb1_place --push→pcb1_clear --push→dump_pads→pcb_fanout --net all --push→pcb1_pour --refresh --push→pcb1_route_pwr --push→pcb1_route_sig --push→pcb1_pour --refresh --push(给新过孔补反焊盘)→pcb1_reload→drc_read。 -
⬅️ 下一步:给
pcb1_route_sig.py加拆线重布(rip-up & reroute),收掉剩下的 91 条。 ⚠️ 直接再跑一遍pcb1_route_sig.py --refresh --push也会有进展(它读板上的铜、 只补还缺的),但一遍过的策略已经到顶了 —— 真要清零得能拆别人的线。⚠️ 北左角的顶层现在被 8 A 宽铜占了大半(x 1.5
28.9 / y 68.693)。 U13 的 I2C、U29、R9 那几条信号得走底层—— 底层整层基本是空的,不占地方。 三条还没布的细线已经有名有姓地写在pcb1_pour.ROUTED_PADS里:U13.10(Kelvin VIN+,接北柱顶端那一小截死头)/DZ1.1/C1.1(栅极)。
⚠️ 动了铺铜或摆位就要重跑 pcb1_pour.py --push,它自己会先把计划跑绿再下发;
下发完自动 --verify 回读实测铜。只想看不想改板就 --verify(加 --cache 用上次的回读)。
⚠️ 动过摆位就要重跑扇出三步,不能省也不能并:
pcb_fanout.py --clean → dump_pads.py → pcb_fanout.py --net all --push。
⚠️ 连接性数字扇出之后会先涨再跌:布线前基线 1145 → 扇出完 1336 → 铺完铜 733。 过孔本身是新对象,铺铜之前每一个都是自己网络里的孤岛。 只看扇出完那一步会以为扇出把事情弄坏了。
⚠️ EasyEDA 客户端这一轮掉线过三次(EDA window ... disconnected)。
掉线时命令可能已经执行完只是回话丢了 —— 别直接重试,先回读板上对象数再决定。
⚠️⚠️ 一条永远别忘的:J24 / J25 的针号是故意错一位的(§26.5,用户已拍板选 A)
p5_btb.py 的 MATE_SWAP = True 让下板这一侧的网络按针号奇偶对调
(两颗封装焊盘编号同手性 + 母座在载板底层、顶视图镜像一次,不改的话下板 VO1_P
会顶在载板 GND 上)。c1_conn.py(载板 C1-CONN)用的是原始表,一个字没动。
⇒ 判据是网络不是针号:下板 pin(2r−1) 正对载板 pin(2r),差一位是预期的。
每次动完摆位跑 pcb1_place.py --verify 逐个焊盘核。
Inner2 分块的结论(详见设计文档 §26.8)
| 层 | 网络 | 名称 | 形状 |
|---|---|---|---|
| Inner2 | +12V |
12V-N |
北带 y 88~124.5 通板 + 风扇指下伸到 y 66.5,挖掉 U13 那一角 |
| Inner2 | +12V |
12V-SW+BR |
南西块 (0.5 |
| Inner2 | +12V |
12V-SE+RC |
南东块 (116 |
| Inner2 | +3V3 |
3V3-SW / 3V3-SE |
南部两块,故意不连通(见下) |
| Inner2 | GND |
GND-FILL-L3 |
剩余全部 |
| Bottom | VOn_P |
五块小岛 | 各约 18 × 13 mm |
| Inner1 | GND |
GND-L2 |
整板,只挖 ESP32 天线区 |
退回走线的四类(都算过,不是省事):+5V 底层 1.2 mm 干线 / VOn 顶层 1.5~2.0 mm /
VOn_P 底层小岛 / 8 A 输入链顶层宽铜 ≥3.5 mm。
关键数:内层 0.5 oz 与外层 1 oz 差约 4 倍(8 A 内层要 18.2 mm,外层只要 3.49 mm),
而这块板底层整层是空的 ⇒ 短距离大电流根本不该上内层。
⚠️ +3V3 在 Inner2 上是 2 个连通域,是设计意图不是漏接。 中桥必须纵向穿过
y 41~58.5 那条带,而 +3V3 的南部焊盘横向铺满同一条带 —— 同一层上两个都要连通的
2-D 区域,交叉拓扑上避不开。让 0.5 A 的那个换层:南西 / 南东各自从北边引一条底层干线。
⭐ 这一轮校核当场抓到的一个错:孤岛判定是按铺铜对象算的,不是按网络
第一版把 +12V 写成六个矩形,栅格连通性说「1 个连通域」,看着完全正常。
新加的孤岛规则校核报出来:12V-RC(右通道)里一个 +12V 焊盘都没有 ——
哪怕它和北带紧挨着、铺出来是连续铜,它自己那一块里没有本网焊盘/过孔照样被整块删光
(边框还在、nPoured 是 0)。⇒ 并成三个 L / ⌐ 形多边形,每块各自含风扇座的通孔针。
另外两条:只有通孔焊盘在内层挖反焊盘(贴片对内层透明,板上实测 122 个通孔);
区域间净距不能用包围盒估(L 形的 3V3-SE 包围盒把 12V-FAN 整个罩住,
算出来 0.00 mm 像严重违规,实际相距 0.50 mm)。
下板还欠的几件(都不影响现在动手)
- C2 / C3 罐高必须 ≤ 11.5 mm(现在按 12.0 建模,
H_FP里那一条)。 载板底面在那块地方是光板,hmax = 12.0,卡着上限过;换一颗矮罐才有余量。§22.3f - CH2 的
POGO2_*三根要横穿 60 mm(信号在 J25、电源在 J24,载板那边决定的,§26.3)。 - U20 的天线端离下板边只有 1.5 mm,而且上面还盖着载板 —— 这块板的 WiFi 本来就指望不上, 真要用就得把天线引出机箱。
- 北中块 x 58
118 / y 85125 有一块约 60 × 40 的空地(CH2/CH3 的排线车道从这里过), 留给电源面和风扇线,暂时不填。
下板 v6 的布局长什么样(设计文档 §26.3 / §26.4)
y 125 ┌──────────────────────────────────────────────────────────────┐
│ P1-INPUT │ CH2 (北,rot180) │ P1B + P6 + J28 │ CH3 (北,rot180) │
│ J1/F1/Q1/D1 │ x 30.1~57.3 │ x 58~118 │ x 118.5~145.7 │
│ C2/C3/R9/U19 │ │ │ │
y 66 ├───────────────┴──────────────────┴────────────────┴─────────────────┤
62.5 │ ==== J24 (x 5.3~58.7) ==== ==== J25 (x 101.3~154.7) ==== │
y 59 ├───────────────┬──────────────┬─────────────────┬────────────────────┤
│ CH1 (南) │ CH4 (南) │ P3/P3B 控制块 │ CH5 (南) │
│ x 10.6~37.8 │ x 42.3~69.5 │ U20/U6/U21~23 │ x 121.8~149.0 │
y 0 └───────────────┴──────────────┴─────────────────┴────────────────────┘
J26/J27 左板边 J29/J30 右板边 J28 顶板边 J22 调试口在右板边
下板新加的工具(.claude/ 下)
| 文件 | 用途 |
|---|---|
pcb1_import.py |
下板「从原理图导入变更」:--probe 读全部变更行 / --apply 真的导 / --verify 拿 PCB 元件表和原理图 BOM 逐条对账 |
pcb1_parts_dump.py |
抓 pcb1-parts.json(每件的真实焊盘框 + 每个焊盘的本地坐标)。导入变更之后必须跑 |
pcb1_env.py |
把载板底面折算成下板的高度预算图 pcb1-env.json(zones / lanes / slots / J24-J25 锚点);hmap() 打一张 ASCII 高度地图 |
pcb1_place.py |
下板摆位模型 + 本地六项检查 + --push / --verify(回读焊盘核对插) |
pcb1_clear.py |
拆掉下板全部铜(导线 / 过孔 / 铺铜边框),保留板框、M3 焊盘、丝印 |
pcb1_rules.py |
下板设计规则:线宽 5 档 / 差分 1 对 / 铺铜全连接;自动命名网按位号+引脚反查。--push / --check |
pcb1_place.DECAP |
去耦电容跟着本尊走的事实来源(第十六轮加)。表里的电容不参与仿射摆位,芯片摆完之后自动贴到电源脚旁边;check() 里有硬校核 |
pcb1_place.pad_clearance_errs() |
全板焊盘到焊盘净距(0.2 mm,照 DRC 那条规则)。「不重叠」不等于「够净距」 |
FANOUT_WHY=U6.15 |
环境变量。打印这个焊盘所有候选孔位卡在哪一道校核上,按次数排序 |
p1_r9f1.py |
P1-INPUT 换件(F1 → 12 A / R9 → R9A~R9E)。增量改法:先 create 后 delete、旗删掉重建、坐标全 5 的倍数、引脚坐标断言不过就停手 |
p1_text_fix.py |
P1-INPUT 文字去重 + 挪位。幂等(新增块也走「先删再建」),逐条对账最终 14 条 |
pcb1_import.py --bom |
逐页重抓原理图 BOM(_p1_sch_bom.json)。改过原理图先跑它再 --verify,一页一个请求(eda.py 截 20000 字符) |
pcb_fanout.guard_not_pushed_twice() |
板上已有过孔就拒绝 --push。这脚本只加不删不幂等,推两遍会叠出同坐标副本而短线数和 DRC 都看不出来 |
pcb1_pour.check_top_clearance() |
顶层手画铜面到异网铜的真实几何净距(不过格子)。0.25 mm 栅格漏得掉 0.1 mm 的侵入;走线的圆头端帽比端点还往外探半个线宽 |
pcb1_pour.poly_vs_rects() |
手写的复杂多边形 vs 它该等于的矩形并集,0.05 mm 逐格对照。写错一个坐标,别的检查一个都发现不了 |
pcb1_pour.obstacles() |
板上已有的铜(438 过孔 + 441 走线)。顶层铺铜要躲它们,内层只躲过孔 |
pcb1_route_pwr.py |
五路功率级手工布线的事实来源(§10.2 四条回路)。路径写在 CH1 母版局部坐标里,仿射到五路。不带参数 = 计划 + 五项校核;--push 下发(--clean --push 先删本轮旧线);--verify 按实测铜核覆盖;--ch N 只看一路 |
pcb1_netlist.py |
下板连通性的事实来源:按实测铜(含回读的铺铜)算每个网络还欠几条。--todo / --json / --refresh(第二十轮加) |
pcb1_route_sig.py |
下板信号布线器(只加不拆)。Kelvin 强制表 / USB 束+偏移 / 细栅格逃逸 / 铺铜当障碍 / sw_keepaway()(第二十轮加) |
pcb1_escape_audit.py |
逐焊盘的逃逸可达性。「布不通」到底是这一头出不来还是中间挤,只有它分得清(第二十轮加) |
pcb1_reload.py |
下板专用的客户端重载(DRC 的连接性那一支要 reload 之后才准)。⚠️ 别拿 pcb2_reload.py 当通用工具:那一份写死载板 uuid,而且会把板上全部铺铜边框删掉 |
pcb_fanout._CH_FORCE_LOCAL |
五路通道里必须五路一样的那几个孔(按 CH1 的解钉死再展开)。不钉的话 CH2/CH3 会往反方向扇 |
pcb1_pour.py |
下板铺铜的事实来源:REGIONS 定义每一块。不带参数 = 计划 + 五类校核 + ASCII 分块图;--push = 校核跑绿才下发,完事自动回读复核;--verify(--cache 用上次回读)= 只按实测铜再跑一遍。下发前后走的是同一段 grid_checks() |
⚠️ pcb_rules.py 是单板时代的,已被 pcb1_rules.py 取代,别再 --push 它。
⚠️ pcb_fanout.py 仍然是下板在用的(PCB = 7e94…),载板的 pcb2_fanout.py 是它的壳。
⚠️ pcb_place.py / pcb-parts.json / pcb-pads.json 是单板时代的,别再拿来摆下板。
✅ 已拍板并落地:C745 往南挪 2.7 mm(2026-09-06,用户选 A)
C745 从 A 圈上的 (100.05, 61.70) 挪到 (100.0, 59.02)(pcb2_place.hub_layout() 里单列一条,
不再跟着 A 圈的 for 循环走)。GND 脚 (100.0, 58.60) / +3V3 脚 (100.0, 59.44),
扇出孔实际打在正东 (100.87, 58.60),引线 0.65。
pcb2_place --push → pcb2_clear(跨元件净距全过)→ pcb2_fanout --only C745.2 --push
→ reload → pour → inner1_verify(五块岛仍各 1 连通域)→ DRC 115 间距(基线)+ 332 连接性。
⇒ 载板上除了 J7n 那 65 个 GND 脚(归 bm24_pre() 管),贴片 GND 焊盘已经全部扇出。
pcb_fanout.py 顺手加了 --only 位号[.引脚],...:板上已经有整轮扇出、只想补一两个漏网的时候用,
不会重复下发另外那 247 个。
当初为什么非挪不可(留作记录)
| 方向 | 挡住的东西 | 余量 |
|---|---|---|
| 南 | UP_DM 东行段 y=60.60(宽 0.28) |
孔要 0.64,只有 0.41 |
| 北 | 自己的 pin1 → 再往北是 U32 南排焊盘 y=63.55 | 缝 0.73,要 1.0 |
| 东 | R715 pin2 | 缝 0.667,要 1.0 |
| 西 | UP_DM 竖段 x=99.25 / UP_DP 竖段 x=98.75(相距 0.5) |
塞不下 Ø0.6 的孔 |
并到邻近同网焊盘也不行 —— 最近的 GND 是 C743 pin2(2.70 mm 外),中间横着这两条竖段。
(已作废)等用户拍板:C745 的 GND 脚被 UP 差分对堵死
C745(Hub 的 +3V3_HUB 去耦 0402,GND 脚在 (100.05, 61.28))是全板唯一扇不出去的焊盘。
不是搜索不够狠,是几何上真的没有位置:
| 方向 | 挡住的东西 | 余量 |
|---|---|---|
| 南 | UP_DM 东行段 y=60.60(宽 0.28) |
孔要 0.64,只有 0.68−0.27=0.41 |
| 北 | 自己的 pin1(+3V3_HUB)→ 再往北是 U32 南排焊盘 y=63.55 | 缝 0.73,要 1.0 |
| 东 | R715 pin2 | 缝 0.667,要 1.0 |
| 西 | UP_DM 竖段 x=99.25 / UP_DP 竖段 x=98.75(相距 0.5) |
两条之间塞不下 Ø0.6 的孔;西边那条缝只有 0.01 mm 宽 |
并到邻近同网焊盘也不行 —— 最近的 GND 是 C743 pin2(2.70 mm 外),中间横着这两条竖段。
三个选项(都要改布局,所以留给用户定):
- A. C745 转 90° 并东移到 (100.0, 61.7) 一带(脚变成东西向,GND 脚在西)。 实算过:孔打在 (100.0, 62.47) 时离 UP_DM 竖段 0.75、离 U32 pin56 焊盘框 0.15,都过。
- B. 把 UP_DM / UP_DP 那两条东行段整体往南挪 0.3~0.5 mm(要重推
pcb2_dp_long.py, 下游 J23 那一头也要跟着动)。 - C. 不管它 —— 那一颗去耦电容的地就只能靠
pcb2_gnd_patch.py想办法, 想不出来就是一条连接性 DRC,实物上等于这颗电容白焊。不推荐。
✅ 这一轮(2026-09-06 第十三轮):载板 77 个信号网络全部布通并下发,DRC 回到基线
| 结果 | |
|---|---|
pcb2_route_sig.py |
87 个网络 / 459 条路径 / 1030 线 / 210 孔,一个没布通的都没有 |
| 本地净距校核 | 全部通过 ✔ |
pcb2_gnd_patch --check |
板上悬空的 GND 连通域 0 个 |
inner1_verify |
五块岛各 1 主域、3 焊盘 + 11 过孔全在主域,最窄 29.20 / 7.05 / 7.20 / 7.70 / 7.60 |
pcb2_route_dp(回归) |
11 对差分偏斜全部 ✔(最大 4.75 mm) |
| DRC | 115 条 = 全部是封装自带的「贴片焊盘到贴片焊盘」基线;间距 0 新增、连接性 0 |
⇒ 载板 PCB2 布线完成。 截图 PCB2-v6-routed-all.png / PCB2-v6-hub.png / PCB2-v6-site4.png。
BM24 那一段是整个重写的(v4 的常量在 v5 上全错)
v5 把 J7n 从 rot 180 改成 rot 0 又挪了位置,站内坐标整体差
u' = u − 5.00 / v' = 32.00 − v(v 轴是镜像的)。
照抄映射也不行 —— v 一镜像,「前排外侧那条缝」换到了另一边,而那边站着 R76n/R77n。
v5 的走法(和 v4 反过来,因为后排 25 个脚在 v5 里全是 GND,没有信号脚要往外挤):
- 前排 5 个 GND(pin 2/8/12/16/24)→ 往内进中央通道,v=35.00 的 0.30 梳子,4 个孔(跳过 u 50.05)
- 后排 7 个 GND(pin 1/19/23/29/37/41/47)→ 往外,v=33.10 的梳子,5 个孔(那一带一路空到 v 26)
- pin52 往东平出到 u=59.60 打一个孔
- THERM / CC1 独占前排外侧:CC1 一根直线上去(R76n 中心特意取 52.233,让 pin1 正好落在 J7n.22 正北); THERM 打两个孔走顶层跨过 CC1
新增 j7_assert():每次跑都拿板上实际焊盘核一遍那一堆手写常量(五个站 × 13 个 GND 脚 + THERM/CC1 + R76n/R77n)。
没有它的时候,坐标失配的表现是「在空地上画了五套梳子」—— 净距校核当然全过(空地不撞人),
只有 DRC 的连接性会含糊地多出几十条。
这一轮修掉的四个真 bug(都在 pcb2_route_sig.py 里)
- 搜索窗口边上没有墙。障碍是裁到窗口里再栅格化的,完全落在窗外的铜一格都不标,
于是贴着窗口边走的线看不见它。实测
SCL的窗口从 x=44.02 起,J31.6 的焊盘框(42.10~43.80) 整个在窗外,布线器就在 (44.10, 67.40) 打了个孔,离那焊盘只有 0.134。 改法:数组往外扩RIM=1.2标障碍,再把外面这一圈整体标成占用 —— 可走区域不变,但看得见窗外的铜。 - 钻孔间距不分网络,但
viafree只看异网铜。于是布线器紧挨着pcb2_dp_long已经打好的孔又打一个 ((92.500,70.000) 与 (92.400,70.500) 中心只隔 0.510),DRC 报「钻孔到钻孔 8.1mil,应 ≥ 11.8mil」, 净距校核一声不吭。新增holefree距离场(所有过孔、不分网络)。 - 手写前置段没有一开始就进障碍表。原来只有 GND 的在循环里补进去,THERM/CC1/+3V3 的要等轮到自己 —— 排在前面的网络照样从这些手写坐标上碾过去。
+3V3_HUB上pcb2_dp_long画的三条会被静默删掉。push_sig()是「本脚本负责的网络整网删掉重画」, 而那三条被 D± 夹着、后面再也挤不进去。改成从同一个事实来源(pcb2_dp_long.build)取回来当前置段, 既让最小生成树知道那些焊盘已经连上,下发时也原样重画。
四段手写坐标(栅格布线器进不去的地方,理由都写在代码注释里)
| 网络 | 为什么手写 |
|---|---|
THERM_n |
出阵列后第一次拐弯的 v 只有 0.053 mm 的窗口(下受前排焊盘、上受 *_DPJ 换层孔),而且那一段必须保持 0.11 |
+3V3_HUB U32.52→U32.46 |
pin46 在东排被 VBUS_DET/NONREM0 围死,唯一出路是往 QFN 肚子里走(南排焊盘上沿与散热盘之间那条 1.6 mm 死带) |
+3V3_HUB C750.1→R716.2 |
R716.2 是最东那颗 +3V3 焊盘,南北被两个 GND 脚夹住,只能翻顶层斜拉过来 |
HUB_RST / VBUS_DET |
R709 与 R712 之间只有 0.66 mm 的缝。0.15 的线走得过去(上下各 0.23),但栅格判据要 0.371 而半缝只有 0.33 |
一处已知的口子(不是错,是要记住的事)
+3V3_HUB 现在由 pcb2_route_sig.py 下发(含 pcb2_dp_long 那三条)。
⚠️ 再单独跑 pcb2_dp_long.py --push 会把 route_sig 布的整个 +3V3_HUB 删掉重画(它按网络整删)。
要改差分的话,顺序是 dp_long --push → route_sig --push,不能只跑前一半。
另外 45° 化把那条顶层干线的近直角拐点切成了两个 51° 的转折(sharp_corners 报「切不动」)——
净距校核过、不是直角,属于可接受;但它意味着板上那条干线的几何已经和 pcb2_dp_long.py 里写的不完全一样。
为此动的两处布局(都验过并重扇出)
- R76n/R77n:v 37.15 → 38.0,R76n 的 u 顺带改成 52.233。
理由:J7n 前排逃逸带原来只剩 0.58 mm,THERM 和 CC1 一个往东一个往北必然交叉,同层无解,
想让 THERM 翻顶层又打不下孔(要离前排 0.5 ⇒ v ≥ 36.80,又要离 R76n 0.5 ⇒ v ≤ 36.38)。
挪完逃逸带 1.43 mm,两条各走各的。挪之前先用新写的
pcb2_del_box.py把 R77n.2 的旧扇出孔删掉。 - C745 往南 2.7 mm(用户拍板,见下)。
新工具
pcb2_del_box.py:删掉指定框里某个网络的线与过孔(板坐标或站内坐标,站内会自动展开到五个位)。
板上的铜是好几个脚本各画各的,挪了元件之后那颗扇出孔就成了没人认领的孤儿 ——
谁的删除逻辑都不管,DRC 只报一条「过孔没连上任何东西」,很容易被当噪声放过。
判据是完全落在框里才删(半进半出一律不动)。
pcb_fanout.py --only 位号[.引脚],...:板上已经有整轮扇出、只想补一两个漏网的时候用。
上一轮(2026-09-06 第十二轮):GND 扇出 + VOn_P 进岛,两步都下发并复核
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | pcb_fanout.py 补三个缺口(见下);pcb2_fanout.py --push |
247 孔 / 122 短线,只有 C745 pin2 扇不出去 |
| 2 | pcb2_vo_vias.py 扩到 35 个焊盘(补 C8n1.1 与 J8n.1,各 3 孔)并修出线层 |
55 孔 / 55 线,净距全过、0 个直角 |
| 3 | pcb2_reload → pcb2_pour --push → _refresh_geo → inner1_verify |
五块岛各 1 连通域,3 焊盘 + 11 过孔全在主域;最窄 31.30 / 7.05 / 7.20 / 7.70 / 7.60(下限 7.0) |
| 4 | drc_read --pcb2 |
450 = 间距 115(与基线一模一样,无新增)+ 连接性 335(布线前 519,−184) |
| 5 | 截图 PCB2-v5-fanout-all.png / PCB2-v5-fanout-site1.png / PCB2-v5-inner1.png |
这一轮修掉的四个「一直在但没暴露」的 bug
- 扇出规划器从来不看板上已有的走线(只看焊盘 / 过孔 / 钻孔)。下板做扇出时板上一根线都没有,
所以从没暴露;载板 v5 是先布了 178 条差分 + 3V3 才扇出的,不修的话过孔会直接压在 D± 上,
而几何验证抓不到(它只查连通性)。现在
EXIST_LAYERS把 1/2/15/16 层的线全登记成障碍, 线对线分层比、过孔对线不分层(过孔贯穿)。 dump_pads.py有 88 个焊盘认不到位号:pad 坐标是干净的、pin 坐标带 1 µm 噪声, 两边各自Math.round到 0.01,落在.xx5上的值进了不同的桶。改成 Python 端最近邻配(容差 0.02), 846 个里认到 805(剩下 41 个是螺钉孔和手机定位柱,本来就不属于任何元件)。 这个 bug 会静默传染:J75 pin15/16 认不到主人,「J7n 的 GND 脚整批跳过」就漏掉两个。- 第三遍「并到旁边的同网络焊盘」没有长度上限、也不查走线/钻孔,实测把 C745 pin2
一路并到 9.78 mm 外的 J64 pin3(而且那是另一层的焊盘,画出来根本不通)。
现在
LINK_MAX = 3.0,并且要求目标是通孔焊盘或同层。 pcb2_vo_vias.py的出线层写死L_TOP。v1 时代器件全在顶层没事;v2 之后 VOn_P 的 35 个焊盘 全在底层,照旧会画出 35 条悬在顶层、一头不搭焊盘的线 —— 而且fits()拿这个 lay 去筛障碍, 连净距校核都是照着空荡荡的顶层算的,一条都不会报。
两处和上一轮的计划不一样(都是实算之后改的)
- U32 pin13 的孔位没有钉在 (94.6, 71.4)。 那个位置过孔放得下(西边走廊 x ∈ [94.59, 94.625]),
但引线出不去:pin13 与 pin14 之间只有 0.20 mm,斜着往西北一定切进 pin14 的净距圈;
平着往西的话过孔又躲不开
USB5_DP的横段(y 69.75,只差 0.5 < 0.64)。 正解是往东打进 QFN 肚子里 —— 引脚环东沿 95.975 到散热盘西沿 97.600 有 1.625 mm 净宽, 自动搜索把 pin13 放在 (96.50, 70.25)、pin19 放在 (97.25, 71.50),离差分车道都在 2 mm 以上。 - J7n 的 65 个 GND 脚整批不扇了(
SKIP_PAD)。它们是bm24_pre()手写死的两条梳子 + pin52 单独出线; 让自动扇出去碰的后果实测是「十个必然失败 + pin 1/2/52 三个『成功』的孔正好落在梳子和车道上」, route_sig 还得写个删除框把它们收回去。
一处对设计文档 §10.3 的有意偏离
§10.3 写的是「功率网络每个焊盘打 3 个过孔」。这一轮 VOn_P 只给
J8n.1(这一路 4 A 的唯一去路)和 C8n1.1(2200 µF 主储能)各 3 个,
C8n21.5 A 够用。C8n6 仍是各 1 个 —— 全按 3 个来会往五块岛里再钻 50 个反焊盘,
最窄断面(下限 7.0)扛不住。这五颗只承担高频纹波,单孔 1
上一轮(2026-09-06 第十一轮):v5 差分布线全部落地
详见设计文档 §25.16 补 6。 走法表、顶层参考面、45° 斜段规矩都在那里,这里只记做了什么和踩了什么。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | pcb2_dp_long.py 整个重写(v3 存 obsolete/):pair 1 直西、pair 2 正北、pair 3 绕 Hub 北边东行、pair 4 整段顶层、pair 5 绕北边再南下跨 UP、UP 底层直东 |
本地净距全过 |
| 2 | SITE_J 按 v5 站内坐标重写:DMJ 底层 45° 斜进、DPJ 翻顶层跳过去(两排之间只有 1.515 mm,孔只能落在 y 36.805~37.31) |
五站同构 |
| 3 | pin5 / pin10 两条 +3V3 车道 + 顶层干线一起画(被 D± 夹住,后面的布线器进不去);C747 → (88.2, 71.5)、C748 → (92.5, 71.2) | pcb2_place --push、pcb2_clear 过 |
| 4 | VO4 岛主体东界 89 → 87(顶层段不许跨岛边);topref 检查 0 处跨界 |
reload → pour → inner1_verify 五块全 ✅ |
| 5 | --push:182 线 / 26 孔;DRC 634 = 间距 115(基线)+ 连接性 519 |
无新增间距错误 |
⚠️ 这一轮踩到的
- 0.5 脚距的车道之间打不了孔(孔要离两边车道各 0.645)。出 QFN 的次序只能是「最外侧先拐走,邻居走了才打孔」, pair 4 的两个孔为此上下错 0.9、DP4 先抬 0.6 再打。想改走法先在纸上按这条排一遍。
chamfer()不能对 45° 拐再切一刀:切出来的 22.5° 小段向内挪 0.1,实测把 DP4 抬升段切进 pin10 车道净距圈。已改成转向角 ≤ 46° 不切。- 上一版脚本里的 U32 坐标(x 105.55)是 v1 的,U32 早搬到 (100, 68) 了 —— 改布局后所有写死坐标的布线脚本都要重推,别只改一处。
- 同宽共线的两段下发后被 EDA 合并成一条(182 → 178),但不同宽的共线段(BM24 出线 0.11 / 0.20 / 0.28 三档)保留 —— 换挡点安全。
✅ 这一轮(2026-09-06 第八轮):载板 v4 —— 尾插槽到 J1401 正下方,J7n 到主板下方远处
详见设计文档 §25.16「2026-09-06 补 3」。 用户指令:尾插槽放在主板尾插接口下方;载板尾插座放在主板下方远处、50 mm 排线能到的地方;连接时排线走载板背面。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | SLOTS['tail'] → u 45TAIL_LANE u 44.6check() 新增一条) |
本地检查过 |
| 2 | J7n → (53.5, 37.0) rot 0;U5n (49.8, 41.5, rot 180) / R76n (52.8, 39.15) / R77n (55.8, 39.15) 挪到 J7n 北边;J6n (62.5, 41);行 D 西移 4.5 | 离 H1 4.0 / H3 2.9,pcb2_clear 816 焊盘净距 ≥ 0.20 |
| 3 | --outline --push --verify --heights |
槽 10/10;J71 回读 pin51(VBUS) 西端 (52.47)、pin4/6 北排西端 (54.65/55.0, 121.06)、奇数排南 ✔ |
| 4 | pcb2_reload → pcb2_pour --push → _refresh_geo → inner1_verify |
五块岛各 1 连通域、焊盘全在主域,最窄 7.05 / 7.20 / 7.70 / 7.60 / 20.85(下限 7.0) |
| 5 | 截图 PCB2-v4-bottom.png / PCB2-v4-ch1.png |
自己看过:槽在 J1401 下、J7n 在远端、U5n 在北 —— 还要用户确认 |
为什么 J7n 从 rot 180 改成 rot 0(不是拍脑袋)
排线只绕 u 轴弯(接口边一个 U 形回折 + 槽里一个台阶),不在面内转 ⇒ J7n 的触点图案是 J1401 图案的平移; J1401 的 pin1/2 端(G1)在 +u ⇒ J7n 的 pin1/2 端要在 −u;封装 pin51 在本地 −X、底层翻层不改 x ⇒ rot 0。 v3b 的 rot 180 把 VBUS 端放到了东边,和 §24.3「VBUS 落到 −X 端」矛盾。翻面次数(回折 1 次、台阶 0 次)算出来公头朝 +z,正好插朝 −z 的 J7n。 剩下唯一没定的是封装奇/偶排分配(§24.3 镜像位),错了只改焊盘编号,D± 换到南排西端。
上一轮(2026-09-06 第七轮):载板 v3 + C8n1 换矮封装(槽位置已被 v4 取代)
详见设计文档 §25.16 末尾「2026-09-06 补」。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 槽挪到主板远端边(v 45.5~48.5),手机位内移 2.5,器件全部收进主板下方 | 本地检查 / 净距 / 镜像回读全过 |
| 2 | pcb2_pour.py ISLANDS v3:走廊主体 + 主板下方整条带,VO2/VO4 为顶层差分收边 |
inner1_check / inner1_verify 五块全 ✅(最窄 7.05) |
| 3 | C8n1 → Rubycon 6.3TPV2200M10X10.5(C3828985):个人库建器件、c4_site.py 五页重画、PCB 导入 80+80 |
极性丝印核过(PCB2-v3-c801.png) |
| 4 | 为差分让位挪 C747/C748、U31 组、U34/C760、R721/722、R717/718 | 见 pcb2_place.py 注释 |
| 5 | 新工具 pcb2_reload.py(重载客户端解卡)、pcb2_snapshot.py |
⚠️ 这一轮踩到的
lib_Device.create()返回 undefined 但器件已建出来;copy()从系统库复制返回 null 且真没建; 个人库内copy()可用;给个人库器件modify(association.footprint = 系统库封装)会把封装复制进个人库(新 uuid)。 建完一律search+get核对,别看返回值(skill §9y)。- 铺铜「重建所有」在大量 API 改动之后必卡(本轮三次),重载后一次就通 ⇒
pcb2_reload.py然后--push。 pcb2_place.py --push的modify()里 layer 和 x/y/rot 分两次下发(一次下发 rot 偶发不生效)。
上一轮(2026-09-06 第六轮):载板 v2 —— 全部到底层 + 穿线槽(已被 v3 部分取代)
详见设计文档 §25.16。 用户两条指令:正面器件全放背面并在背面重新布局;排线通过板内细长槽穿到载板。 中途补充:器件可以放到主板投影下方(底层),方便布线。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | pcb2_snapshot.py:先把 v1 全部铜/元件抓成 _pcb2_snap_topside.json |
161 件 / 1586 线 / 416 孔 / 7 铺铜,可回滚 |
| 2 | 实测底层镜像语义(U51/J71/R761 翻到底层读焊盘再翻回来) | 本地 y 翻 → 再转 rot;两脚件 pin1 方向不变,SOT23/BM24 南北排对调 |
| 3 | pcb2_place.py 重写成 v2 |
顶层 20 / 底层 141;槽 10 条;C8n1 卧倒 keep-out;新增「压槽 / 螺钉头 / 顶层只许顶针」三项检查 |
| 4 | --clean-copper --outline --mech --push --verify --heights |
清 1416 线 + 416 孔 + 7 铺铜(另有 16 条漏网线第二遍才删掉);槽建 10/10;镜像回读全 ✔ |
| 5 | pcb2_clear.py |
跨元件净距全部 ≥ 0.20 |
| 6 | pcb2-bottom-heights.json |
底层 141 件高度图 + 10 条槽,给下板布局避让 |
⚠️ 这一轮踩到的三条(都写进 skill)
- 底层元件的焊盘位置不能推:翻层不是绕 y 轴镜像,是本地 y 翻。J23 用顶层那版的 rot 90 放到底层,
口会朝板内;改 rot 270 并把 R717/R718 的 y 对调,
--verify回读核过。 - 挖槽区域 =
pcb_PrimitiveFill.create(12, polygon)(layer 12 = MULTI)。跑道形用两段 90° 弧, 和板框一样逆时针、正角度。 - 批量
delete()一遍删不干净:1416 条按getAll()删完,回读还剩 16 条(旧 BM24 GND 梳子, 正好压在新槽上)。删除循环要跑到剩余为 0 再收。
上一轮(2026-09-06 第五轮):载板收尾 —— 已被 v2 整体作废,只留作历史
详见设计文档 §25.15。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | .claude/pcb2_vo_vias.py:25 个贴片电容各打一个过孔下到 Inner1 的岛 |
25 线 / 25 孔,全部一段直线,连接性 −31 |
| 2 | .claude/pcb2_gnd_patch.py:自己算出悬空的 GND 连通域再补孔 |
5 个域(C749.2/R710.2/U32.19/J23.A1B12/J23.B1A12)→ 6 线 / 5 孔,连接性 −10 |
| 3 | .claude/pcb2_npth.py:从铺铜反焊盘反解封装自带的非金属化孔 |
J23 里两个 Ø0.600 @ (213.660, 59.100) / (213.660, 64.900) |
| 4 | inner1_verify.py 加了过孔校核 |
五块岛 5 焊盘 + 5 过孔全在主域,最窄断面 7.20/9.10/10.15/7.35/7.60(下限 7.0) |
| 5 | pcb2_gnd_patch.py --check |
板上悬空 GND 连通域 0 个 |
| 6 | DRC | 125 = 同封装内 115(与基线一模一样)+ 连接性 10 + 间距 0 + 物理 0 |
⚠️⚠️ 这一轮最值得记住的:封装自带的孔,API 一个都读不出来
J23 的封装里有两个 Ø0.60 非金属化定位孔,正卡在 A1B12/B1A12 两个 GND 焊盘的外侧角上。
pcb_PrimitivePad.getAll() 里没有、pcb_PrimitiveRegion/pcb_PrimitiveFill 是空的、
getPrimitiveByPrimitiveId('e55') 返回空、getPrimitiveAtPoint() 只回最上面那条线。
只有 DRC(「挖槽区域 到 导线」)和内层铺铜的反焊盘看得见它们。
⇒ pcb2_npth.py 拟合铺铜环里的圆弧,用「已知过孔的反焊盘半径 − 过孔半径」标定净距
(实测 0.3583,234 个样本,离散度 0.005),再反推孔径。
另外三条
- 净距校核只看铜,钻孔是另一条规则。 第一版打的孔落在同网
J23_4的焊盘里, 校核全过,DRC 报「钻孔 到 钻孔 距离 0mil」。同网的通孔焊盘/过孔都要按DRILL=0.33再判一遍。 - 金属化的槽孔焊盘和非金属化孔待遇完全不同:线可以压在焊盘自己的孔上,
不能压在非金属化孔上。混在一起判的话,
J23.1/J23.4这种槽孔焊盘连碰都碰不得 (焊盘上任何一点离自己的槽都不足 0.80 mm)。 - 只加不删的下发必须记账。
pcb2_gnd_patch把自己画的坐标存_pcb2_gnd_patch.json, 下次先删再画。不记账实测多出一对旧坐标的过孔,而且新画的细线被 EasyEDA 并进旧的粗线里。
✅ 这一轮(2026-09-06 第四轮):栅格迷宫布线器,载板剩下的 77 个网络
详见设计文档 §25.14。 事实来源是 .claude/pcb2_route_sig.py(BM24 周围的车道
是手写死的几何,其余交给布线器)。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 新写布线器 pcb2_route_sig.py |
栅格 0.1/0.05/0.025 三档 + scipy 稀疏图 Dijkstra + 45° 整形;产出过同一份 check() |
| 2 | 77 个网络 | 431 条路径 / 991 段导线 / 160 个过孔,净距校核全过 |
| 3 | BM24 的 65 个 GND 脚(§25.8 的待办) | 两条梳子 + 12 个过孔/站,中央通道那 1.64 mm 是这一轮才用上的 |
| 4 | 抓到并修掉 R76n 装反 |
rot 0 → 180。不改的话五个站的 VBUSn 全布不通 |
| 5 | +5V 线宽 0.80 → 0.60 |
§25.10 说了段电流 ≤0.6 A;0.80 在走廊里挤不过去 |
| 6 | PWR1A 的 strokeWidthMin 0.25 → 0.15 |
+3V3_HUB 要穿 Hub 卫星圈 0.659 mm 的缝;载流够(420 mA / 0.15 mm 可载 ~0.8 A) |
| 7 | 布完线重新铺铜 | 不铺的话 DRC 多出 445 条「铺铜区域到过孔/钻孔」(新过孔没有反焊盘) |
| 8 | DRC | 166 = 同封装内 115(与 §25.8 基线一模一样)+ 连接性 51 + 间距 0 + 物理 0 |
| 9 | 45° 化(用户拍板) | sharp_corners / chamfer45 / smooth_micro 三个工具放在 pcb2_route_dp.py,两个脚本共用。差分对 10 → 0、信号线 72 → 0,DRC 不变 |
⚠️⚠️ 这一轮最值得记住的一条:expand() 会静默丢掉路径末端的过孔
pcb2_route_dp.expand() 打过孔的条件里有 i + 1 < n - 1 —— 最后一个点被显式跳过。
于是「走到目标正上方再换层落下去」这种路径,过孔没了,两层的铜各画各的、中间断开。
三道校核全都抓不到:净距校核只看撞没撞(断开当然不撞)、
inner1_verify 只看铜面、DRC 的连接性又要 location.reload() 才刷新。
踩到两次:布线器自己的路径断了 5 个网络;手写的 bm24_pre() 断了 50 个 GND 过孔
(DRC 里 GND 55 条,补上之后掉到 10)。
⇒ _end_via():末点上有换层就补一个重复点,所有路径(含手写的)都要过这一道。
另外三条(都写在 §25.14 里)
- 障碍要先胀
QERR再栅格化,否则距离场高估 0.08 mm,净距校核在下发前一刻才炸。 location.reload()会被「离开该页面」确认框挡下来(文档有未保存改动), 点「留在该页面」= 根本没重载,客户端仍卡在后台任务上, 于是铺铜管理器…点了没反应。重载前先save()。- 先给所有焊盘留好出线段再开始布线(escape routing 先行), 否则先布的网络会把后布的网络出焊盘那 0.6 mm 占掉。
✅ 这一轮(2026-09-06):载板 Inner1 分割电源面
详见设计文档 §25.11。 事实来源是 .claude/pcb2_pour.py 的 ISLANDS,改铜面改那里再 --push。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 五块 VOn_P 岛 + GND 底填落到 Inner1 |
优先级 L15 VO1<VO2<VO3<VO4<VO5<GND(岛先占地盘) |
| 2 | 每条边界都按具体那颗排母针的反焊盘卡出来 | 9 条边界逐条列在 §25.11 的表里;最窄的颈是 VO3 的 7.40 mm(被 J32.27/J32.35 夹住) |
| 3 | 新脚本 inner1_check.py(下发前预测) |
按 ISLANDS 多边形 + 异网焊盘/过孔反焊盘栅格化,五块全过 |
| 4 | 新脚本 inner1_verify.py(下发后实测) |
读回 EDA 真吐出来的铜再栅格化:五块各 1 个连通域、5/5 本网通孔焊盘在主域,最窄断面 7.20/7.60/7.70/11.70/12.90 mm |
| 5 | 新脚本 inner1_diffref.py(§10.1) |
顶层 5 段 *J 差分下方都只有 GND-INNER1 一块铜、覆盖 100% —— 这条约束从「可以接受」升级成「已实测满足」 |
| 6 | 查出铺铜默认是十字花连接 | 铜面把自己的焊盘挖空只留 4 条 0.254 mm 辐条 ⇒ 校核量到「0.3 mm」。给 VOn_P 挂 PLANE-DIRECT(connectMode:1)后铜面是一整块。见下 |
| 7 | pcb2_rules.py 收编该规则 |
新增 PLANE_RULE / PLANE_NETS,--push 建条目 + 指派,--check 十三项全过 |
| 8 | 新脚本 shot_pcb_layers.py |
只显示指定铜层截图(可带区域),截完自动恢复图层可见性 |
| 9 | DRC | 499 = 同封装内间距 115(与基线一模一样)+ 连接性 384 + 间距错误 0。连接性从 396 掉到 384,正是五块岛接上的那 12 处 |
截图 PCB2-inner1.png(整板)/ PCB2-inner1-left.png / PCB2-inner1-right.png(走廊两半,
能看清五块岛的轮廓和两条颈从排母针缝里穿下去/上来)。
⚠️ 这一轮最值得记住的一条:铺铜的「段数」不能当连通性判据
getState_PourFills() 把一块铜面吐成十几个 fill,那多半是切块(相邻块共边)不是断开。
「17 段」既可能是一整块被切碎、也可能真碎成 17 座孤岛,段数分辨不出来。
唯一靠得住的是把所有环画进同一张栅格再做连通域 —— 这就是 inner1_verify.py。
而且十字花连接时那十几个 fill 里有一部分是 fill:false 的描边辐条(不是填充区),
只画填充区会得到假的断路。两个坑叠在一起,skill §9j-2 有完整实测表。
⚠️ 另一条:优先级的步长是 2,不是 1
同层建 6 块铜面,回读的优先级是 1,3,5,7,9,11(同层只有一块时是 1)。
所以脚本里只能断言相对次序,不能断言「第 k 块 == k」——
pcb2_pour.py 第一版就是后者,白报了一次「优先级和计划对不上」。已改成断言次序。
✅ 这一轮(2026-09-05 夜):换件实施全部落地
详见设计文档 §22.3f 末尾的实施清单 + 新的 §25.9。 上一轮那三条硬约束消掉了两条。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 实测两颗新符号的引脚几何 | 公母完全一样:第 r 行 = pin 2r−1(左, cx−30)/ 2r(右, cx+30),第 1 行 y = cy+95 |
| 2 | p5_btb.py 换件 |
器件换 C55020601;引脚公式 r/41−r → 2r−1/2r;ROW0 100 → 95;补上「坐标必须是 5 的倍数」断言 |
| 3 | c1_conn.py 换件 |
器件换 C5307344;引脚公式和 ROW0(95) 都不用动 |
| 4 | P5-BTB / C1-CONN 重生成 | verify_sch 两页 PASS(C1-CONN 要带 --nc c1-nets-nc.json,U34.3 是有意悬空)· xpage 0 漏接 · port_audit 下板 513 / 载板 330 可疑 0 |
| 5 | §22.2 逐针表重写 | 两块板共用一张表(VO1_P 两边都是 1/3/5 脚),旧的「两套编号」警告整节作废 |
| 6 | 载板 PCB 从原理图导入变更 | 只有 14 条:C1-CONN 那 7 个元件各一条「移除+增加」(整页重画 ⇒ UUID 全新)。「包含设计规则」没勾,11 条规则原样保留 |
| 7 | 五个 Ø10 电解整体上移 2.5 mm | BAND['c1'] 的 v:−11.0 → −8.0。见下,这是换件逼出来的 |
| 8 | 载板重摆 / 重扇 / 重铺 | 161 元件本地四项过 · pcb2_clear.py 816 焊盘过 · GND 扇出 210 孔(和换件前一样)· Inner2 重铺 213 段 |
| 9 | DRC | 511 = 同封装内间距 115(和基线一模一样)+ 连接性 396(没布线)+ 其它 0 |
截图 PCB2-carrier-v4.png / P5-BTB.png / C1-CONN.png。
⚠️ 换成通孔件逼出来的连锁改动:五个大电解必须上移
直插排母 = 顶层也占位,而 J31/J32 的焊盘区高 4.24 mm。 原来的走廊里,上排三站的罐底 y=68.25、下排两站的罐顶 y=66.95,中间只剩 1.30 mm —— 怎么摆都塞不进去(贴片排母时代它只在底层,所以从来没露出来过)。
罐体在服务带里 u 5.75~18.25 这一段是独占的(行 1/2/3 的件都从 u=21 起), 所以 v 可以一直收到「不进手机投影」为止(v ≤ −6.25)。取 v = −8.0(和行 2 齐平):
| 罐体边 → 手机板边 | 3.00 mm(keep-out 边 1.75) |
| keep-out 边 → J31/J32 焊盘 | 1.63(上排)/ 1.43(下排)mm |
| 实测可行区间 | v ∈ [−9.0, −6.75] 本地四项全过,取中间偏保守 |
这一轮踩到的三个坑(都已写死在脚本里)
pcb2-parts.json不能在元件摆好之后随便重抓。 焊盘的x/y跟着元件转, 但pad数组里的 w/h 不转(方向记在pad.rotation上)。 摆好之后直接抓会得到「位置转了、尺寸没转」的四不像包围盒,pcb2_place.box()再按 rot 翻一次 ⇒ 全错:实测凭空报出 12 条假问题 (手排的 0402 互相重叠、J23 越框)。 ⇒ 新脚本.claude/pcb2_parts_dump.py把焊盘位置反转回元件本地系再存, 和「刚导入、全是 rot=0」时抓的那一份等价。pcb_fanout.clean()在载板上是破坏性的。 它删的是「layer !== 11 的所有导线」—— 下板除板框外没有别的线所以没事,载板上却有 丝印 50 + 装配 100 + 文档 20 = 170 条 会被一起删光,而且 DRC 和几何检查一条都不报。 ⇒pcb2_fanout.py里另写了clean2(),只删过孔和信号层(1/2/15/16) 的导线, 并回读断言 3/9/13 三层还在。 ⚠️ 板框是 layer 11 上的pcb_PrimitivePolyline,不是Line—— 「剩余导线里有没有 layer 11」不能当板框还在的证据(第一版断言就是这么写错的)。- 通孔焊盘不需要扇出:
pcb_fanout.one_net()的todo本来就带not p['hole']。 所以SKIP_HDR直接关掉即可,J31/J32 那 80 个脚自己就是过孔, §25.8 里「33 个 GND 脚扇不出去」这条自动消失。
⛔ 还剩的一条硬约束(另两条已解决)
1. C2~C6 压在 J24 位置上 / 3. 两板之间没有立柱位 —— 这两条是下板的事,
留到下板重排接口区时一起处理(下面「下一步」第 1 件)。
C2/C3 重选时罐高必须 ≤ 11.5 mm。现在的 C22383480 商城一个参数都没标,
§22.3d 那个「约 12 mm」是估的。⚠️ 别换成聚合物(低 ESR 会让输入滤波欠阻尼,同 §6.2 的理)。
✅ 追加一轮(2026-09-05 夜):排母逐针分配按几何重排
铺 Inner1 之前摸底,摸出 §22.2 那张「定稿」表和载板几何对不上,用户拍板改(方案 A)。
全过程见设计文档 §25.10,新的逐针表在 §22.2(事实来源仍是 .claude/p5_btb.py)。
硬冲突:排母南排前 16 位全是 GND 焊盘(Ø1.70 / 间距 2.54 ⇒ 缝里只剩 0.44 mm 铜网),
而 4 A 按 IPC 内层 0.5 oz 要 6.97 mm ⇒ 4 A 的电源面穿不过排母的地针行。
09-04 那版偏偏把 VO4_P/VO5_P 放在 J32 的北排,它们的站却在排母南边。
新规矩:北排喂北边的站,南排喂南边的站,一路都不许跨行。 J31 正对 site1(北)+site4(南),J32 正对 site3(北)+site5(南),site2 取 J31 北排最右三针。
| 09-04 | 09-05 | |
|---|---|---|
五路 VOn_P 最远曼哈顿 |
~120 mm(VO4 要绕排母端头钻手机 2 底下) | ≤42 mm,全部同侧 |
+5V |
3 针,Inner1 上要一条 150 mm 的横贯脊 | 10 针分散取电,段电流 ≤0.6 A ⇒ 顶层走线即可,不占内层 |
| GND | 33 针 | 26 针(0.77 A/针) |
| Inner1 规划 | 5 岛 + +5V + +3V3_HUB + 底填 |
5 块 VOn_P + GND 底填 |
网络表一个都没变,只是落点变了。五路的「正对面 3 针 GND」(§15.4)一条不少,
p5_btb.py 的 plan() 逐行断言。
原理图画法改了一处:GND 也走 netport(不再画地母线)—— 重排后 GND 散成若干段,每段配母线 + 地旗会让旗掉进相邻行的端口里(行距 10 / 旗高 15~20)。
验证:verify_sch 两页 PASS · xpage 0 漏接 · port_audit 下板 544 / 载板 361 可疑 0 ·
载板导入变更 65 条全是 J31/J32 的「修改网络」(无元件增删)·
PCB 上 80 个焊盘的网络和逐针表逐行比对一致 ·
重扇 210 孔 + Inner2 重铺 185 段 · DRC 518 = 同封装内 115(与基线一致)+ 连接性 403 + 其它 0。
✅ 这一轮(2026-09-06 第二轮):布线开工,连接器侧 5 对差分完成
详见设计文档 §25.12。 事实来源 .claude/pcb2_route_dp.py(SITE_J 是路径,同文件里带本地校核)。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 抓到并改掉一个摆位错误:U5n 装反 | USBLC6-2SC6 是直通器件,朝连接器那一排原来背对着 J7n。BAND['u5'] rot 0→180。SOT-23-6 对称,6 个焊盘落在完全相同的位置,只是引脚编号对调 ⇒ 无新间距冲突 |
| 2 | 跑完整的返工流程 | 重摆 161 元件 → pcb2_clear 816 焊盘过 → GND 扇出 clean/dump/push 210 孔 → 重铺铜 → inner1_verify + inner1_diffref 全过,各项数字与改之前完全一致 |
| 3 | 判定自动布线器在载板上不可用 | 配置逐条回读断言(所选网络 22 个 / 移除已有 / 只勾顶底层)之后跑:只画出 J23 自己那两条 A6↔B6/A7↔B7,全板 0 过孔,30 秒"跑完"。pcb2_route_diff.py 连同 --rollback 留着,别再试 |
| 4 | 新工具 pcb2_route_dp.py |
路径按站内坐标写一遍映射到五站;本地校核线-焊盘/线-过孔/线-线/过孔-×4,一般 0.20、对内 0.15;--corridors 扫底层横向走廊 |
| 5 | 5 对 USBn_DPJ/DMJ 布完 |
DP 全程顶层 5.05 mm;DM 两端次序相反必须交叉,两个过孔沉底层穿过去,9.19 mm,偏斜 4.14(容差 5)。60 段导线 + 10 过孔 |
| 6 | 出线宽度按几何算死 | BM24 焊盘阵列里 0.11 mm(上限算出来是 0.1197,取 0.12 会差 0.00015 卡线);出阵列后两线还隔 0.35 时 0.20;分开后 0.28。DIFF['strokeWidthMin'] 随之降到 0.10 |
| 7 | DRC | 479 = 同封装内 115(与基线一致)+ 连接性 364 + 间距错误 0 |
截图 PCB2-dp-site1.png(能看清 DPJ 走顶层、DMJ 沉到底层从它底下穿过去)。
⚠️ 加完线和过孔一定要重铺铜再看 DRC
不重铺时刷出 40 条间距错误(「到过孔」20 +「到钻孔」20)—— 那是铺铜还不知道新过孔、没让开它们。重铺之后 GND-INNER1 环数 217→227、 INNER2 330→340,正好是 10 个新过孔的反焊盘,40 条全消失。
✅ 这一轮(2026-09-06 第三轮):6 对长差分布完,载板差分对收工
详见设计文档 §25.13。 事实来源 .claude/pcb2_dp_long.py(坐标 + 逐条理由)。
决定全局走法的是 x 112~116 那道隘口:Hub 北边的 C751(1206,底层)
加 C815 的 GND 扇出孔,把北高速掐成「底层低窗 0.535 mm(1 对)」+「底层高窗」+
「顶层窗口 0.58 mm(1 对,C751 在底层所以顶层是通的)」;
南边被 Hub 的 A 圈 0402 封死(只剩 y≈56 一条缝)。
三对东行的只能一对一个窗口,而南北次序被 U32 的引脚次序锁死 —— 正好对得上,不用额外交叉。
pair4 是全板唯一要横穿其它对的一对,一出焊盘就上顶层南下。
新脚本 / 新用法:
/c/Python314/python .claude/pcb2_net_conn.py # 按实际铜算连通域(不看 DRC 面板)
/c/Python314/python .claude/inner1_diffref.py --routes # §10.1 沿实际走线逐点查参考面
/c/Python314/python .claude/_refresh_geo.py # 刷 inner1_* 三个脚本的输入缓存
⚠️ 三个这一轮踩到的坑
drc_read.py不加--pcb2跑的是下板。 第一次读到 649 条全是下板的网络。- DRC 的连接性结果在程序化建线后是陈旧的,
location.reload()才刷新。 刚下发报 403(每个网络还报 2~7 条"断开了连接"),重载后是 338 —— 比布线前少 26,正合预期。 间距错误那一支倒是即时的。 - 45° 切角会把线甩进旁边焊盘的净距圈。
chamfer()的 r 从 0.6 降到 0.35, 焊盘阵列附近还要多插一个共线点把切角压到 0.15~0.2。
下一步
| # | 做什么 | 备注 |
|---|---|---|
| 1 | 收尾 Hub 卫星圈那三段(+3V3_HUB 2 段 / VBUS_UP 1) |
要用户拍板:方案 ① R711/R716/R720 从半径 8.0 挪到 9.5(这三颗的 GND 扇出要重做;挪完走 §25.12 那套返工流程:pcb2_place --push → pcb2_route_dp --dump → pcb2_route_sig --push → pcb2_gnd_patch --push → pcb2_pour --push → reload → DRC)。SCL/POGO5_VDN 已通,U34 已挪。见 §25.15 末尾 |
| 2 | 下板 PCB 导入变更 + 接口区重排 | 删了 15 个连接器 + 53 个 7xx 器件,新增 J24C2C6,必须挪;C2/C3 顺便重选(罐高 ≤11.5)。J24/J25 的 40 个焊盘按 pcb2-header-pads.json 减 (30,5) 摆,别按引脚号推理 |
| 3 | 让用户截图确认载板布线 | 已截 PCB2-vo-vias-site1.png(VO1 站的电容打孔)/ PCB2-gnd-j23.png(J23 那两个屏蔽脚)/ PCB2-u34-j31.png(U34 新位置)。⚠️ EasyEDA 窗口必须在前台,在后台时 document.hidden = true、画布不渲染,getCurrentRenderedAreaImage() 只回 88 字节的空 PNG |
⚠️ 2026-09-05 从实物照片新看出来的两条(都还没处理)
1. 原厂排线长约 85~90 mm,只给了 3 mm 水平距离 → ✅ 用户 2026-09-05 拍板:不管,装配时折叠。
照片上排线从主板下边缘一路走到尾插小板(板上印 MAIN + 箭头),
按主板宽 67.05 mm 标定,两个公头相距约 87 mm,而载板的 J7n 在 v = −3.0、
离手机板边只有 3 mm ⇒ 有 80 mm 以上的余量。
用户原话「排线长度不用管,我到时折叠一下」 —— 所以 J7n 不为此挪位,
也不为此改 §13.5 的「台阶式下折」。留一条给装配:折叠的那一坨会占手机上方的高度,
定压板高度(h_up)时要把它算进去。
2. J7n 并没有像注释说的那样「正对 J1401」,横向差 3.9 mm。
mi8_contacts.json(卡尺校核过)记的 J1401 中心是 u = 53.58(长轴沿 u,占 47.4659.71),
而 48.2)撞上 0.7 mm,
要连着 pcb2_place.BAND['j7'] 是 u = 57.5。
不致命(软板本来就有余量、而且照片上原厂排线自己就是歪的),
但不能直接改成 53.58 —— 那会和同一行的 j6(EDL 座,占 u 39.8j6 / c3 一起重排。和上面第 1 条一起决定。
✅ BM24 的镜像位 2026-09-05 已实测(§24.3):结论是「不用改」。
用户量出「G1 在右端 / 靠板边那一排是奇数排」,照片确认排线从板边直出,
所以载板 +Y 排(朝手机那一排)= 奇数排 ⇒ ODD_ROW_TOP = True 是对的;
顺带第二位(贴片方向)也自洽:VBUS 端落在 −X = 手机左端,和「52=GND / 51=VBUS」对上。
打样前已经没有会导致重打板的未知量了。
⚠️ 但同一批照片带出两条新的机械问题,见下一节。
上一轮:排母选型(2026-09-05,用户授权「你来定选型」)—— 已实施
详见设计文档 §22.3f。 两个占位到此全部消灭。
| 型号 | 立创 | 关键参数 | |
|---|---|---|---|
| 下板 J24/J25 公头 | AZ-PZ2.54-V11-2*20P(宝讯) | C55020601 | 塑高 2.5、配合针长 6、端接针长 3;3 A/针、镀金、直插 |
| 载板 J31/J32 母座 | PC104-2*20(洲日) | C5307344 | 塑高 10.5、行距 2.54、2.5 A/针、直插 |
对插 = 2.5 + 10.5 = 13.0 mm(塑料对塑料定位),满足 §22.3d 的 ≥13 mm。 把库里「2.54 间距 + 标了塑高」的排母全扫了一遍,10.5 mm 这一档只有 PC/104 一族, 其余全是 8.3~8.5(对插只有 11 mm,够不到),而 2.54 双排排针的塑高全是 2.5,没法从公头补。
改成插装是比高度更重要的收获:80 个通孔脚本身就是过孔, 直接连 Inner1/Inner2 —— §25.8 里被迫跳过的「J31/J32 那 33 个 GND 脚扇不出去」自动消失。
(这一节原来的「三条硬约束」与「编号体系已实测」已经实施完,结论搬到文首「这一轮」和设计文档 §22.3f / §25.9。)
开工前必读(按顺序)
五路直供电源板设计.md§22.7 —— 两个 Board 各有哪些页、位号怎么分、验证结果。- §22.2(J24/J25 逐针表 + J31/J32 编号对照)、§22.4 末尾(EDL 驱动与载板开关)、 §22.6(风扇 + TACH 分压)、§13.5(USB 最终走方案 D:原厂主板-尾插排线 + 载板上同型号 BM24 座;那里也留着已作废的方案 A / C 记录,和「买一块尾插小板当量具」的测法)。
.claude/skills/easyeda-sch-geometry—— §9j-2 是最新一轮(Inner1)加的: 铺铜的焊盘连接方式connectMode藏在 DRC 规则里,默认十字花会让几何校核报假断路; §9j 第 5 条补了「优先级步长是 2 不是 1」。 §9m / §9n / §9o 是换件那一轮加的: 包围盒不能在元件摆好之后重抓 / 通用clean()会删掉丝印 / 「从原理图导入变更」怎么读全。 §9j~§9l 是铺铜那三条,§6b / §6c / §6d 是「几何量必须读回、不能推」的血泪条目。
上一轮(2026-09-05):布线前置三件 + 一个必须记住的事实
全部见设计文档 §25.7。 这一轮一根走线都没画(用户拍板停在排母上,见上)。
| # | 做了什么 | 结果 |
|---|---|---|
| 1 | 载板设计规则整套下发 .claude/pcb2_rules.py |
--check 十一项全过 |
| 2 | 11 对差分对(不是 7 对) | 5 长段 + 5 短段 *J + UPSTREAM |
| 3 | Inner2 整板地平面 | 一块 GND-INNER2,重建后 81 块填充,已存盘(.claude/pcb2_pour.py) |
| 4 | 两个中跨 M3 (76.6, 80.1) / (148.1, 80.1) | §25.6 ⑤ 那条待办关闭;pcb2_clear.py 816 焊盘全过 |
| 5 | 查清排母占位件是 SMD、排距 4.80 | 成了眼下的路障,见上 |
| 6 | 解决了「铺铜优先级设不上」 | 优先级 = 同层内的创建顺序,先建的赢;对照实验见 skill §9j |
| 7 | GND 扇出 .claude/pcb2_fanout.py |
210 个过孔落盘(全 GND,Ø0.610/0.305),出线 105 顶 + 21 底 |
| 8 | 查出 BM24 的一个硬约束 | 五颗 J7n 的 65 个 GND 脚里 51 个打不下过孔(0.35 间距),见 §25.8 |
| 9 | DRC 基线 | 全部 551 = 同封装内间距 115(封装自带)+ 连接性 436(没布线)+ 间距错误 0 |
⚠️ 看 DRC 只看「间距错误」那一支
「同封装内间距错误」那 115 条是 J71~J75(0.170 mm) / U32(0.125) / J23(0.200)
封装自带的引脚间距,安全间距规则 0.2 mm 比这几颗连接器自己的脚距还严,必然报,
三个数都在嘉立创四层工艺下限 0.1 mm 之上、可以打。
别去调低全局安全间距来「修」它们,那是把全板保护一起放掉。成因逐条见 §25.8 末尾。
为什么差分对是 11 对:载板上每一路 D± 被 ESD 器件 U5n 切成两段 ——
长段 USBn_DP/DM(U32 → U5n,走底层)+ 短段 USBn_DPJ/DMJ(U5n → J7n,约 5 mm,走顶层)。
两段都要建对,否则短段会按普通信号布、在 ESD 那头留一截不受控阻抗。
中跨孔为什么放在手机之间的缝下(y≈80)而不是走廊正中(y≈67.5): ① 要支撑的是 y 85.5~130 那一排手机,80.1 离它 5 mm、67.5 差 18 mm; ② 走廊正中被 C811/C831 两个 Ø12.5 电解罐夹着,只剩 6.9 mm 高的缝, 净间距 2.90 mm;缝下那两处是 5.28 mm。 ⚠️ 再往上会被顶针 J48/J52(y 87.96)逼住,y=83 掉到 2.41、y=84 只剩 1.41。
这一轮踩到的四条 EasyEDA 坑(skill §9j~§9l)
- 铺铜优先级是「数字小的先铺、占地盘」 —— 对话框底部自己写着。
下板
pcb_pour.py里 GND=1 / 电源岛=5 是反的(那边铜面从不重叠所以没出事), 抄到载板之前必须翻过来(Inner1 是分割电源面 + GND 底填,全靠优先级互相裁)。 create()的pourPriority参数不生效 —— 优先级 = 同层内的创建顺序。 ⚠️modify()补设不生效还会把客户端弄卡死。 ✅ 对照实验已做,规则确认:先建的赢。电源岛先建、GND 底填最后建。pcb2_pour.py的plan()顺序就是优先级,--push会回读断言。- 「重建所有」点了没反应 = 客户端卡在后台任务上(窗口底部一条 0% 进度条),
location.reload()后一次就通。⚠️ 重载会丢掉重载前建的铺铜边框,即使 save 过 —— 所以「建边框 → 重建 → 确认 → save」必须一气呵成。 - 每个下发脚本必须自己
openDocument:这一轮因为上一条命令开了 PCB1, 把下板的 14 块铜面重铺并存了盘(无损失,但两边返回值都看着正常)。
另记一条正常行为:preserveSilos=false 时区域内没有本网焊盘/过孔,整块会被当孤岛删光
(边框还在、填充是 0)。所以扇出必须排在铺铜之前。
上上轮(2026-09-05 凌晨):载板切 4 层 + 布线前四处返工
全部见设计文档 §25.6。 这一轮没加新东西,是把上一版摆位里四处会咬人的地方修掉 —— 四处全部是「原理图侧看不出来、包围盒检查也过」的那一类。
| # | 改了什么 | 为什么 |
|---|---|---|
| 1 | 载板 2 层 → 4 层 | 11 对 USB HS 要 90 Ω,两层板 1.5 mm 介质做不出来;20 A 没有内层电源面;两面都是元件面 |
| 2 | J23 左口袋 → 右口袋 | §25.2 当初只核了下游。上游 J23(x5)→Hub(x110) 曼哈顿 140 mm,手册上限 152。挪完上游 104 / 最远下游 113 |
| 3 | U31 (25,42) → (105,71) | §22.7 写了「就近 Hub 供电是 PCB 摆位约束」,上一版没落实,离 90 mm。现在离 U32.pin64 8.62 mm |
| 4 | Hub 26 件网格粗排 → 逐脚对位手排 | 见下 |
| 5 | M3 孔 (8,127)/(212,127) 挪走 | 压在 CH1/CH3 手机本体投影里,其中一个和定位孔 P1H2 重叠 0.93 mm |
⚠️ 载板的层角色和下板是镜像的:载板 Inner1 = 电源面 / Inner2 = 完整地平面,
因为 U32 在底层,所有长距离 D± 都从底层出发,底层的参考面是 Inner2。
反过来的话底层 D± 就以分割的电源面为参考。嘉立创四层 1.6 mm 叠层上下对称,
所以 W0.28 / S0.18 照搬,不用重算。
Hub 手排真正多做的事是让出三条通道(网格排法做不到):西侧扇出(10 根下游 D±)、
南侧竖井(XTAL + UP_D±,宽 2.16 mm)、晶振窝。实测对位距离:
RBIAS 1.43 / VDD18PLL 1.43 / 各 +3V3 1.462.30 / 配置电阻 1.43.2 /
晶振 4.955.76(是通道的代价,不是疏忽,负载电容贴端子 1.51.8 已做到)。
新写的检查:.claude/pcb2_clear.py(焊盘级净间距)
/c/Python314/python .claude/pcb2_clear.py
按焊盘真实形状两两算净间距,分「同封装内」(只报负值)/「跨元件」(判据 0.2 mm)。
M3 孔那个错误只有它抓得到 —— 自由焊盘不属于任何元件,
pcb2_place.py 的包围盒检查根本不看它们(成因写进 skill §9i)。
写它踩到两个 API 陷阱,都写进 skill 了:
- §9g 焊盘归属只能靠
primitiveId前缀 ——getState_ParentComponentPrimitiveId在getAll()回来的对象上不存在,try/catch 包住会静默地把所有焊盘当自由焊盘。 - §9h ⚠️ 焊盘的 rotation 是弧度、元件的是角度。按角度判会把所有旋转过的 矩形焊盘长短边认反 —— J23 的 12 个信号焊盘被误报成两两重叠 0.3 mm(真实 +0.2)。
本轮验证结果(全过)
pcb2_place.py 本地四项 + M3 孔断言 ✔ 161 元件(顶 133 / 底 28)
pcb2_clear.py 跨元件净间距 >= 0.2 mm ✔ 814 个焊盘
pcb2_clear.py 同封装内负间距 ✔ 0 对
铜层数 ✔ 2 -> 4(id 1/15/16/2)
截图 PCB2-carrier-v2.png。
上一轮(2026-09-04 晚)做完的两件
- ✅ 顶针占位型号换掉了:
C9900001338→ YTC1P-2510-01(立创 C5221288), 配 3.0 mm 立柱(原来文档写的 2.0 mm 买不到,见 §22.3e 的整族对照表)。c4_site.py已改,C4-SITE1~5 五页重生成,六项验证 + 跨页对账 + port_audit 全过。 - ✅ C1-CONN 抓到并修好一处真断线:GND 旗写在 y=362,旗被吸附到 360、导线端点留在 362,
旗压在导线中段 → J31 pin1 的地是断的。
verify_sch/ DRC / 跨页对账全过, 只有port_audit.py抓得到。已在c1_conn.py/c4_site.py加「坐标必须是 5 的倍数」断言, 成因写回 skill §8a-2b。⚠️ 上一轮记的「可疑 0」不能当数,每轮都要重跑。
上一轮(2026-09-04 深夜):USB 定为方案 D,载板改造已完成
方案 D(设计文档 §13.5):手机 J1401 ──原厂「主板→尾插」排线── 载板上一颗同型号 BM24-50DS 座 ──> 载板上的 USB 电路。 用户拍板:天线自己另买插主板上,麦克风 / 震动马达不要,「只要能开机正常运行系统就行」。
载板改造做完了,三道验证全绿(verify_sch 每页 PASS / xpage 0 漏接 / port_audit 载板 330 个可疑 0、下板 513 个可疑 0):
| 页 | 改了什么 | 脚本 |
|---|---|---|
| C5-J1401A / B(新) | 五颗 J71~J75 BM24-50DS 座(C424561),A 放 CH1 |
.claude/c5_j1401.py |
| C5-AUX(新) | 每路 U5n USBLC6 ESD + R76n 56k(CC1→VBUS) + R77n 100k(口温→GND) | .claude/c5_aux.py |
| C3-USB(改) | 删掉 J17~J21 五个 1.25mm 出线座;D± 不再经过这一页,只留 VBUS 负载开关 | .claude/c3_usb.py |
USB 链路现在是:C2-HUB 的 U32 → C5-AUX 的 U5n → C5-J1401 的 J7n → 原厂排线 → 手机 J1401。
NC 清单 .claude/c5-nets-nc.json(每颗座子 34 个悬空脚,含必须悬空的 CC2)。
截图:C3-USB.png / C5-J1401A.png / C5-AUX.png,都看过。
调试串口(pin24 TX / pin29 RX)故意没引:1.8V 电平接 ESP32 要加电平转换, 且 pin29 夹在两个地脚中间会把排版挤垮。要用是一次改板。
上一轮(2026-09-04 深夜):Board3 画完又作废 —— USB 方案改回 A(后又改成 D)
先按方案 C 画完了 Board3(J1401 转接小板),当晚用户确认「用排线连接」之后
改回方案 A:装原厂尾插小板 + 一根改端子的 USB-A 转 C 线。
Board3 / phonerail-usbtap / PCB3-USBTAP 已从工程删除,
生成脚本挪到 .claude/obsolete/t1_usbtap.py。理由对照表见设计文档 §13.5。
方案 A 当时是最优的,但用户随后提出「直接用原厂排线连主板和载板」—— 那就是方案 D,比 A 更紧凑、电路全在载板上好改。方案 A 已作废, 它唯一的优势(保留天线馈点)被「用户自己另买天线」抵消了。
作废但仍有效的四条结论留在 §23:立创 BM24 封装编号是错的(同时佐证了 §13.4 的引脚表)、 BM24 只有 10 次插拔寿命、Rp/Rd 分压核算、以及两条 EasyEDA API 经验(skill §8b / §8c)。
上一轮(2026-09-04 深夜):BM24-50DS 封装自建完成
下一步四件里的第 2 件做完了。 器件在个人库:BM24-50DS/2-0.35V(51) HRS-PIN
(771dbf002b714bb79cde47cf86aa0fb1,封装 940f093c1578426aa81e91ebf0bfbd76),
c5_j1401.py 已改用它,C5-J1401A/B 重生成,三道验证全绿
(verify_sch 两页 PASS / xpage 0 漏接 / port_audit 载板 330 个可疑 0)。
生成脚本 .claude/fp_bm24.py,封装截图 FP-BM24.png。逐项尺寸与来源写在设计文档 §24。
几何不是外推、也不是抄立创:从 Hirose 官方样本(D47680_en)的 PDF 矢量图里 逐段反算的,50 芯的 A/B/C 表直接可用。信号焊盘 0.18 × 0.48 @ 0.35 间距、两排 y = ±1.06、 电源焊盘 0.49 × 0.76 @ x = ±6.03。
⚠️⚠️ 但是多出一件必须做的事:镜像位实测(§24.3)
原来文档里写「两种候选正好差一个 180° 旋转,转贴片方向就能切换」——这句是错的,
候选有 4 个:2(1 脚在哪一端)× 2(奇数脚在哪一排)。
前一位靠旋转能纠,后一位是镜像,错了要重打载板(D+ 会落到 45 脚 SPKR_OUT_P)。
BM24 的 DS 座是完全对称件(Hirose 样本注 2:no polarity,本体两端与上下两排全一样), 所以「哪一排是奇数排」查图纸查不到 —— 它是小米8 那块板的布局事实,只能量。
量法(两个探点,零成本零风险,打样前必做):
- 两个电源触点(焊盘大好戳)分别对主板地量电阻,~0 Ω 的是 G1 = 1/2 脚那一端。
- 从 G1 端数,两排各自的第 2 个信号触点对地量:通的那排是奇数排(pin3 = GND;
偶数排第 2 个是 pin4 =
VREG_L19A_3P0,不通)。
结果若是「奇数排在 −Y」,把 fp_bm24.py 的 ODD_ROW_TOP 改成 False 重跑一遍即可。
上一轮(2026-09-04 深夜):载板 PCB 从零布起
板框 220 × 135(R5 圆角),五块主板 3+2 双排面对面、第二排居中(用户拍板)。
原理图 161 个元件全部导入并摆好,本地四项检查全过(位号齐全 / 不越框 / 分层重叠 / 顶层不压主板投影)。
逐项见设计文档 §25,脚本 .claude/pcb2_place.py,截图
PCB2-carrier-v1.png(全板)/ PCB2-site1.png(CH1 位)/ PCB2-j23.png。
已落盘的东西:板框 1 条闭合多段线、载板自己 4 个 M3、35 个手机定位孔(NPTH Φ2.5)、 5 个手机位丝印外框 + 标签、161 个元件(顶层 133 / 底层 28)。
⚠️ 抓到一个会让侧键永久短路的封装错误(§25.3)
立创给顶针 C5221288 的封装焊盘是 Ø2.5,而小米8 四个侧键触点最近的一对
(J601↔J604)只隔 2.217 mm —— 两个焊盘直接叠在一起,音量+ 会被永久按住。
顶针法兰其实只有 Ø2.0,是立创把焊盘开大了。原理图侧完全看不出来。
已自建 POGO-SMD1P_D1.9(焊盘 Ø1.9,净间距 0.317 mm),c4_site.py 改用新器件,
C4-SITE1~5 重生成、三道验证全绿、PCB 重新导入。
三条布局上的硬决定
- J31/J32 排母在底层(下板公头朝上,载板母座必须朝下)。
「第 r 行对第 r 行」不靠脑补:
--push会把载板侧 80 个焊盘坐标导出到.claude/pcb2-header-pads.json,下板照数值摆即可(下板目标 = 导出值 − (30,5))。 - USB Hub 也在底层、压在通道正中 (110,67) —— 放左右口袋的话最远那台 D± 要走 200+ mm,超 USB2517I 手册的 152 mm 上限;放中间后最长 92 mm。 ⚠️ 布下板时别让 C2/C3(12 mm 电解)撞上载板底面的元件。
- 35 个定位孔做 NPTH,立柱必须用尼龙 —— 金属化 + 金属立柱 = 五台手机的地
在载板上并成一根,4 A 回流会绕开专用
P−针,违反 §6.4。
载板这一版还欠的(详见 §25.5)
✅ J23 已定稿(rot=270、x=5.134,壳体前端面与板边齐平)· ⬜ Hub 那 26 个件是网格粗排、布线前要手调 · ⬜ 设计规则 / 铺铜 / 布线一根没做 · ⬜ 主板 7 个定位孔的孔径没量
上一轮写的「下一步四件」(第 1/2/5 件已完成,剩下的搬到文首)
| # | 做什么 | 备注 |
|---|---|---|
| 1 | ✅ 排母选型 + 换件实施全部完成(2026-09-05 夜,见文首) | |
| 2 | ✅ 自建 BM24-50DS 封装 + 镜像位实测(§24.3)—— 全部完成,结论是当前的 ODD_ROW_TOP = True 就对 |
|
| 3 | 下板 PCB 导入变更 | 删了 15 个连接器 + 53 个 7xx 器件,新增 J24~J30 / U33,接口区要重排,最后统一重铺铜。⚠️ 下板的铜面 2026-09-05 被误重铺过一次(无损失,本来就要重做) |
| 4 | 🟡 载板 PCB —— 摆位 + 规则 + 差分对 + Inner2 地平面 + 6 个 M3 + GND 扇出 + 换件重摆/重扇/重铺 + Inner1 分割电源面都已完成。⬜ 只剩 布线 | |
| 5 | ✅ 载板上半部分的安装点 —— 已加两个中跨 M3 (76.6, 80.1) / (148.1, 80.1),§25.7 ③ |
改完照例:verify_sch.py(当前页)+ 跨页对账 + port_audit.py
(载板加 --board Board2),三个都干净了再让用户看截图。
动 PCB 的话再加一条 pcb2_clear.py(焊盘级净间距,自由焊盘只有它管)。
载板布线的下一步,按顺序
- ✅ 换设计规则 ——
.claude/pcb2_rules.py --push,--check十一项全过(§25.7 ①)。 - ✅ 建差分对 —— 11 对(5 长 + 5 短
*J+ UPSTREAM),短段也建了对。 - ✅ Inner2 整板 GND —— 一块
GND-INNER2,81 块填充,已存盘(.claude/pcb2_pour.py --push)。 - ✅ GND 扇出 ——
.claude/pcb2_fanout.py,换件后已 clean → dump → push 重来一遍, 210 个过孔。J31/J32 那 33 个 GND 脚不再需要扇出(通孔件自己就是过孔,one_net()的todo带not p['hole'])。 ⬜ 只剩五颗 BM24 的 51 个 GND 脚(过孔塞不进 0.35 间距,只能逃逸布线,§25.8)。 - ✅ Inner1 分割电源面 —— 2026-09-06 铺完(五块
VOn_P岛 + GND 底填, §25.11 /pcb2_pour.py的ISLANDS)。三个校核脚本全过:inner1_check.py(下发前预测)/inner1_verify.py(下发后实测,最窄颈 7.20 mm)/inner1_diffref.py(§10.1 参考面)。 ⇒+5V/+3V3_HUB/VOn_P的扇出现在可以做了(之前扇过去全是悬空过孔)。 - 🟡 布线 —— 先 D±(长的先走),再电源,最后杂散信号。 ⚠️ BM24 的逃逸布线单独排一项:前排往南、后排往北,同排横向穿不过去(净距 0.17 mm)。
Inner1 分割电源面的规划(⚠️ 这是 09-05 的规划稿;落地结果与实测见设计文档 §25.11)
层角色:Inner1 = 电源面(分割),Inner2 = 完整地平面(§25.6 ①,和下板是镜像的)。
只做三类岛,其余全部 GND 底填(不是原来写的「+5V / +3V3_HUB / VO15_P / VBUS15」
十二块)——理由是内层是 0.5 oz,按 IPC-2221 内层算 4 A 要 6.97 mm 净宽、
2.8 A 要 4.3 mm,而两排手机之间的走廊(y 49.66~85.5)只有 35.8 mm 高:
| 岛 | 网络 | 为什么要 | 最小颈宽 |
|---|---|---|---|
| 5 块 | VO1_P~VO5_P |
每路 4 A,这是上四层的头号理由 | ≥7.0 mm |
| 底填 | GND |
剩余全部,优先级数字给最大 | — |
⚠️ 2026-09-05 夜简化:
+5V和+3V3_HUB两块岛取消了(§25.10)。 排母逐针分配按几何重排之后,+5V从 3 针变成 10 针、分散在两颗排母的南北四处, 每个入口只喂本站的U2n+C70n、段电流 ≤0.6 A ⇒ 顶层 0.5~0.8 mm 走线就够, 内层不用给它留地方。+3V3_HUB(420 mA、局部)同理。 ⇒ Inner1 只剩「五块VOn_P+ GND 底填」,五路 4 A 各走各的,互不挤。
VBUS1~5 不做岛:每口 0.5 A、走线只在本服务带内约 18 mm,PWR1A(0.6 mm) 走线足够。
额外好处:顶层那 5 段 *J 差分(U5n→J7n,5 mm)下方的 Inner1 就是完整的 GND 底填,
参考面比分割的电源面干净得多(§25.7 ① 那条警告因此自动消解)。
走廊里已知的障碍(Inner1 只怕直插焊盘,不怕表贴件):
C801/C811/C821 三个 Ø12.5 电解在 y 68.2580.75,66.91,
两个新中跨 M3 的 Ø6 焊盘在 (76.6, 80.1) / (148.1, 80.1),
外加 J31/J32 的 80 个通孔焊盘(坐标已定稿:J31 x 37.869C831/C841 在 y 54.4186.129 / J32 x 133.869182.129,
两行 y = 66.23 / 68.77,见 pcb2-header-pads.json)。
⚠️ 2.54 间距的通孔在内层会把铜面打成筛子(Ø1.7 焊盘 + 0.2 净距 ⇒ 相邻反焊盘之间只剩 0.44 mm 网),
电源岛的颈宽要绕开这两条带,别指望从排母底下横穿过去。
还欠用户的五件事
BM24 的镜像位实测→ ✅ 2026-09-05 已完成,结论「不用改」(§24.3)。 可选的零成本复核(还没做):奇数排上从 G1 端数,位置 17-18-19 与 22-23-24 各自三连导通 (SPKR_OUT_N33/35/37 和SPKR_OUT_P43/45/47),偶数排没有这种三连。 0b.量排线两个公头之间的距离→ ✅ 用户拍板不管,装配时折叠(余量约 80 mm,会占手机上方高度)。- h_up:主板正面(朝上那面)最高元件高出板面多少。只影响压板高度与整机层高。
- ✅ 排针/排母选型已定(§22.3f):
C55020601+C5307344,对插 13.0 mm。顶针已定稿。 ⬜ 到货要核两件:母座触头镀层(商城只写了「黄铜」,没写镀层)、 对插后实测板间距是否真的 13.0 mm(确认是塑料贴合、不是针先顶死)。 2b. 首件上电前量一次:VBUSn对 GND 的电阻(确认电源触点 G1/G2 没接反,§13.5)。 负载开关默认关断,所以这一步零风险、零成本。 - 顶针首件必验:±1 mm 公差链对 1.2~2.2 mm 的侧键触点是危险的,而且 选定的顶针冠径只有 Ø0.9 球头、又是贴片不可调,所以首件实测是唯一对策(§22.3c)。
- 用卡尺重量那 7 个定位孔:±1 mm 里有 ±0.5 来自「孔位是从照片提的」, 量一遍能直接砍掉一半误差 —— 这是眼下性价比最高的一项。
这一轮做了什么(2026-09-04 傍晚,载板画完)
载板 8 页
C1-CONN(排母 J31/J32 + U31 5V→3.3V + U34 LM75BD 温度芯片 0x48)、
C2-HUB、C3-USB、C4-SITE1~5(每台手机一页:P+/P− 焊盘 + §6.3 电容组 +
4 根侧键顶针 + EDL 座/模拟开关/下拉/去耦)。逐页内容与位号见 §22.7。
手机位一台一页,和下板「一路一页」保持同一结构;五页由 .claude/c4_site.py
同一段代码生成,改一处五页一起变。
这一轮自己发现并补上的两个洞
UP_VBUS_DET在载板上没有源头。 §22.2 给它留了一根排针,但 J23 的 VBUS 是 5V, 排针那头是 3.3V 域的 IO44。在 C2-HUB 上补了 R721 10k / R722 20k 分压(3.33V)。- 载板上原来没有 3.3V。 排针只送 +5V,而 Hub、温度芯片、五颗 EDL 开关都要 3.3V。 U31 从下板搬过来后画在 C1-CONN(+5V 进来的那一页);§9.5 要求的「就近 Hub 供电」 是 PCB 摆位约束,与分页无关,已在文档里说清楚。
两个明知不对、故意先放着的占位型号(⚠️ 顶针那条已于当晚解决,见文首)
| 占位 | 差在哪 |
|---|---|
C9900001338 |
自由高 9.0 mm,§22.3e 要的是工作高 2.0 / 自由高 3.0 |
排母 C9900025309 |
高度没按 13 mm 净空选;而且编号体系和公头不同(§22.2 有对照表) |
放占位是为了让网表先连通、能跑完整验证;两个都在 §22.7 里单列了一节,打样前必换。
⚠ 一个必须记住的编号陷阱
下板公头(DIP 式)第 r 行 = 引脚 r / 41−r;载板母座(IDC 奇偶式)第 r 行 = 引脚 2r−1 / 2r。
只能按行对,不能按引脚号对(VO1_P 在下板是 1/2/3 脚,在载板是 1/3/5 脚)。
原理图这边不会错 —— c1_conn.py 直接 import p5_btb.py 的行表,两边同一个事实来源。
风险在 PCB:焊盘编号 + 摆放镜像决定第 r 行会不会真对上第 r 行,布板时要实打实核一次。
新踩的两条 EDA 坑(已写进 skill §6c / §6d)
- 同一颗电解、同样
create(270),在两个图页里 pin1 方向相反。 方向表一张都别信, 极性元件放完必须断言(c4_site.py里断言「电解 pin1 的 y 必须大于 pin2」)。 - 红黑两个测试点的引脚偏移不一样(−20 / −10),按名义值算线会画出斜线,
而
create()对斜线直接抛create failed!并让整批下发中途断掉。 现在下发前统一断言「横平竖直且非零长度」。
状态一览
| 阶段 | 状态 |
|---|---|
| 方案设计 | ✅ 定稿 v1.6(五路直供电源板设计.md) |
| 小米8 侧接口引脚定义 | ✅ 已从原理图逐脚追踪(设计文档 §13) |
| 器件选型 | ✅ 全部立创有货,UUID 缓存在 .claude/eda-parts.json |
| P1-INPUT | ✅ 补完输入侧 INA226(U13+Rs6+C11,§19),六项验证全过,需重新看一眼 |
| P2-CHANNEL(CH1) | ✅ 已删 J2/J7,侧键漏极改挂 POGO1_* 端口,六项全过 |
| P2B-CH2 ~ P2E-CH5 | ✅ 同样删了 J3POGO2~5_*,各自六项全过 |
| P3-CONTROL | ✅ 画完,六项验证全过,等用户看截图 |
| P3B-EXPAND | ✅ 画完,六项验证全过,等用户看截图 |
| ➡️ 已整页搬到 Board2 载板,改名 C3-USB;下板已删 | |
| ➡️ 已整页搬到 Board2 载板,改名 C2-HUB;下板已删。⬜ 还要在载板上补 U31 | |
| P1B-5V | ✅ 已摘掉 U31 + C736/C737(跟 Hub 一起上载板),现在是纯 12V→5V,9 元件,六项全过 |
| P5-BTB(原 P5-HARNESS) | ✅ 整页重画:J24/J25 两条 2×20,47 信号 + 33 地,六项全过(截图 P5-BTB.png) |
| P6-FAN-EDL | ✅ 新建:U33 第三片 595(EDL_EN1P6-FAN-EDL.png) |
| 跨页网络对账 | ✅ 下板 11 页 0 漏接 / 0 待补;✅ 载板 62 个网络 0 漏接 |
port_audit.py |
✅ 下板 513 个、载板 260 个,可疑 0 个(载板加 --board Board2)。⚠️ 2026-09-04 晚重跑时载板曾报 1 个(C1-CONN 的 GND 旗),已修 —— 每轮都要重跑,别信上一轮的数 |
| Board2 载板原理图 | ✅ 8 页全部画完:C1-CONN / C2-HUB / C3-USB / C4-SITE1~5,六项验证 + 跨页对账 + port_audit 全过 |
| Board2 载板 PCB | 🟡 4 层 / 220×135 板框 / 35 个定位孔 / 6 个 M3(含 2 个中跨)/ 161 个元件已摆完(§25、§25.6、§25.7);⬜ 未布线 |
| Board2 载板 GND 扇出 | 🟡 210 个过孔已落盘(§25.8),脚本 pcb2_fanout.py;⬜ 差 J31/J32 的 33 个(等换件)+ BM24 的 51 个(要逃逸布线) |
| Board2 载板 设计规则 | ✅ 整套已下发,pcb2_rules.py --check 十三项全过(四层能力集 / PWR4A·PWR5V·PWR1A / 11 对差分 / USBPAIR 0.15 / 12 个网络指派 / PLANE-DIRECT 铺铜全连接 + 5 个网络指派) |
| Board2 载板 铺铜 | ✅ 两层内层都铺完:Inner2 整板 GND + Inner1 五块 VOn_P 岛 + GND 底填(§25.11)。脚本 pcb2_pour.py(ISLANDS 是事实来源,--verify 回读)+ 三个校核脚本 inner1_check / inner1_verify / inner1_diffref |
| BM24-50DS 封装 | ✅ 自建完成(§24),个人库器件 771dbf00…,几何来自 Hirose 官方样本。⬜ 镜像位待实测(§24.3) |
| PCB 转板 | ⚠️ 已过期:原理图这一轮删了 15 个连接器 + 53 个 7xx 器件、新增 J24~J30/U33,还没导入变更 |
| PCB 板框 | ✅ 160 × 125 mm,四角 R5 圆角 + 4 个 M3 金属化定位孔(接 GND) |
| PCB 布局 | 🟡 第五遍:8 A 输入链整条重排(见下),等用户看 PCB-layout-v5.png |
| 设计规则 | ✅ 整套已下发,--check 七项全 OK(能力集 / PWR4A / PWR12V / 差分对 / 0.2 mm 间距 / 7 对 / 12 个指派)。上一轮说的「只能手动」是错的,见下 |
| PCB 板框 | ⚠️ 改成一条闭合 polyline 了(原来的 4 直线 + 4 圆弧自动布线器不认,见下) |
| 设计规则 | ✅ 等长容差 5 mm;新增 USBPAIR 0.15 mm,只挂在每一对自己的两个网络之间 |
| 铺铜 | ✅ 19 块(新增 +5V / +3V3_HUB / +3V3×2),三条低压轨的 91 个焊盘已扇出 |
| 7 对 USB 差分 | ✅ 6 / 7 布通(USB1~5 + UPSTREAM)。⬜ 只剩 ESP32 调试口 USB_DP/DM |
| PCB 布线 | 🟡 1208 过孔 / 5814 导线已落盘(save 后重开验证过)。⚠️ 铺铜是旧填充、接口区马上要大改 —— 先别继续布 |
| Kelvin / 灌铜网络的漏网焊盘 | ⬜ 要手工布:INA226 的 VIN±、五路泄放电阻 R*19、输入侧 U13 —— 5 mΩ 分流器满量程才 20 mV,多 1 mΩ 走线就是 20% 误差 |
| GND 漏网焊盘 | ⬜ 20 个(459 个扇出过孔之外的) |
| 丝印整理 | ⬜ 位号大面积压字,打样前要单独跑一遍 |
| 原理图 SCL 断线 | ✅ U13 的 SCL 压在导线中段、根本没接上,已修(设计文档 §19.4)。新增全工程扫描 .claude/port_audit.py |
| 板载 USB Hub 改造 | ✅ 原理图 + PCB 布局都做完了 |
| 两板架构(2026-09-04 拍板) | ✅ 设计文档 §22 定稿;✅ 下板原理图改造已做完(§22.7);⬜ 载板 C1-CONN 未画 |
| 上板(载板) | ✅ 原理图 8 页画完;⬜ PCB 还是空板;✅ 顶针已定稿 YTC1P-2510-01;⬜ 只剩排母占位(§22.7) |
| 散热 | ✅ 定稿 + 电路已画(P6-FAN-EDL:Q6/D2/J26~J30 + TACH 分压)。⬜ 风扇型号未定(若选 4 线风扇,§22.6 那段 MOS+分压可整段删) |
| 小米8 主板机械数据 | ✅ 全部到手:44.66×67.09 实测、7 个定位孔 ±0.5、触点 ±0.3(§22.3)。数据在 .claude/mi8_mech.json / mi8_contacts.json |
| EDL(9008) 控制口 | ✅ 两侧都画完:下板 U33(第三片 595)→ EDL_EN1~5 → 排针 → 载板 U41 |
下板原理图 11 页(顺序已排): P1-INPUT / P1B-5V / P2-CHANNEL / P2B-CH2 / P2C-CH3 / P2D-CH4 / P2E-CH5 / P3-CONTROL / P3B-EXPAND / P5-BTB / P6-FAN-EDL。
载板原理图 8 页(顺序已排): C1-CONN / C2-HUB / C3-USB / C4-SITE1~5。
截图:12 页里已截 10 页(缺 P2B/P2C/P2D,它们是 P2 的机械复制)——
P1-INPUT.png、P1B-5V.png、P2-CHANNEL.png、P2E-CH5.png、P3-CONTROL.png、
P3B-EXPAND.png、P4-USB.png、P4B-HUB.png、P5-HARNESS.png,PCB 最新是 PCB-layout-v4.png。
(上一轮那三张空白 PNG 是渲染没出来,不是图元缺失;这一轮重截,都是好的。)
这一轮做了什么(2026-09-04,方案转向)
一、自动布线(转向前完成的部分)
见下面「自动布线终于跑起来了」那节。结果保留着:1208 过孔 / 5814 导线, 6/7 对 USB 差分布通,间距/物理错误 0。但接口区马上要大改,别在这上面继续投入。
三条最贵的教训(每条都卡了很久):
- 板框必须是一条闭合 polyline,不能是 4 直线 + 4 圆弧。
自动布线器取板框走
fu(),只扫 polygon 管理器;独立线/弧不在那个表里 →getFilterBoard()返回空 → 弹「请先绘制板框!」然后静默退出。 正确写法:pcb_MathPolygon.createPolygon(SRC)→pcb_PrimitivePolyline.create('', 11, poly, 5)。 ⚠️ 直接把源数组喂给create()会抛「无法创建多边形图元」而且真的没建 —— 跟铺铜那条「抛了也算成功」方向相反。 - 布线跑的时候什么都别碰。不要
save()、不要pcb_Drc.check()、 连getAll()这种只读调用都别做。被打断时routerResult()里state!==1会直接丢弃结果 —— 画布上看得见、getAll()数得到, 但没进文档模型。判据只有一个:save 后重新openDocument再数一次。 - 差分对要一次统筹全部 7 对。一对一对布会互相堵死(
保留模式下先布的成了障碍)。 正确姿势:全选 7 对的焊盘 + 「已有导线/过孔 = 移除」+「布线网络 = 所选网络」。
其它:重载客户端会清空自动布线对话框的设置;DRC 面板刷不出来时走
设计 → 清除错误 → 设计 → 检查DRC。
二、方案转向:两块板(用户 2026-09-04 拍板)
用户的原始需求分四次说清楚:
- 「做成两块板,五路 usb 移到上板,排针对接,上板加五块主板定位」
- 「进入 9008 的控制接口也接入」
- 「与主板连接的 usbhub 移到上板,不用排针,排针只需要把下板的电源部分接上来」
- 「侧键做顶针,EDL 做 2 针 1.25mm 座子我自己接线,VBUS 通断走排针」
- 「不要散热垫,加风扇」→「侧面两侧各 2 个小风扇吸入,顶部 1 个大风扇排出」→「风扇装 3D 打印外壳上」
完整规格写在设计文档 §22,这里不重复。
三、⭐ 小米8 主板机械数据:一直就在我们自己的 PDF 里
旧结论「PDF 是电气原理图、不含机械尺寸、只能实测」是错的。
小米8原理图/Xiaomi_Mi_8_Schematic.pdf 共 46 页,
前 2 页是正反面位号图(含完整板框轮廓 + 全部位号),后 44 页才是原理图。
当初只按「原理图」去读,get_text() 一看全是位号就跳过了,从没渲染出来看一眼。
拿到的数据(方法与精度分档见 §22.3a / §22.3b):
- 比例尺 17.0 pt/mm(从 464 个 0201 封装反推,卡尺验证 0.7%)
- 主板 44.66 × 67.09 mm(卡尺实测)
- 7 个定位孔(从用户标红框的照片提取,±0.5 mm)
- EDL / 尾插 / 侧键触点(从器件本体图元提取,±0.3 mm,并被两个卡尺读数交叉验证)
四、⚠️ 这一轮我判错了四次,都是同一个毛病
没有实物对照就从矢量图元硬推。 记下来,别再犯:
| 判错 | 错因 |
|---|---|
| 「主板没有定位孔」 | 轮廓用「逐行取最外侧墨点」提取,这方法看不见板内的孔和挖空 |
| 「定位孔就是图纸上那 7 个圆」 | 抓到的是测试点和圆焊盘。摆成横向和用户照片一对就露馅 |
| 把两个卡尺读数当成「板厚」 | 用户说的是侧键到板边的距离 |
| 把 7.61 读成 1.76 | 卡尺屏是旋转的,数字看反了 |
规矩:卡尺读数先跟用户确认再落档;从图纸推的几何结论, 必须有一个实物基准交叉验证才能写进文档。
五、EDL(9008) 电路(原理图 Sheet 17,比维修站的付费短接图可靠)
VREG_S4A_1P8 ──[R1204 1.8kΩ 0201]── TP1204 ← 0.75 mm 测试点
FORCED_USB_BOOT ──┬── TP1205 ← 0.75 mm 测试点
└── CR1209 PESD3V3V1BCSF ── GND
短接两点 = 把 FORCED_USB_BOOT 拉到 1.8V → 进 9008。
串了 1.8 kΩ,短路电流只有 1 mA,一颗模拟开关就够。详见 §22.4。
这一轮:自动布线终于跑起来了(2026-09-04)
⚠️⚠️ 头号发现:板框必须是一条闭合多段线,不能是 4 直线 + 4 圆弧
布线 → 自动布线... → 运行 静默失败:对话框正常关闭、一根导线不画、
console 没有任何异常、也没有网络请求。三次尝试(React onClick / 原生
dispatchEvent / 等 90 秒)现象完全一样。
顺着客户端 bundle 追下去才找到:
pcb_ManufactureData.getAutoRouteJsonFile()
→ ui.js getAutoRouteJson() → windowBridge.rpcCall('getNewAutoRouteJson', …)
→ pcb.js vP.autoRouting() → createPangolinJson()
→ getFilterBoard() → fu() → [] ← 就是这里
→ 弹 "Please draw a board outline first!" 然后 return undefined
fu() 的判据是(pcb.js 反编译):
[Ni.getInstance().getAllItem(), tn…, Yi…] // 只有 polygon 管理器
.forEach(t => t.forEach(e => {
!e.isEmpty && (e.type == "Board Outline" || e.type == "Line" && e.layerId == 11) && o.push(e)
}))
它只扫多段线(polygon)管理器。我们的板框是
pcb_PrimitiveLine.create() × 4 + pcb_PrimitiveArc.create() × 4,
八个独立图元,压根不在那个表里 —— 于是 fu() 恒返回空。
这个错极难往板框上想,因为除了自动布线,板框在别处全都是好的:
渲染正常、铺铜按它的几何铺好了、DRC/ERC 全过、Gerber 也认。
同一个 fu() 还卡着 getDsnFile() 和 getAutoRouteJsonFile()
(两个都返回 undefined,等于 freerouting 那条路也被堵死了)。
正确姿势(两步,缺一不可):
const poly = await eda.pcb_MathPolygon.createPolygon(SRC); // ⚠️ 必须先过这一步
await eda.pcb_PrimitivePolyline.create('', 11, poly, 5);
⚠️ 把 TPCB_PolygonSourceArray 直接喂给 pcb_PrimitivePolyline.create()
会抛「错误:无法创建多边形图元」,而且是真的没建(getAll() 仍是 0)——
注意这跟铺铜那条「create() 抛了也算成功」的经验方向正好相反,别混用。
pcb_place.py 的 push_outline() 已经改成这个写法(新增 outline_poly())。
一路排查中间踩到的三件事
pcb_PrimitiveArc.getAll()/getAllPrimitiveId()在本工程恒返回 0。 排查时我以为四个 R5 圆角不存在,又补建了四段 —— 于是 layer 11 上现在有 8 段重叠圆弧 + 4 条重叠直线(几何完全重合,Gerber 上是同一条路径描两遍)。 它们不在fu()的表里,不影响布线;但删不掉(枚举不到就没有 primitiveId), 要清只能在客户端里手删,或者隐藏其它层后框选 layer 11 全删再重建 polyline。- 桥接的 WS 请求 30 秒硬超时,
eda.py的timeout=只管 HTTP 那一段。 JS 里sleep超过 30 s 一律拿到Request … timed out after 30000ms。 要等就分多次短调用轮询。 - 客户端重连会换
windowId。curl http://127.0.0.1:49620/eda-windows可以确认 当前只有一个窗口 —— 排查「是不是打到另一个窗口去了」时先看这个,别猜。
抓静默失败的通用手法(这轮就是靠它破的)
页面里挂三层钩子,再触发一次,就能看见吞掉的错误:
// ① console 全接管 ② window.onerror / unhandledrejection ③ fetch / XHR / Worker
// ④ 最关键:postMessage —— 父窗口与编辑器 iframe 之间的 rpc 都走这里
window.addEventListener('message', e => { …记录 topic 与 message… }, true);
document.querySelector('iframe.editframe').contentWindow
.addEventListener('message', …, true);
回包里 {"topic":"getNewAutoRouteJson_MSG_BUS_RPC_64600","type":"push"}
没有 message 字段 —— 一眼就能断定是 iframe 里的处理函数返回了 undefined,
而不是参数没传对或者按钮没点上。
再配合 console.warn 里打 new Error().stack,能直接定位到
ui.js:1819 的 getAutoRouteJson,然后把 pro-ui/…/js/ui.js(17 MB)和
pro-pcb/…/js/pcb.js(10 MB)下下来 grep —— 这两个包是公开 CDN,随便下。
自动布线的设置(对话框里的状态是持久化的,改一次就记住)
| 项 | 取值 | 为什么 |
|---|---|---|
| 布线网络 | 所有网络 | |
| 布线拐角 | 45° | |
| 已有导线/过孔 | 保留 | 708 个扇出过孔和平面层不能被冲掉 |
| 效果优先级 | 完成度优先 | |
| 布线图层 | 只勾顶层 + 底层 | 内层1 是完整地平面、内层2 是电源平面,不许被信号线割开 |
| 忽略网络 | 15 个 | GND(459 个扇出过孔已经接到地平面了,让它布会刷出一堆顶层地线)+ 7 对差分的 14 个网络(USB1~5_DP/DM、UP_DP/DM、USB_DP/DM,打算之后按对单独布) |
对话框是 React 的,忽略网络 那个下拉可以直接从 fiber 上取 onChange 逐个灌:
const p = /* 从 [data-id^="selectOptionKey_"] 往上找带 {list, onChange} 的 fiber */;
p.onChange('GND'); // 每加一个 list 就少一个,要重新取 fiber
⚠️ 自动布线的结果不到最后不落盘 —— 中途被打断就白跑
第二趟(补差分对)跑完,getAll() 报 6196 条导线、几何上逐段查过
USB1_DP 从 U32.2 一路连到 J17.3、并查集只有一个连通块,
但 DRC 咬定这两个焊盘断开。pcb_Document.save() 之后
重载客户端,板子回到了第一趟的 1095 过孔 / 3455 导线 —— 第二趟一根线都没进文档。
原因在 routerResult():
routerResult(t){ if(this.state!==1){ console.error("Auto Routing is stop!", this.state); return; } … }
布线结果是一批一批推回来的,state!==1(被打断)时直接丢掉。
画布上看得见、getAll() 数得到,但那只是 historyGroup 里的预览,没有进文档模型 ——
所以 DRC 是对的,错的是我。
判据只有一个:save 之后重新 openDocument,再数一次导线/过孔。
数字对不上就是没落盘。别信 getAll(),也别信画布。
跑的时候不要碰客户端:不要 save()、不要 pcb_Drc.check()、
连 pcb_PrimitiveVia.getAll() 这种只读调用都先别做 —— 第二趟就是这么被打断的
(第一趟跑满 ~50 分钟、进度条自己消失,结果就落盘了)。
盯进度只读 DOM 文本:document.body.innerText 里那行 自动布线中...(NN%)。
顺带两条
- 重载客户端会把自动布线对话框的设置清空(忽略网络清零、四个布线图层全勾回来)。 重载之后必须重新配一遍再跑。
- DRC 面板刷不出来时走菜单:
设计 → 清除错误(全部 (0))→设计 → 检查DRC。 面板上那个「刷新」按钮不是 DRC 的,pcb_Drc.check()单独调有时也不刷新。
局部返工:把 7 对 USB 差分抢回通道(2026-09-04,用户拍板)
第一趟自动布线之后,7 对差分只布上 2 对(USB1/USB2),其余 5 对 连「所选网络」单独重跑都一根线不画 —— 板子已经被普通信号线占满了。 用户拍板走「局部返工」而不是整体回滚。四步:
① 先改规则(pcb_rules.py --push),否则布完还是一堆假违规:
| 改动 | 从 | 到 | 为什么 |
|---|---|---|---|
differentailPairLenTolerMax |
0.5 mm | 5 mm | USB 2.0 HS 对内偏斜预算 ≈100 ps ≈ 15 mm;实测偏差 1.6~1.9 mm ≈ 10 ps。为凑 0.5 去绕蛇形线是拿串扰换数字 |
新增安全间距条目 USBPAIR |
— | 0.15 mm | 只通过 overwriteNetByNetRules 挂在每一对自己的两个网络之间;对其它网络仍是 0.2 mm |
USBPAIR 那条不能用全表抬下限的循环扫到(会被抬回 0.2),代码里单独跳过了。
net-by-net 的条目形状文档没写,是从 pcb.js 的 getBetweenNetDataItem() 反出来的:
{net1:{type:'net',name:'USB1_DP'}, net2:{type:'net',name:'USB1_DM'},
rule:'USBPAIR', copperRuleName:null, planeRuleName:null}
⚠️ overwriteNetByNetRules 是整表覆盖,要先 getNetByNetRules() 读回来再合并。
改完 DRC 172 → 166,4 条对内间距 + 2 条等长违规一起消失。
② 三条低压轨改成铺铜(铜面 15 → 19 块):
| 铜面 | 网络 | 层 | 范围 | 盖住 |
|---|---|---|---|---|
| 5V-L3 | +5V |
L3 | x 11 |
10 / 16 |
| 3V3HUB-L3 | +3V3_HUB |
L3 | x 59 |
21 / 21 |
| 3V3-CTRL-L3 | +3V3 |
L3 | x 95.5 |
合计 45 / 61 |
| 3V3-CH-L3 | +3V3 |
L3 | x 5 |
同上 |
都塞在 12V 那三条带子之间的空地里,互相与 12V 都留 ≥0.5 mm。
3V3-CTRL-L3 的右边界 150.5 是被 ESP32 天线禁铺区(x≥151)逼的。
这带出两个工具改动:
pcb_pour.audit()要按网络做并集。+3V3是两块铜面,逐块判「焊盘在不在里面」 会让每一块都把另一块盖住的焊盘报成「意外漏掉」。- 新增
POUR_PARTIAL = ('+5V', '+3V3'):这两条轨的铜面故意只盖一部分, 漏在外面的焊盘靠走线接,不算错。
③ pcb_fanout.py 只对「落在自己铜面里」的焊盘扇出(新增 _pour_filter())。
在没有铜的地方打一个下到 L3 的过孔 = 一个悬空过孔,DRC 反而多一条断连。
三条轨一共扇出 91 个过孔,5 个焊盘周围太挤放不下,留给走线。
④ 局部返工的选取判据(_snap_before.json 是基准):
删掉「布线器加的」且满足下面任一条的**整个网络**的线与过孔:
a) 网络已经改成铺铜(+5V / +3V3 / +3V3_HUB)
b) 该网络在 y < 28 mm 的下带里有走线,且不是 GND/12V/VOn 这类扇出网络、也不是差分对
结果:34 个网络 / 678 条导线 / 68 个过孔,只占布线器产物的 18%。 整网删、不留断头,剩下的 80% 一根没动。
⚠️ 差分对必须一次统筹全部 7 对,一对一对布会互相堵死
下带清空之后,选中 5 对跑「所选网络」,只布上 USB4/USB5;再跑一次,
一根线都不加。原因是 保留 模式下先布好的那两对成了后面的障碍 ——
USB3 与上游 UP 要横穿它们,而下带就那么宽。
正确姿势是把 7 对全选中 + 「已有导线/过孔 = 移除」,让布线器把已布的两对
一起拆掉重排。clearRoute(false, undefined, this.selectNets) 只清选中的网络,
不会波及别处(pcb.js 里确认过)。一趟下来 6 / 7 对布通(USB1~5 + UPSTREAM),
只剩 ESP32 那条调试口 USB_DP/DM。
顺带澄清一条之前的误判:「所选网络」模式是好的。早先它「跑完却一根线不画」
不是模式坏了,是那时候板子上真的没路了 —— 日志里 routingRange:1、选中 24 个焊盘
都对,弹的也是「自动布线已完成」而不是报错。
布线跑完之后要做的(按顺序)
- DRC ——
python .claude/drc_read.py。看「间距错误」和「物理错误」两支, 连接性错误剩多少 = 还有多少没布上。 - 七对差分单独布(
USB1~5_DP/DM、UP_DP/DM、USB_DP/DM)。 规则里已经建好 7 对、W 0.28 / S 0.18 / 等长容差 0.5 mm。 ⚠️ 顺序上其实应该先布差分再自动布线——这一轮是为了先验证布线器能不能跑才反过来的。 如果跑完发现差分对已经无路可走,就按快照回滚,改成「先差分后自动」重跑。 - 重新铺铜。底层(layer 2)上有 VO1~5_P 五块铜面,自动布线在底层加了信号线,
铜面要重建才会让开它们:
工具 → 铺铜管理器... → 重建所有(pcb_pour.py --rebuild里已经把这一串打好了;rebuildCopperRegion()API 是坏的)。 - 丝印整理 —— 位号大面积压字,打样前要单独跑一遍。
- Gerber 预览复核板框闭合 + 四个 M3 定位孔。
跑之前先存了个快照(.claude/_snap_before.json)
布线前把 708 个过孔 + 575 条导线的 primitiveId 全存了下来。
万一自动布线的结果不能要,删掉不在这张表里的线和过孔就能精确回滚,
比 清除布线(会连扇出一起清)干净得多。
上一轮:开始布线(2026-09-03)
顺序是「先把面铺好,再拉线」—— 平面定了,信号线的参考面和过孔位置才有依据。
1. 先抓到一个真的断线:U13 的 SCL 没接上 ⚠️
追 12V 母线的网络时发现 PCB 里 U13.2 / U13.5 挂在一个只有两个焊盘的
$1N257 上,而 SCL 网络 11 个焊盘里没有 U13。输入侧检流那片 INA226 是死的。
根因不是漏画线,是 SCL 端口的引脚压在一条导线的中段而不是端点
(端口在 (920,340),导线是 [920,320 → 920,570])。EasyEDA 只在端点认连接。
同一页的 SDA 端口恰好落在端点上,所以 SDA 是好的 —— 两个端口在图上一模一样。
DRC / ERC / verify_sch --nets / 跨页对账四道全过,因为
verify_sch 的判据是「引脚落在某条导线段上」,中段搭接满足这一条。
新增 .claude/port_audit.py:全工程 572 个旗/端口逐个判「是不是某条导线段的端点」。
修之前 1 个不合格(就是它),修完 0 个。写进 easyeda-sch-geometry §8a-2 与设计文档 §19.4。
写这个检查时踩了个自伤的坑:第一版按折线解析导线,572 个里报出 197 个可疑, 其中一大片是工作正常的 GND 旗。
getState_Line()返回的是端点对(skill §5), 按折线解析会凭空造出斜线段。误报到这个程度的检查等于没有检查。
2. 8 A 输入链整条重排
原来这一串是仿射映射甩出来的,J1(x5.8) → F1(x46.6) → Q1(x53) → 体电容(x66/75) → Rs6(x84.6~90.7):8 A 要从 x5.8 走到 x90.7,85 mm,其中 $1N223(J1→F1)
单独一段就 42 mm。8 A 在 0.5 oz 内层要 ΔT=20 °C 得 18 mm 宽铜面
(IPC-2221 内层按外层 50% 取,面积翻倍),band B 一共才 36 mm 高,塞不下四条。
而且这一串在 y 上来回折(F1.2 在 y60.08,Q1 源极在 y62.85,中间还夹着
$1N236 在 y60.56 的一个焊盘),L3 上按 y 切开分块,切线最窄处只剩 0.2 mm。
改成 J1 右手边一条走廊 x 11.546 / y 49.568.5(栅极网络放 J1 上方口袋):
| 之前 | 现在 | |
|---|---|---|
$1N223 J1→F1 |
42 mm | 6.9 mm |
| 8 A 段的横跨 | x 5.8 ~ 90.7 | x 12 ~ 40 |
| 分叉点 | x 90.7 | x ≈ 40(Rs6 下游) |
| 母线最大截面电流 | 8 A 全程 | 顶带 ≤ 6.4 A(往左喂 CH1、往右喂 CH2~5) |
顺带把 13.7 × 9.7 的立式泄放电阻 R9 也钉死了 —— 仿射区右缩之后它被推到 x95,
本体框伸到 x102 压进 P3 区。relax() 只夹中心坐标、不看外框,这类件必须手钉。
3. 平面层(.claude/pcb_pour.py)
| 铜面 | 层 | 网络 | 范围 |
|---|---|---|---|
| GND-L2 | L2 | GND | 整板(板框内缩 0.5,挖 ESP32 天线区) |
| VIN-RAW-L3 | L3 | $1N223 |
x 3 |
| VIN-FUSED-L3 | L3 | $1N227 |
x 14.6 |
| VIN-SW-L3 | L3 | $1N236 |
L 形,x 14.4 |
| 12V-L3 | L3 | +12V |
下带 x14.6 |
| VO1~5-L3 | L3 | VOn |
每路 30 mm 宽 / y 82.5~99.5 |
| VO1~5_P-L4 | L4 | VOn_P |
每路 30 mm 宽 / y 110~116.2,带一条下伸臂到分流电阻 |
分界线是焊盘边定的不是整数:+12V 顶边 81.9 卡在 C102 那排 1210 焊盘(y80.281.7);
VOn 底边 82.5 是因为 C107 pin1 是 rot90 的 1210,焊盘跨到 y83.0784.55 ——
按「元件中心在 y85.4」算会把它漏在两块铜面之间的缝里。
U8~U13 的 0.5 mm 间距 Kelvin 脚一律不进铜面(U13.10 是 VIN+、U13.9 是 VIN−,
相距 0.5 mm,任何分界线都塞不进去),走顶层细线,本来也不载流。
pcb_pour.py 里 audit() 会把「本网焊盘在铜面外」的逐条列出来,
EXPECT_OUT 是白名单,漏在名单外的就要查。
4. 铺铜 API 的三个坑(都实测过)
rebuildCopperRegion()是坏的,四种写法都抛Cannot read properties of undefined (reading 'length')。 唯一能填充的路子是 GUI:工具 → 铺铜管理器... → 重建所有 → 确认, 已用 React 的onClick打进pcb_pour.py。判据是nPoured不是nPour。- 多边形必须显式闭合(末尾把首点再写一遍)。文档说「首尾不重合会自动重合」
对铺铜边框不成立:不闭合就直接不建。
board_poly()一直能用是因为 它最后那段圆弧正好回到起点。 create()成功了也会抛错误:无法创建覆铜边框图元。返回值和 try/catch 都不能当判据,只能建完getAll()按名字核对 —— 而且名字会被转成大写。
排查时分不清 1 和 2 的办法:不闭合的矩形建完 getAll() 数量不变,闭合的 +1,
两者抛一模一样的异常。
5. GND 扇出:改布局之后必须按这个顺序重跑
python .claude/pcb_fanout.py --clean # 1. 先清空板上的过孔与走线
python .claude/dump_pads.py # 2. 再重新抓障碍表
python .claude/pcb_fanout.py --push # 3. 最后规划 + 下发
--clean --push 写成一条会用旧的 pcb-copper.json:清是清干净了,
规划器看的还是那 459 个已经不存在的过孔,把它们当障碍绕 ——
实测 459 → 292 个过孔、五路 Buck 的 pin2 全扇不出去、27 个散热盘只打进 4 个孔。
反过来先 dump 再 --clean --push 更糟,板上会剩两套(747 个)。
正确顺序的结果(15 个网络一趟跑完):708 个过孔 / 0 个失败, 其中 GND 459、+12V 128、输入链三条 48、五路 VOn 各 17、五路 VOn_P 各 8。
6b. 出线宽度必须按焊盘算,不能按网络算
第一版给功率网络一刀切 1.0 mm(= 线宽规则的最小值),结果 29 个焊盘扇不出去:
+12V 上挂着 R104 那种 0402 分压抽头,焊盘只有 0.57 × 0.54 mm,
1.0 mm 的线比焊盘还宽。退回 0.3 mm 硬扇出去,又被判
「线宽 11.8 mil,应在 39.4 ~ 393.7 mil」—— 34 条物理错误。
两处一起改才对:
PWR4A/PWR12V的最小值 1.0 → 0.3(默认值仍是 1.5 / 2.0)。 这就是 §10.1 那条「载流靠灌铜保证,不靠 DRC」再走一步。- 出线宽度按焊盘短边 × 0.9 取,夹到 [0.3, 1.0];过孔数按焊盘短边 ≥ 0.8 mm 给 3 个、 否则 1 个 —— 0402 抽头本来就不载功率电流,多打过孔只会把平面打成筛子。
6c. 扇出的障碍模型漏掉的两件事(DRC 抓出来的)
| 现象 | 根因 |
|---|---|
| 10 条「导线到过孔 3.4 mil,应 ≥ 7.9 mil」 | 板上已有的过孔只按钻孔半径 0.15 登记进 holes,没按焊盘半径 0.3 登记进 grid。差的正好是 0.15 mm |
| 1 条「导线到导线 6.2 mil」 | 出线之间的间距压根没查过 —— 原来只查了线对焊盘、线对钻孔 |
两条都补进 pcb_fanout.py 了(trk_grid + seg_seg_dist)。
补完重跑:间距错误 0、物理错误 0。
6. 顺手发现的两条 EasyEDA 路子
- 设计 → 从原理图导入变更(Alt+I) 能用 DOM 打开并读出变更表,
逐条列
U13:2 $1N257 → SCL。改完原理图必须走这一步,PCB 才会跟着变。 - 布线 → 扇出布线... / 自动布线... / 差分对布线 / 等长调节 都在菜单里。 信号线那 400 多条不必自己写布线器,下一轮直接驱动它。
最新一轮:设计规则不用手动了(2026-09-03 深夜,收尾)
上一轮结论「规则配置只能在 GUI 里手建」是错的。原因是参数形状给错了,
现在 python .claude/pcb_rules.py --push 一条命令把五项全下发,--check 七项全 OK:
[OK] 四层板能力集已就位(最小线宽 0.102 / 过孔 0.30-0.20,含 0.5oz 内层条目)
[OK] 线宽规则 PWR12V 存在 [OK] 线宽规则 PWR4A 存在
[OK] 差分对几何 W 0.280 / S 0.180 mm
[OK] 安全间距下限 0.2000 mm
[OK] 差分对 7 对: DEBUG UPSTREAM USB1 USB2 USB3 USB4 USB5
[OK] 线宽指派 12 个网络
当初为什么以为不行 —— 两个坑叠在一起
-
overwriteCurrentRuleConfiguration(x)要 config 本体,不要{name, config}包装。 传包装返回undefined且静默无效;传本体返回true。 (saveRuleConfiguration(cfg, name, allowOverwrite)确实是三个参数 —— 上一轮只传了一个 —— 但补齐了照样返回false。它是真的不能用, 好在我们并不需要存命名配置。) -
两种 config 的形状不一样,混用会被默默吃掉。
来源 线宽 安全间距表 getRuleConfiguration('<系统能力集>')平: form.strokeWidthDefaultentry.tablegetCurrentRuleConfiguration()嵌套: form.data['1'].defaultValueentry.tables['1'].content正确姿势:拿
getCurrentRuleConfiguration().config改,原形状送回去。 系统能力集只当数据源用(那张内层 0.5 oz 间距表是从它身上抄的)。
「切到多层板能力集」其实只是五个字段
把两个能力集逐值 diff 过:只差 3 个数 + 2 条默认不启用的条目,其余 300 多个数完全一致。
| 项 | Two Layers | Multiple Layers |
|---|---|---|
Track/copperThickness1oz 最小线宽 |
0.127 | 0.102 |
| 过孔外径最小 | 0.5 | 0.3 |
| 过孔内径最小 | 0.3 | 0.2 |
Track/copperThickness0.5oz |
无 | 有(min 0.089,isSetDefault=false) |
Safe Spacing/copperThicknessInner0.5oz |
无 | 有(isSetDefault=false) |
也就是说,能力集之间的差别全是放宽限制,两层板那套只是更保守。 真正必要的是最小线宽 0.102(U32 那个 0.5 mm 间距 QFN-64 的扇出要用)。
⚠️ 下发之后千万别做的一件事
别再去 GUI 的 设计 → 设计规则… 里动“配置”下拉框。
换一下就是整套重置:PWR4A / PWR12V 两条线宽规则会被冲掉,
12 个功率网络的指派立刻变回悬空(DRC 不报错,线宽也就不生效 —— 查不出来的那种坏)。
要改规则就改 pcb_rules.py 再 --push。
配置名下发后会自动变成 “自定义配置”,这是对的 —— 说明它已经从系统能力集上摘下来了。
顺带踩到的一个坑:mm 值会被换算成 mil 再换回来
写进去 0.2,回读是 0.19999959999999997(差 4e-7 mm)。
任何几何值都不能做精确比较,--check 的容差已放到 1e-4。
(第一版容差 1e-9,结果安全间距那项在明明写对的情况下报红。)
DRC 结果读得回来(.claude/drc_read.py),而且真抓到了 9 个错
先说一句:本轮早些时候写的「DRC 结果读不回来」是错的,已推翻。
pcb_Drc.check() 确实只返回一个 boolean,40 多个方法里没有结果 getter,
也没有 DRC 标记层可读。但 DRC 面板在 DOM 里是挂载着的
(跟设计规则对话框不一样,那个窗口不前台就不挂载)。三个手法:
- 左边分类树要用 React 的 onClick 点 —— 从元素上找
__reactProps$*, 一路往上找到带onClick的那层直接调。el.click()不管用,不点就不选中, 右边明细区一直是「空列表」。 - 「清除错误」那个反过来,它是原生
<button class="i18n">,没有 React props,el.click()直接生效。同一个面板上两种按钮混着,别一条路走到黑。 - 明细表 innerText 尾巴上粘着分页组件的一大段 CSS,碰到
input::-webkit截断。
跑法:/c/Python314/python .claude/drc_read.py(--no-check 只读不重跑)。
那一片黄叉:是 check() 画的,不是板子坏了
跑完 check(),每个未布线焊盘都盖一个黄叉,没布线时 1200+ 个,
满屏都是,看着像出了大事。分类是:
全部 (1273) = 间距错误 (9) + 连接性错误 (1264)
↑真错误 ↑还没布线,预期之内
只看「间距错误」那一支。 看完用面板上的「清除错误」清掉。
9 个真错误,两类,都已修掉(1273 → 1264)
① 贴片焊盘到贴片焊盘 ×5 —— 五路的 Cin 挨太近,差 10 µm
C%01 / C%02(Buck 输入去耦,§10.2-1 手调的热回路)两片焊盘只剩
0.1905 mm,安全间距要 0.2 mm,差 10 µm,五路一起报。
这是把全局间距从 0.152 抬到 0.2 mm 的直接后果 —— 老规则下它是过的。
改法:pcb_place.py 里 C%02 的 y 从 14.9 挪到 14.95,缝变 0.2405 mm。
0.05 mm 对约 4 mm 周长的热回路可以忽略。模型和板子都已改(五路各挪一颗)。
② 钻孔到钻孔 ×4 —— 四个 M3 定位孔各有一个重影
四个角上各有两个 MHn 焊盘,焊盘一样大(6.0 mm),钻孔一个 3.2 mm(对的),
另一个 0.32 mm —— 正好差 10 倍,当初建孔时 mm/mil 滑了一位,
改对之后旧的那 4 个没删。两个孔完全同心,距离 0 mil。
已删掉 4 个 0.32 mm 的,MHn 从 8 个回到 4 个,全是 3.2 mm / GND。
⚠️ 这两类本地几何检查一个都抓不到 ——
pcb_place.py的check()只看 本体框重叠和越框,不看焊盘间距,更不看钻孔。布局改完要跑一次drc_read.py。
布线时如果 U32 扇出被 0.2 mm 间距卡住
0.5 mm 间距 QFN 出线,相邻焊盘到走线边缘算下来约 0.233 mm,
比 0.2 mm 只多 33 µm,能过但很紧。真被刷屏了不要去改全局间距 ——
pcb_Drc.getRegionRules / overwriteRegionRules 可以在 U32 周围开一块局部规则区。
最新一轮:缩板 + 定位孔 + 设计规则(2026-09-03 深夜)
1. 板框 200×150 → 160×125,四角 R5 + 4 个 M3 定位孔
用户拍板的尺寸。定位孔 M3 金属化接 GND:孔 Φ3.2 / 焊盘 Φ6.0 / 禁布 Φ6.5, 孔心 (5,5)(155,5)(5,120)(155,120),与圆角同心。
⚠️ 四角定位孔和 band A 的连接器是几何上打架的,5 × 32 = 160 正好铺满板宽,
CH1 / CH5 的顶角必然被连接器本体压住。解开靠两条(都写在 pcb_place.py 的 CH1 表里):
- J7~J11(按键出线)是立式座,本来就不需要板边 —— 挪进通道内部 (25.5, 31), 顶边只留 J2~J6 一个 12 mm 宽的右角式座;
- J2 摆在通道中央(local x=15,本体 x 9~21),左右各让出 9 mm。
功率链那一段(local y 3~42)一个件都没动 —— §10.2 的四条关键回路是手调出来的, 缩板不该动它们。按键驱动那 9 个件从「J2 右边一横排」改成右列 x 24/28 的纵排。
check() 里新加了定位孔禁布区这一项。孔不是元件,原来那两项检查都看不见它。
2. 又是本体框:J2 离板边差了 4 mm
FP_BODY 里 Micro-Fit 的本体我上一轮手量成 12 × 13.5 mm,渲染图上重量是 9.8 × 10.1
(对得上 43650 两位右角式的 8.6 × 10.1)。偏大 4 mm 的后果是 check() 以为贴着板边、
实际差 4 mm。已改成 (-4.95, -9.0, 4.95, 1.4) 并把 J2 挪到 local y=49 顶住 y=125。
这是 FP_BODY 第二次出错了(第一次是 U20 压出板框)。手量的本体框都要复核一遍, 偏大是安全方向(连接器缩进去),偏小是危险方向(本体悬空出框)。
3. Hub 挪到板中央 —— D± 最长砍掉一半
原来 U32 在最左边(x=31),到 CH5 要走 112 mm。挪到板中央 x=79 之后:
| USB1 | USB2 | USB3 | USB4 | USB5 | 上游 J23 | |
|---|---|---|---|---|---|---|
| D± 长度 | 64 | 32 | 0 | 32 | 64 mm | 16 mm |
⚠️ U32 正上方就是 CH3 的负载开关块,所以去耦不能像原来那样在正下方排一横排 —— 改成上面一排 4 颗 + 左边一列 3 颗。
4. 设计规则与差分对(.claude/pcb_rules.py)
API 能做的已经做了:
- 7 对差分对:USB1~5(Hub → 五路出线座)、UPSTREAM(J23 → Hub)、DEBUG(J22 → ESP32)。
- 12 个功率网络指派线宽规则:VO1
VO5 / VO1_PVO5_P →PWR4A;+12V / +5V →PWR12V。
API 做不了的(当时这么写的,2026-09-03 深夜已推翻,见下一节):
—— 是参数形状给错了,不是 API 不行。saveRuleConfiguration/overwriteCurrentRuleConfiguration全部返回 false 或 undefined,什么也不做。
5. 外层铜厚:2 oz → 1 oz(用户 2026-09-03 拍板)
理由:U32 是 0.5 mm 间距的 QFN-64(2 oz 外层最小线宽/间距 0.2 mm 起,扇出很吃力, 1 oz 是 0.102 mm),而且 90 Ω 的几何随铜厚变(2 oz 下同样 W/S 只剩 83 Ω)。 设计文档 §10.1 已改写,附了完整的载流表。
这条改动连带否掉了一个数:原来 PWR12V 定的 2.5 mm,在 1 oz 下
8 A 温升 35 °C,不能用。已改成:
| 规则 | 值 | 说明 |
|---|---|---|
PWR4A |
默认 1.5 / 最小 0.3 / 最大 6.0 mm | 4 A 温升 16.7 °C ✓;最小值 2026-09-03 由 1.0 降到 0.3,见设计文档 §10.1 的补注 |
PWR12V |
默认 2.0 / 最小 0.3 / 最大 10.0 mm | 只给分支(每路 1.6 A);0402 抽头引不出 1.0 mm 的线 |
⚠️ 12V 总干那 8 A 不许当走线画 —— 1 oz 下要 3.49 mm 才够 20 °C。 必须走灌铜:Inner2-PWR(0.5 oz)上要 ≥18 mm 宽,或顶层灌铜 ≥3.5 mm。 没把
PWR12V的最小值设成 3.5 mm,是因为 +12V 上还有一堆进 0402/SOT-23 焊盘的 短支线,卡 3.5 会刷出几百条假 DRC 错误,规则就没人看了。 载流靠灌铜保证,不靠 DRC。
6. 还有一条要用户确认
- 90 Ω 的 W/S 必须拿板厂叠层复核。0.28/0.18 是按嘉立创四层 1.6 mm 标准叠层 (h=0.2104 mm、εr≈4.3、1 oz 外层)的微带近似算的,叠层一变就得重算。
上一轮(2026-09-03 晚)做了什么
新增了三个脚本(都在 .claude/):
| 脚本 | 干什么 |
|---|---|
refresh_cache.py |
重抓 pcb-parts.json / pcb-pads.json / sch-xy.json。改完原理图 + importChanges 之后必跑,否则 pcb_place.py 看到的还是旧元件表 |
shot_pcb.py |
截整块 PCB(shot.py 只能截原理图页,对 PCB 会报错) |
shot_pcb_region.py <out> <x0> <y0> <x1> <y1> |
截 PCB 的指定 mm 区域。方向错误只有放大了才看得出来,整板图上一个 SOT-23-6 才几个像素 |
1. PCB 导入终于点下去了:357 → 391 元件
上一轮卡在「importChanges() 弹了确认框但点不了」(屏幕会话拿不到,CopyFromScreen
报 handle invalid)。这轮换了个办法绕过去:桥接执行的 JS 能拿到主窗口的 document,
所以弹窗可以直接用 DOM 点:
[...document.querySelectorAll('button')].filter(b => b.innerText.trim()==='应用修改')[0].click();
弹窗本身在 div[class^=lc_modal_dialog__body] 里,变更表是虚拟滚动的,
DOM 里只有可见的那几行(想抓全表得滚动收集,实测容易撞 30 s 桥接超时,没必要)。
结果 357 → 391(新增 39、删掉 5 个 USB-A)。上一轮预估的 386 少算了几个。
做了一次逐位号对账(.claude/refresh_cache.py 顺带的能力):
原理图进 PCB 的 391 个 ↔ PCB 的 391 个,重号 0 / 漏 0 / 多余 0,
C801~C806 正确地没进来。
2. 【C】带按板载 Hub 重排(.claude/pcb_place.py)
USB-A 腾出来的 75 mm 板边给了 Hub。现在是:
x 2..28 【C1】12V→5V(U30 + L3 + Cin/Cout)—— 靠左板边,离 J1 的 12V 近
x 30..62 【C2】板载 Hub:J23 USB-C 在下板边 / U32 QFN-64 / Y1 / U31 3V3 LDO
x 64..198 【C3】五路 TPS22918 + J17~J21 出线,节距 27 mm
D± 全程留在这一带里,不纵穿五路 Buck 的电感与 SW 铜箔(§15.3)。
Hub 那 27 个件全部手排:仿射映射会把 strap 电阻甩进 U32 与晶振之间那条
1.8 mm 的缝里,而两个落在同一点的器件在推挤迭代里是对称的、永远分不开
(R719/R720 就卡在那儿,relax() 跑 3000 轮也出不来)。
3. 截图看出来的四个真错误(几何检查全都查不到)
| # | 错误 | 为什么本地检查抓不到 |
|---|---|---|
| 1 | U30 的 Cin 摆在了封装的另一侧,热回路绕整个封装 | 检查只管重叠/越框,不管「哪个焊盘挨着哪个」 |
| 2 | 五路负载开关的四个卫星件全部左右镜像(CIN/ON 该在右、COUT/CT 该在左) | 同上,而且五路一起错 |
| 3 | J22 调试口开口朝板内 | 封装本体外形 API 取不到,box() 用的是对称框 |
| 4 | U20 ESP32 模组整块压出右板框 2.7 mm | 同上 —— 模组原点在焊盘形心,天线端往外还有 7 mm |
根因是同一个:rot=0 时 SOT-23-6 的 1/2/3 在右列、4/5/6 在左列,
和画原理图时的直觉相反。现在是照着焊盘坐标读回来写死在注释里的,不是猜的:
U30 / U24~U28 (SOT-23-6, rot=0)
右列 自下而上 pin1(-0.94) / pin2(0) / pin3(+0.96)
左列 自下而上 pin6(-0.94) / pin5(0) / pin4(+0.96)
3 和 4 已经在 FP_BODY 里补了两条手量的不对称本体框(USB-C、ESP32 模组),
以后再动这两个器件,check() 就能自己拦住了。
读回焊盘框有个坑:
getState_Pad()给的w/h是未旋转的, 所以对rot≠0的元件,我那段「px−w/2」算出来的框是错的(J22 的 x/y 跨度和 同封装的 J23 对不上就是这么发现的)。要拿准确的框,得先把元件转回rot=0读一遍再转回去。pcb_place.py的FP_PAD只收rot==0的实例,正是为了避开这一条。
4. P4B-HUB 的压字
6 行注释横穿 J23 的符号(几何验证不看文字)。注释挪到 U32 右下的空白带
(sch (460, 210~300))—— 一次挪过头压到标题栏上,又上移了 30。
每次重画 P4B,
importChanges都会报 4 行J23/R717/R718 修改网络: CC1/CC2 是没有旗/端口的页内两脚网络,w('CC1', …)那个名字不会落到网表上, 每次重画都重新分配一个$15Nxxx。是噪声不是错误,已按提示应用。 想根治就给这两条网络各加一对端口。
前四轮做了什么(都在 2026-09-03)
0. P5-HARNESS(新增,原理图 10 页画齐了)
线束页,§20。四个分区:【A】三条线束的板端↔手机端连接图、【B】手机侧电容组(真电路)、
【C】五路对照表、【D】五条红字警告。除【B】外全是非电气图元
(矩形 / 折线 / 富文本),不参与网表。模型在 .claude/p5_harness.py。
手机侧电容组 C801~C806 是离板件:addIntoBom=false / addIntoPcb=false,
读回确认过。只画了 CH1 一套,CH2~CH5 位号见页上的【C】表。
顺手补了一个真缺口:VOn_P(Rs01 之后 → J.1 / Q*02 / INA226 的 VIN−、VBUS)
原来是页内一段没名字的网络,P5 的电容组挂不上去。已在五页各加一个
VOn_P 端口((960,745),dir='OUT',从 J2 那根横线上引 50 长支线),
五页重推 + 六项验证全过。
⚠️
VO2_PCH5 的电容组就删掉。VO5_P全工程只有 1 个实例(P5 只画了 CH1 一套电容组), 这不是漏接。.claude/xpage.py新增NAMING_ONLY集合把它们列为例外, 哪天补画 CH2
前三轮
1. 五路复制(新增 P2B~P2E 四页)
用 dmt_Schematic.copySchematicPage 从 P2 复制,再由
.claude/p2gen.py(模型)+ .claude/p2push.py(下发)按通道号改写位号 / 网络名 / 地址跳线。
复制时发现母版上有两个真问题,五页一起修了:
K1_VUP端口其实没连上。 它的引脚坐标和R122.1完全重合, 靠一段零长度导线"连着"。几何验证在旧 P2 上不报,是因为那段零长度线 恰好落在引脚上;真按这个网表打样,那一路音量+ 很可能是断的。 三个按键端口 +VDAC1现在统一改走向上 40 的短支线,末端才放端口。- 两处标签压字:
VDAC1与K1_PWR的标签面对面撞在一起(渲染成VDACK1_PWR); J7 的地旗文字压在Q105的位号上(渲染成G5)5)。
⚠️ 所以 CH1 也重画了一遍(元件没动,只重画了导线 / 旗 / 端口 / 文字)。 用户之前确认过的是旧版 P2,这一页要重新看一眼。
2. INA226 地址按布线难度重排(§8.2)
INA226 的 A1/A0 各能接 GND / VS / SDA / SCL,但只用 GND 和 VS 只能凑出 4 个地址 (0x40/0x41/0x44/0x45),全板要 6 个,必然有两片得把地址脚搭到 I²C 线上。 原来的顺排(0x40~0x44)会让 CH3 接 SDA、CH4 接 SCL,而 A0 脚上下正好夹着 Alert 和 SDA 两根横线,往 SCL 拉必然跨线。
| CH1 | CH2 | CH3 | CH4 | CH5 | U13(输入侧) | |
|---|---|---|---|---|---|---|
| A1 | GND | GND | VS | VS | GND | GND |
| A0 | GND | VS | GND | VS | SDA | SCL |
| 地址 | 0x40 | 0x41 | 0x44 | 0x45 | 0x42 | 0x43 |
这样五个通道里只有 CH5 需要搭 SDA(0 交叉),剩下那个 SCL 落到 U13 头上—— 它画在 P1 的空白区,布线自由。 代价:通道号不再等于地址低位,固件里要查表,不能算。
3. P1 补输入侧检流(§19)
U13 INA226 + Rs6 5mΩ + C11,量 12V 母线,地址 0x43(A1=GND / A0=SCL)。
体电容在 Rs6 上游,所以量到的是负载电流;满板约 8A → 40mV(满量程 81.92mV)。
U13 转了 90°,VIN+/VIN−/VBUS/GND/VS+ 五个脚朝上正对分流电阻,I²C 朝下走空白区。
又挖出一个同类真错误:P1 上 BLD_REF/BLD_A 两个端口的引脚坐标和 U29(TL431) 的
REF / 阴极完全重合,靠零长度导线"连着"。已挪开 40 并补真导线。
这类错误几何验证抓不到(零长度段会让"引脚落在导线上"通过),只有重画那一页才暴露——
规律已写进 skill §8a。
4. 之前:P4-USB(§18)
五路 TPS22918 负载开关,一路一列,D±/GND 直通、只切 VBUS,全页 0 处交叉。
ON 下拉 / CT 摆率 100pF / QOD 短接 VOUT / CIN>COUT 四条按 datasheet 定稿。
⚠️ TPS22918 的引脚号:datasheet 引脚图的文字被排版拆开后读起来像
1 VOUT / 2 QOD / …,和 Pin Functions 表相反。按文字坐标还原后确认表是对的:1 VIN / 2 GND / 3 ON / 4 CT / 5 QOD / 6 VOUT。接反了图上看不出来。
✅ 板载 USB Hub(2026-09-03 做完原理图部分)
外部只留一根 USB 线。用户拍板:USB2517I 单片 7 口 + USB-C 上游口。 硬件配置逐条对 datasheet 复核,写在设计文档 §21;5V 轨与 LDO 见 §9.4 / §9.5。
最终方案
| 项 | 结论 |
|---|---|
| Hub | U32 = USB2517I-JZX(C478081,QFN-64);口 1~5 给手机,口 6/7 用 10k 上拉禁用 |
| 配置 | CFG_SEL[2:0]=000 → 内部默认配置 + strap 使能;寄存器 06h 默认 9Bh、bit7=1 = 自供电 |
| 上游口 | J23 USB-C(UFP),CC1/CC2 各 5.1k 下拉,不做 PD |
| 下行 VBUS | 板上 5V 轨(U30 = 第二片 LMR51430,12V→4.98V),经 U24~U28 由 ESP32 通断 |
| Hub 的 3.3V | U31 AMS1117-3.3 专供(Hub 满配约 420mA,挂不上 U19 那条 0.5A 控制轨) |
| Hub 复位 | U22.Q7(74HC595 唯一那个空着的输出)→ HUB_RST,R716 10k 上拉 |
| 删除 | J12~J16 五个 USB-A 母座 |
三个 datasheet 陷阱(都已避开,细节在 §21)
- 别下错手册:Microchip 站上
00001692C.pdf是 USB251xB 系列(另一世代)。 我们这颗要看2517.pdf(SMSC USB2517 Rev 2.8)。 - 引脚图和引脚表打架(
RBIAS图上读作 64、表里是 63)。 按坐标还原引脚图后确认表是对的 —— 和 §18 的 TPS22918 同一个套路。 库里的符号也与表一致,三方对上了才动笔。 - 必须是自供电。声明成总线供电,主机按每口 100mA 做功率预算,
而手机描述符要 500mA → 主机拒绝配置,手机根本枚举不上。
靠
CFG_SEL=000走内部默认值拿到自供电,不需要 EEPROM。
验证结果
| 页 | 结果 |
|---|---|
| P4B-HUB | 27 元件 / 112 导线段,六项全过,0 处交叉,32 个脚在 NC 清单 |
| P1B-5V | 12 元件 / 44 导线段,六项全过,2 处合法交叉(+12V×SW5V、SW5V×CB5V) |
| P4-USB | 30 元件 / 180 导线段,六项全过,0 交叉 |
| P3B-EXPAND | 重推(加 HUB_RST),六项全过 |
| 跨页对账 | 0 个漏接(12 页);+5V/+3V3_HUB/HUB_RST/USB1~5_D± 全部两端对上 |
顺手修了一个遗留真错误:P3B 的
C611(DAC 的 CAP 旁路 2.2µF)在页面上被挪动过, 位置与p3b_nets.py的接线对不上,两个脚都是悬空的。上一轮没重跑 P3B 的验证所以没发现。 已按 skill §8d 用rot=270摆回横放(这颗 0805 传 rot=0 反而是竖的)。
这一页为什么满是网络端口
U32 是 65 脚 QFN,33 脚在左、32 脚在右,脚间距只有 10。外围器件直接连脚必然打结。 做法:每个用到的脚只引一小段 stub,末端放网络端口当页内标号, 器件按功能分块摆在四周,块间靠端口名对上。 netport 约 10 单位高,在 10 的脚间距上排一列不重叠(skill §8b-2);netflag 有 20 高,放不下。 J23 的 USB-C 正反脚也用端口重名并联,省掉了 Type-C 固有的一堆交叉(P3 的 J22 当年是靠绕线解的)。
这三件收尾都已经做完(2026-09-03 晚)
- PCB 导入已应用(357 → 391),逐位号对账通过。
- 【C】带已按 Hub 重排并下发,391 个全部在框内、无重叠。
- 三张空白截图已重截,都是好的。
⭐⭐ 第二件事:主板载板(2026-09-03 用户提出,尚未动手)
需求原话:「底板是现在设计的电源板,另外做一块板子,按照主板定位孔设计, 将 5 块主板固定在这块板子上,固定主板的板子直接对插到我们现在设计的这块板子」。
即两块板叠起来:
┌─────────────────────────────────────┐
│ 载板:按小米8 主板定位孔打孔,装 5 块主板 │
│ 每台的 P+/P− 焊盘 + §6.3 电容组 │
│ 侧键触点(顶针?)+ USB │
└───────────┬─────────────────────────┘
│ 板对板对插(电源 4A×5 + 信号约 30 根)
┌───────────┴─────────────────────────┐
│ 底板:现在这块 200×150 五路电源板 │
└─────────────────────────────────────┘
这是好事,但先说清楚它推翻了什么
推翻的:
- §15.4 的线束基本消失 —— 现在图上的 J2
J6(Micro-Fit)/ J7J11 / J17~J21 共 15 个连接器,会被板对板连接器取代。 - P5-HARNESS 那页要重写(它现在画的是线束)。
- PCB 布局的连接器分边决策作废。我 2026-09-03 排的「上边=电源+按键、 下边=USB 子系统」是为了线束走向和 D± 不穿功率区;改成板对板之后, 接口位置由机械叠层决定,不是由「线从哪出」决定。【A】【C】两带都要重排。
变好的(值得高兴的一条):
- §6「最容易翻车的地方」大幅缓解。 原方案是 20 cm 线缆 ≈ 300 nH, 射频突发 2 A/µs 时压降 0.6 V。改成板对板后回路电感掉到大约 30~50 nH 量级, 同样 di/dt 下压降约 0.1 V。粗算 L=50nH / C=2200µF: f_res 从 6.2 kHz 升到约 15 kHz,Z0 从 11.7 mΩ 降到约 4.8 mΩ, Q = 4.8/(走线+触点 R + ESR 60mΩ) ≈ 0.07,比原来还过阻尼。 → §6.1 / §6.2 的数要重算;C801 用普通铝电解做阻尼这条依然成立(更保险了)。
- §6.4「大电容必须在排线端」自动满足 —— 电容组直接放在载板上 P+/P− 焊盘旁边, 离得比原来更近,而且是贴片工艺,不用手焊线束。
✅ 头号阻塞项已解除(2026-09-04):机械数据一直就在我们手里
下面这段旧结论是错的,留着当教训:曾经断定
「Xiaomi_Mi_8_Schematic.pdf 是电气原理图,不含任何机械尺寸,只能实测」。
实际上那份 PDF 共 46 页,前 2 页是正反面位号图(含完整板框轮廓 + 全部位号),
后 44 页才是原理图。当初只按「原理图」去读,get_text() 一看全是位号就跳过了,
从没渲染出来看一眼 —— 一张图看 5 秒就能推翻的结论,靠翻文字翻了一整轮。
已经从中反推出:比例尺 17.0 pt/mm、主板 ≈ 45.0 × 66.8 mm、 J600 / J1401 / TP1204 / TP1205 / J601~J604 的坐标与所在面。方法与数据见设计文档 §22.3。
⚠️ 仍需用户做一件事:用卡尺核一次主板长宽。45 mm 对 74.8 mm 的机身偏窄, 怕比例尺有系统性偏差;后面所有孔位都按这个尺子换算。
六个待定项
- 电池那一路怎么接。 §13.5 已经论证过:必须保留原厂 BM3E 排线
(立创只有母座
5050040812,没有对插的公座;而且BATT_ID可能是 1-wire 认证 IC,复制不了)。所以载板上做P+/P−焊盘, 排线一头焊在载板、另一头插回主板 J600。 ⚠️ §13.5 那条「别焊错边」的坑原样保留 —— 只是从焊线束变成焊到载板焊盘。 - 侧键触点怎么接。 J601~J604 是主板上的弹片触点。 载板上用**弹簧顶针(pogo pin)**顶上去最干净,但要求机械对位准 (定位孔正好提供这个基准)。前提是主板装上去时触点那一面朝着载板 —— 要先确认第 2 项。
- USB 怎么从尾插小板引到载板。 USB-C 母座在原厂尾插小板上, 小板靠 FPC 插主板 J1401。最省事:一根短 USB 线从小板 USB-C 到载板上的连接器。 要不要把尾插小板也固定在载板上,一起定。
- 板对板连接器选型 —— 这条最难。 每台 2 根电源(4A)+ 4 根按键 + 4 根 USB = 10 根, 五台共 50 根,其中电源合计要过 20 A。单个信号 BTB 扛不住。 现实做法是电源与信号分开:电源用大电流连接器或多针并联, 信号用普通 BTB;D± 那 5 对还要满足 §15.3 的 90Ω 差分。
- 叠层间距(立柱高度)。 底板上现在有一批高个子:
C2/C3 470µF 直插电解(约 10
12 mm)、R9 水泥电阻(约 9 mm)、 ~~J12J16 USB-A 母座~~(Hub 改造后已全部删除,板上最高的连接器变成 J23 USB-C,约 3.2 mm)、 U20 ESP32 模组。要么立柱够高,要么把这些挪到不被载板压住的区域。 - 和「板载 USB Hub」那件事的关系。 两件事都动 USB 接口,建议一起设计: Hub 改造让外部只剩一根线,载板改造让内部不用线束 —— 合起来才是 「一根 USB + 一根 12V 进来,五台手机插上去就行」。
建议的推进顺序
- 先量主板(上表 1~4)。拿不到机械数据,载板一步都走不了。
- 量的同时可以并行做 Hub 改造(那件事不依赖机械数据)。
- 机械数据到手后:定叠层与板对板连接器 → 画载板外形与孔位 → 反过来改底板的接口区布局。
- 底板的功率部分(P1、五路 Buck、控制)不受影响,不用重画。 受影响的只有「接口怎么出板」这一层。
改完要同步的地方
| 位置 | 改什么 |
|---|---|
| 设计文档 §6.1 / §6.2 | 线缆电感 300nH → 板对板约 50nH,压降与 Q 重算 |
| 设计文档 §6.3 / §6.4 | 电容组从「装在排线端」改成「装在载板上 P+/P− 焊盘旁」 |
| 设计文档 §13.3 | 侧键从飞线改成顶针(如果定了顶针方案) |
| 设计文档 §15.4 | 线束表基本作废,改成板对板接口定义表 |
| 设计文档 新增一节 | 载板:机械、孔位、叠层、板对板连接器 |
| 原理图 P5-HARNESS | 从「线束页」改成「载板接口页」 |
| 原理图 P2 系列 / P4 | J2 |
| PCB | 接口区重排;载板是第二块 PCB(EasyEDA 里新建一个 Board) |
PCB(2026-09-03 转板完成)
PCB1(uuid 7e9415a16a909c51)挂在 Board1 下,关联原理图 c63aa0a04d951314。
| 项 | 结果 |
|---|---|
| 铜层 | pcb_Layer.setTheNumberOfCopperLayers(4) → 顶层 / Inner1-GND / Inner2-PWR / 底层 |
| 元件 | 357 个,与原理图逐个对上,无重号 |
| 网络 | 227 个 |
| 日志 | 致命错误 0 / 错误 0 / 警告 0 |
| P5 的 C801~C806 | ✅ 没有进来(addIntoPcb=false 生效) |
转板要知道的三件事
pcb_Document.importChanges()返回true只表示「弹窗开了」,不代表导入完成。 它会弹一个「确认导入信息」对话框列出全部变更,要点右下角的 「应用修改」 才真的写进去。 API 里没有确认这一步的接口,canvas 截图也拍不到弹窗(截图只有画布)—— 要看弹窗得截整屏(System.Drawing的CopyFromScreen)。- 导入前先
sch_Document.save(),否则 API 改的内容可能还没落盘。 - 「元件 $Ixx 唯一 ID 与其他元件重复」那 578 条提示是良性的。
来源是 P2B~P2E 由
copySchematicPage复制而来、内部唯一 ID 撞车, EasyEDA 生成网表时自动重分配。担心的是「每次重分配结果不同 → 以后改原理图会把布局冲掉」, 已实测排除:第二次importChanges日志是「无变更,原理图与 PCB 已经匹配!」, 说明重分配是确定性的,后续是增量更新。
PCB 布局(2026-09-03;第一遍见下,第二遍见开头「本轮」那节)
板框 200 × 150 mm,原点在左下角,Y 向上,API 单位 mil(sys_Unit.getFrontendDataUnit())。
模型在 .claude/pcb_place.py(--push 下发)。
截图:PCB-layout-v1.png(第一遍,357 元件)/ PCB-layout-v2.png(当前,391 元件)。
分区(2026-09-03 用户拍板板框尺寸 + 连接器边)
y=150 ── 上边:每路 J2~J6 电源 Micro-Fit + J7~J11 按键 4P(去手机)
【A】五路功率级 y 86~150,每路 40 mm 宽,同方向横排(§10.3)
y= 86 ──
【B】P1 输入(左 x0~96) │ P3/P3B 控制(右 x96~200) y 42~86
y= 42 ──
【C】USB 子系统 y 0~42(**已按板载 Hub 重排**,见开头「本轮」第 2 节)
y= 0 ── 下边:唯一的外部 USB 口 J23 在这条边上
左短边 = J1 12V 接线端子;右短边 = J22 调试 USB-C + U20 ESP32(天线朝板边)。
⚠️ 与用户选择的一处偏离(已知,等确认)
用户选的是「手机的三根线束全部同点出板」。但 J17~J21 放上边的话, D± 必须纵穿五路 Buck 的电感与 SW 铜箔,这是 §15.3 明令禁止的 (比线长本身严重得多)。所以实际排的是:
- 上边 = 每路的电源 + 按键(两根线束同点出板)
- 下边 = 整个 USB 子系统,J12(上游)与 J17(去手机)相距约 19 mm,D± 全程留在 USB 带里
要恢复成「三根同点」就把 J17~J21 挪到上边,代价是 D± 穿功率区。
怎么排的
- 五路功率级是显式手排(
pcb_place.py的CH1表),自下而上: 反馈/补偿 → Buck(Cin 贴 VIN/GND)→ 电感/输出 → eFuse → 分流+INA226(Kelvin)→ 连接器。 按键驱动放右列,远离 SW。CH1 排好后按 40 mm 节距平移复制五份(§10.3 同方向)。 - P1 / P3 / P3B 按原理图坐标仿射映射进各自分区,再跑分离迭代消重叠—— 原理图上的功能分组画图时就想清楚了,直接当粗排初值。
- 本地检查:重叠 0、越框 0。
踩到的三个坑
- 封装本体不一定以原点对称。 USB-A(KH-AF90ZJ)本体从原点往 −Y 伸 10.7 mm、 往 +Y 只有 1 mm。按对称尺寸算,第一次放 y=8 时本体伸出板框 2.7 mm, 而本地检查(用对称框)查不出来,是截图看出来的。已改到 y=11.5。
- API 取不到封装的本体外形。
pcb_PrimitiveLine.getAll()只返回板框那 4 条, 元件内部的丝印 / 外形图元不外露(和原理图里sch_PrimitivePin.getAll()对元件实例 返回空是同一个套路)。能读回来的只有焊盘:component.getState_Pads()给的是{primitiveId, net, padNumber}, 要用pcb_PrimitiveComponent.getAllPinsByPrimitiveId(id)才拿得到真实坐标, 焊盘尺寸在pad.getState_Pad()=["RECT", w, h, r]。 所以pcb_place.py的占位框 = 标称本体框 ∪ 真实焊盘框, 板边连接器另有一张手量的FP_BODY。「本体伸出板框」只能靠截图人眼看。 zoomToRegion的参数是(left, right, top, bottom),不是 (x0,y0,x1,y1)。 传错了不报错,只是没缩放。
下一步(按顺序)
- 人眼确认
PCB-layout-v4.png,尤其下面「等确认」那几条。 设计规则已全部下发(pcb_rules.py --push,--check七项全 OK)。 Hub 到五路的 D± 最长约 140 mm(U32 在 x=45、CH5 在 x=185), USB 2.0 HS 走得了,但必须是受控阻抗,且五对要等长成对。- 精调关键回路(§10.2):五路 Buck 的 Cin 热回路 / SW 铜箔 / FB 走线 / Kelvin 取样, 调好再重新复制五份;U30 那一路的 SW 只能走 x 13.9~15.4 那条 1.5 mm 的缝 (VIN 与 GND 夹着 SW 是这个封装的固有形状,绕不开)。
- U31(SOT-223,耗散约 0.71 W)与 U32 的散热铺铜。
- 灌铜(Inner1-GND 整片不许割断;Inner2-PWR 走 12V + 5V + 五路输出)→ 布线 → DRC。
等用户确认的事(几何验证证明不了)
- 板边连接器的朝向。都是从渲染图上量出来的推断,要对着连接器图纸核一眼——
接反了板子做出来插不进去:
J2~J6Molex 43650 右角式,转 180° 让开口朝上板边(未改动)。J23/J22USB-C:焊盘在 +Y、壳体往 −Y 伸 5.2 mm,也就是 rot=0 时开口朝 −Y。 J23 rot=0 开口朝下板边;J22 这轮从 270 改成了 90(原来开口朝板内)。J1KF128:丝印上两个大箭头 = 进线方向,rot=0 时朝 +Y(板内), 这轮改成了 90,进线朝左板边。箭头到底是不是进线方向,请核一眼。U20ESP32 模组:这轮从 x=186 挪到 183.2,让整块模组(含天线端)不出框。 如果你本来就想让天线伸出板外,那要挪回去并把板框改成带缺口的形状。
- 丝印位号大面积压字(EasyEDA 自动生成的),布线前要单独跑一遍丝印整理。
打样前的阻塞项(都不需要电源板,可并行做)
- 排线旁路验证(设计文档 §13.7)—— 确认焊在
P+/P−而非B+/B−。 判定性测试是第 3 步:可调电源升到 4.40V,BTB 座电压必须跟随且不断开。 - 侧键触点定位(§13.3)—— 万用表 5 分钟,拍照存档。带电验证那一步是安全闸门。
- 环路补偿实测(§9.2)—— 图上按 fco≈18kHz 放了 R106/C110/C111,三个位置都留了焊盘。
已知遗留
- 上游用的是 USB-A 母座(§15.1 定的),所以板到 Hub 之间是一根 A 公 - A 公线。 这不是标准 USB 用法;好处是物理上插不了充电头(VBUS 一旦被抬到 9V, TPS22918 的绝对最大 6V 会当场炸)。要换 USB-B / Type-C 就趁现在,改 5 个连接器而已。
- C7(P1 上 U19 的输入去耦)只有 2.2 µF,datasheet 要求高频去耦 ≥4.7 µF。 建议改成 10 µF/50V 0805(C440198,BOM 里已有)。不改也能工作, 但 PCB 上必须把 C7 贴到引脚旁。§9.3
- 电感 Isat 略低于目标:SPM6530T-4R7M 是 Isat 6.2A,原定 ≥7A。 正常峰值 4.61A、eFuse 限流工况 5.08A 都够;但 Buck 高侧限流 datasheet 是 8/11/14A,打嗝保护触发时电感会短暂深度饱和。首板要确认发热与饱和表现。
- eFuse 最坏容差下可能到 4.519V 才跳,超过 1S 上限 4.45V 约 60mV。已接受,理由见 §5。
- PCA9555 的 4 个备用脚直接接地(不串电阻),依赖"上电默认为输入"。 固件严禁把 Port0[7] / Port1[7:5] 配成输出。
- U29 泄放的 R9 是突发额定(10Ω/5W,13.3V 下 17.7W), 持续故障靠固件监控输入电压兜底:>13.0V 关全部 VBUS,>13.8V 关全部 EFUSE_EN。
- 交叉点:P2 系列各 6 处、P3 有 1 处(USB-C 的 D± 交叉,Type-C 固有)、P4 是 0 处。 几何验证确认互不相连。
- 手机侧 C802/C803 用了 MLCC 代替聚合物(§6.3)。立创基础库里没有 100µF/6.3V 聚合物, 改用 100µF/6.3V X5R 1206(C15008)。3.9V 偏压下每颗只剩约 40 µF, 中频段储能从 200 µF 掉到约 80 µF。C801 的 2200 µF 才是主力,估计影响不大, 但这是个待用户拍板的偏离:要严格照原方案就得单独采购聚合物。
- C801 是选型陷阱:必须买普通铝电解(C49256788,ESR 数十 mΩ)。 同尺寸的聚合物 C53446724(ESR 12 mΩ)会让线缆 LC 的 Q 从 0.19 回到 4.5 而振铃。 这一条已写成 P5 页上的红字。
沉淀下来的工具(.claude/ 下)
| 文件 | 用途 |
|---|---|
eda.py |
跑任意 JS 到桥接:python .claude/eda.py x.js。HTTP 500 会带出中文错误原文 |
place.py / place4.py |
批量放置元件;place4.py 支持 mirror(位置表 p4_pos.py) |
fetch_pins.py |
导出某页的真实引脚坐标成 pins.json |
build.py |
本地几何检查 + 分批推送(--push),用于 P3/P3B/P4 |
p2gen.py |
五路母版模型:几何来自 p2-ch1.json,按通道号改位号 / 网络名 / 地址跳线 |
p2push.py |
python .claude/p2push.py <n> <pageUuid> --temp|--final|--push |
p2-ch1.json |
CH1 页的几何快照(导线 / 旗 / 元件),是五页的唯一事实来源 |
shot.py |
截图某页存 PNG —— 画完先自己看一眼,能省掉大部分返工 |
xpage.py |
跨页网络对账,找"全工程只有一个实例"的网络 |
drc_read.py |
跑 DRC 并把结果读回来(DOM 抓 DRC 面板)。布局一改就跑,本地几何检查抓不到焊盘/钻孔间距 |
p3_nets.py / p3b_nets.py / p4_nets.py |
三页的网表源码(改图改这里,再跑 build.py) |
p4b_nets.py + p4b_pos.py |
P4B-HUB:USB2517I 那一页的位置表与网表 |
p1b_nets.py |
P1B-5V:12V→5V + Hub 专用 LDO |
p1_add.py |
P1 的增量:导线/旗/文字整页重画(底稿 p1-base.json),端口只增不重建 |
p5_harness.py |
P5 整页模型 + 下发(含文字越界 / 压标题栏的本地自检) |
pcb_place.py |
PCB 布局模型 + 下发(分区表 + CH1 手排 + 仿射映射 + 重叠/越框自检) |
pcb-parts.json |
PCB 侧 357 个元件的位号 / 封装 / 坐标(--push 前要重新读) |
pcb-pads.json |
每个元件的真实焊盘外框(封装不对称时靠它,见上面的坑 1) |
sch-xy.json |
每页元件的原理图坐标,仿射映射的输入 |
eda-parts.json |
器件 + 工程/图纸/全部 10 页的 UUID |
⚠️ P2 早期的
p2-nets.py/p2-pos.py等四个文件已和页面对不上(位号还是改号前的 C1/R1,端口位置也是旧的),已挪到.claude/obsolete/,别 import。 五路的唯一事实来源是p2-ch1.json+p2gen.py。
位号改名要分两趟
copySchematicPage 会把复制页的位号自动改成不撞车的(C11~C24、U2/U3/U4……),
而这些恰好就是别的通道要用的号。一趟改名会撞号被 EasyEDA 悄悄改掉,
所以 p2push.py 先把所有页都改成 Z<n>T<i> 临时名(--temp),
再统一改成目标名(--final)。
新会话开工检查清单
- 当前状态:
- ✅ 板载 USB Hub 的原理图部分做完了(12 页,跨页 0 漏接)。 还欠:把新器件导进 PCB + 【C】带重排 + 人眼看三张新图的截图。
- ⭐⭐ 主板载板(5 块主板固定在一块载板上,对插到底板)—— 卡在「没有小米8 主板的机械数据」,只能等用户实测。
- 桥接在不在:
/c/Python314/python .claude/eda.py随便跑一段 JS;不通就按下面「环境」起服务。 - 页面 UUID 全在
.claude/eda-parts.json的pages里(12 页都有),不用重新枚举。 - 动手前读
easyeda-sch-geometryskill,尤其 §8a(零长度导线)、§8b(端口 dir 决定标签朝向)、 §2(旗的 rot 语义,四种组合都实测过了)。 - 还欠人眼确认的页:P1-INPUT、P2-CHANNEL、P3-CONTROL、P3B-EXPAND、P5-HARNESS,
以及这轮新改/新增的 P4-USB、P4B-HUB、P1B-5V
(P2B~P2E 与 P2-CHANNEL 完全同构,看一页即可)。
⚠️ 截图 API 有时会抓到另一个窗口(返回的
windowId会变),拍出来只有元件没有导线。 遇到就换个时机重截,或直接在客户端里看 —— 先用 API 读回图元数量确认数据在不在,再判断。 - 改完任何一页,跑
verify_sch.py(当前页)+xpage.py(全工程),两个都要干净。
环境
- EasyEDA 桥接:
node C:/Users/35138/.claude/skills/easyeda-api/scripts/bridge-server.mjs后台运行,端口 49620~49629 自动选。客户端里要装run-api-gateway.eext扩展。 - Python:
/c/Python314/python(Windows 路径写C:\Python314\python.exe)。 已装pymupdf—— datasheet 可以直接下下来用fitz抽文字(get_text("words")带坐标,用来还原被排版拆散的引脚图,P4 就是靠这个确认 TPS22918 引脚号的)。 - 验证:
/c/Python314/python .claude/skills/easyeda-sch-geometry/scripts/verify_sch.py --nets(跑的是当前打开的那一页,所以要先openDocument)。 - 清空上下文后,先看
CLAUDE.md,再看这份,然后读easyeda-sch-geometryskill。