# -*- coding: utf-8 -*- u"""载板 C3-USB:五路 USB 的 VBUS 负载开关(TPS22918)。 2026-09-04 方案 D 改造:**删掉 J17~J21 那五个 1.25mm 出线座**。 现在 USB 是「C2-HUB → C5-AUX 的 ESD → C5-J1401 的 BM24 座 → 原厂排线 → 手机」, 本页只负责切 VBUS,D± 根本不经过这一页。 本页原来由 `p4_nets.py` + `build.py` 生成(那时还叫 P4-USB,在下板上)。 这个脚本是自带布线的重写版:**元件位置一律沿用页面上现成的**(只删 J1x), 所以不会动到已经验证过的开关部分排版。 用法: /c/Python314/python .claude/c3_usb.py [--push] """ import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run PAGE = 'f924c6608ffd1bc1' # C3-USB XS = [130, 345, 560, 775, 990] def call(js, timeout=180): r = run(js, timeout=timeout) if not r.get('success'): raise RuntimeError(json.dumps(r, ensure_ascii=False)[:900]) return r['result'] def wiring(pins): W, F, PT, TX = [], [], [], [] def w(*p): for i in range(0, len(p) - 2, 2): W.append([p[i], p[i + 1], p[i + 2], p[i + 3]]) for n, X in enumerate(XS, 1): u = pins['U%d' % (23 + n)] c70, c71, c72 = pins['C70%d' % n], pins['C71%d' % n], pins['C72%d' % n] r70 = pins['R70%d' % n] # 元件位置沿用页面上现成的 —— 这里把每一个用到的坐标都断言一遍 exp = [(u['1'], (X + 75, 600), 'U.VIN'), (u['2'], (X + 85, 600), 'U.GND'), (u['3'], (X + 95, 600), 'U.ON'), (u['4'], (X + 95, 480), 'U.CT'), (u['5'], (X + 85, 480), 'U.QOD'), (u['6'], (X + 75, 480), 'U.VOUT'), (c70['2'], (X + 35, 595), 'C70.2'), (c70['1'], (X + 35, 555), 'C70.1'), (c71['1'], (X + 55, 420), 'C71.1'), (c71['2'], (X + 55, 380), 'C71.2'), (c72['1'], (X + 95, 450), 'C72.1'), (c72['2'], (X + 95, 420), 'C72.2'), (r70['1'], (X + 125, 660), 'R70.1'), (r70['2'], (X + 125, 620), 'R70.2')] for got, want, nm in exp: assert tuple(got) == want, u'CH%d 的 %s 实测 %s 期望 %s' % (n, nm, got, want) # ---- 板上 5V 轨 -> 开关 VIN,带 2.2µF 输入电容 C70n ---- # +5V 端口原来放在 y=660,正好和 (X+65,640) 那个朝上的 GND 旗叠在一起(截图上看出来的), # 往上挪到 700 就干净了 —— VBUS_EN 的横线在 y=685,只跨 X+25~X+95,不碰 X+15。 w(X + 15, 700, X + 15, 610, X + 75, 610) w(X + 75, 610, X + 75, 600) PT.append(['BI', '+5V', X + 15, 700, 0]) w(X + 35, 610, X + 35, 595) w(X + 35, 555, X + 35, 540) F.append(['Ground', 'GND', X + 35, 540, 0]) # ---- 开关自己的地 ---- w(X + 85, 600, X + 85, 620, X + 65, 620) w(X + 65, 620, X + 65, 640) F.append(['Ground', 'GND', X + 65, 640, 180]) # ---- ON:VBUS_ENn 端口 + 100k 下拉 R70n ---- w(X + 95, 600, X + 95, 685, X + 25, 685) w(X + 25, 685, X + 25, 675) PT.append(['BI', 'VBUS_EN%d' % n, X + 25, 675, 0]) w(X + 95, 660, X + 125, 660) w(X + 125, 620, X + 125, 605) F.append(['Ground', 'GND', X + 125, 605, 0]) # ---- 输出 VBUSn:QOD 短接 VOUT,100nF 输出电容 C71n,再送去 C5-AUX / C5-J1401 ---- w(X + 75, 480, X + 75, 330) w(X + 75, 330, X + 15, 330) PT.append(['BI', 'VBUS%d' % n, X + 15, 330, 180]) w(X + 85, 480, X + 85, 455, X + 75, 455) w(X + 75, 420, X + 55, 420) w(X + 55, 380, X + 55, 365) F.append(['Ground', 'GND', X + 55, 365, 0]) # ---- CT 摆率电容 C72n ---- w(X + 95, 480, X + 95, 450) w(X + 95, 420, X + 95, 405) F.append(['Ground', 'GND', X + 95, 405, 0]) TX.append((X + 45, 705, u'CH%d' % n, 13)) TX += [ (45, 796, u'【载板 C3-USB】五路 VBUS 负载开关(TPS22918)—— 本页只切电,D± 不经过这一页', 14), (45, 776, u'USB 链路:C2-HUB 的 U32 → C5-AUX 的 ESD → C5-J1401 的 BM24 座 → 原厂排线 → 手机 J1401。', 10), (45, 185, u'TPS22918: 1~5.5V / 2A / Ron 52mΩ,ON 脚不得悬空', 11), (45, 168, u'R701~705 100k 下拉 → ESP32 复位期间 VBUS 全断', 11), (45, 151, u'CT 100pF C0G: tR≈425µs,涌流≈94mA(10µF 负载)', 11), (45, 134, u'QOD 短接 VOUT: 内部 RPD≈24Ω,关断约 200µs 归零', 11), (45, 117, u'CIN 2.2µF > COUT 100nF;VBUS 来自板上 5V 轨 U30,不吃上游线电流(§9.4)', 11), (45, 100, u'2026-09-04 方案 D:J17~J21 五个 1.25mm 出线座已删 —— USB 改走原厂尾插排线(§13.5)', 11), ] # 下发前的几何断言(skill §6c / §8a-2b) for a, b, c, e in W: assert (a == c) != (b == e), u'有一段导线不是横平竖直或零长度: %s' % ((a, b, c, e),) for v in (a, b, c, e): assert v % 5 == 0, u'导线坐标 %d 不是 5 的倍数: %s' % (v, (a, b, c, e)) for lst in (F, PT): for it in lst: assert it[2] % 5 == 0 and it[3] % 5 == 0, \ u'%s 坐标 (%d,%d) 不是 5 的倍数' % (it[1], it[2], it[3]) return W, F, PT, TX def main(): push = '--push' in sys.argv if not push: print(u'C3-USB:删 J17~J21,重画布线(元件位置沿用现成的)。加 --push 执行') return # 只删 J17~J21 这五个连接器 + 全部旗/端口/导线/文字;其余元件原地不动 n = call(""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r=>setTimeout(r,1400)); const nm=(await eda.dmt_Schematic.getCurrentSchematicPageInfo()).name; if (nm!=='C3-USB') throw new Error('page='+nm); let conn=0, flag=0; for (const c of await eda.sch_PrimitiveComponent.getAll()){ const t=c.getState_ComponentType(); if (t==='sheet') continue; if (t==='netflag'||t==='netport'){ await eda.sch_PrimitiveComponent.delete(c.getState_PrimitiveId()); flag++; continue; } if (t==='part' && /^J(1[7-9]|2[01])$/.test(c.getState_Designator())){ await eda.sch_PrimitiveComponent.delete(c.getState_PrimitiveId()); conn++; } } for (const w of await eda.sch_PrimitiveWire.getAll()) await eda.sch_PrimitiveWire.delete(w.getState_PrimitiveId()); for (const t of await eda.sch_PrimitiveText.getAll()) await eda.sch_PrimitiveText.delete(t.getState_PrimitiveId()); const left=(await eda.sch_PrimitiveComponent.getAll()).filter(c=>c.getState_ComponentType()==='part').length; return {conn, flag, left}; """ % PAGE) print(u'删掉连接器 %d 个、旗/端口 %d 个,剩余元件 %d 个' % (n['conn'], n['flag'], n['left'])) # 第一次跑删掉 5 个;之后重跑时它们已经不在了,conn=0 也是正常的。 # ⚠ 这个守卫原来写成 `!=5 就退出`,第二次跑就会在「导线已经删光」之后才退出, # 把页面留在半清空状态(2026-09-04 踩过一次)。守卫要么放在删之前,要么就别拦。 if n['conn'] not in (0, 5): raise SystemExit(u'期望删掉 5 个或 0 个连接器,实际 %d 个 —— 先查清楚再跑' % n['conn']) if n['left'] != 25: raise SystemExit(u'剩余元件 %d 个,期望 25(5 路 x 5 件)—— 先查清楚再跑' % n['left']) raw = call(""" const out={}; for (const c of await eda.sch_PrimitiveComponent.getAll()){ if (c.getState_ComponentType()!=='part') continue; out[c.getState_Designator()] = (await c.getAllPins()).map( p=>[p.getState_PinNumber(), Math.round(p.getState_X()), Math.round(p.getState_Y())]); } return out; """) pins = {k: {p: (x, y) for p, x, y in v} for k, v in raw.items()} W, F, PT, TX = wiring(pins) def batch(items, size, js): for i in range(0, len(items), size): call(js % json.dumps(items[i:i + size], ensure_ascii=False)) batch(W, 40, "for (const s of %s) await eda.sch_PrimitiveWire.create(s); return 1;") batch(PT, 25, "for (const p of %s) await eda.sch_PrimitiveComponent.createNetPort(p[0],p[1],p[2],p[3],p[4],false); return 1;") batch(F, 25, "for (const f of %s) await eda.sch_PrimitiveComponent.createNetFlag(f[0],f[1],f[2],f[3],f[4],false); return 1;") batch([list(t) for t in TX], 12, "for (const t of %s) await eda.sch_PrimitiveText.create(t[0],t[1],t[2],0,null,null,t[3],false,false,false,0); return 1;") print(u'C3-USB: 导线 %d 旗 %d 端口 %d 文字 %d' % (len(W), len(F), len(PT), len(TX))) if __name__ == '__main__': main()