# -*- coding: utf-8 -*- u"""C2-HUB(R1 载板):补 USB2517 晶振的 **1 MΩ 跨接电阻**(XTAL1–XTAL2)。 /c/Python314/python .claude/c2_xtal1m.py # 读活页 + 碰撞检查(干跑) /c/Python314/python .claude/c2_xtal1m.py --push # 放件 + 连线 + 端口 + 存盘 ## 依据 USB2517 Hardware Design Checklist(DS00004211)p9 原文:「A 1 MΩ resistor connected across XTAL1 and XTAL2 is **required**. Failure to place this resistor will result in unstable crystal operation.」 数据手册 DS00001598C p52 Figure 9-1 典型晶振电路也画着 1Meg。 2026-09-10 核实:XTAL1 / XTAL2 上只有 U32 / Y1 / C740 / C741 —— **R1 起就漏了**(R2 底板照搬,同样漏)。 用户 2026-09-10 定:底板(`r2/a4_hub.py` 的 R723)和 R1 载板**都补**。 ## ⚠ 用前 - 要先在客户端里**打开 R1 工程**(五路手机直供电源板):本脚本只 `openDocument` 这一页、不切工程, 页名不是 `C2-HUB` 就停手(`c2_cfgsel.open_page()` 的断言)。切工程前先把 R2 那边存盘。 - 只动原理图。载板 PCB(PCB2)上的摆位 / 布线另做 —— Y1 在 (95.8, 58.8)、C740 (92.8, 59.65)、 C741 (96.9, 55.7)(`pcb2_place.hub_layout()`),导入变更之后要给这颗 0402 找位置并补两段短线。 - 做完跑 `verify_sch.py --nets`(先打开这一页)+ `port_audit.py`,再让用户看截图。 ## 画法(同底板 A4-HUB 的 R723) Y1 正上方 y=800 那一行空着:电阻横放 rot 0(1 脚 x−20、2 脚 x+20,同页 R708 实测), 两端各一段短线接 `XTAL1` / `XTAL2` 同名端口 —— 本页本来就是「一脚一段短线 + 同名端口」的画法。 页面说明最上一行在 y=780(11 号字,盒子到 y≈789),端口标签在 y 795~805,不压字;右端标签到 x≈1115 < 边框 1145。 """ import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from c2_cfgsel import call, open_page, dump, _on, FRAME # noqa: E402 (c2_cfgsel 的 main 在 __main__ 保护下) DES = 'R723' LIB = '0819f05c4eef4c71ace90d822a990e87' R1M = '512988296f6244ad86a0777e419ee0f1' # C26083 0402WGF1004TCE 1MΩ(`lib_Device.get` 核过) RC = (1000, 800) PINS = sorted([[RC[0] - 20, RC[1]], [RC[0] + 20, RC[1]]]) W = [((950, 800), (980, 800), 'XTAL1'), ((1020, 800), (1050, 800), 'XTAL2')] PT = [['BI', 'XTAL1', 950, 800, 180], # BI + rot180 ⇒ 符号在左,占 885~950 ['BI', 'XTAL2', 1050, 800, 0]] # BI + rot0 ⇒ 符号在右,占 1050~1115 BOX = (880, 785, 1120, 815) # 新块(件 + 线 + 端口标签)要占的矩形 def check(geo): errs = [] if any(p[0] == DES for p in geo['parts']): errs.append(u'页上已经有 %s(跑过了?)—— 不重复放' % DES) inb = lambda x, y: BOX[0] <= x <= BOX[2] and BOX[1] <= y <= BOX[3] # noqa: E731 for des, _x, _y, _r, pins in geo['parts']: for nm, px, py in pins: if inb(px, py): errs.append(u'引脚 %s.%s@(%d,%d) 落在新块里' % (des, nm, px, py)) for net, x, y, _r in geo['flags']: if BOX[0] - 20 <= x <= BOX[2] + 20 and BOX[1] - 25 <= y <= BOX[3] + 10: errs.append(u'旗 %s@(%d,%d) 挨着新块' % (net, x, y)) for net, x, y, _r, _nm in geo['ports']: if BOX[0] - 60 <= x <= BOX[2] + 20 and BOX[1] - 10 <= y <= BOX[3] + 10: errs.append(u'端口 %s@(%d,%d) 挨着新块' % (net, x, y)) for w in geo['wires']: for i in range(0, len(w) - 3, 4): a, b = (w[i], w[i + 1]), (w[i + 2], w[i + 3]) if inb(*a) or inb(*b): errs.append(u'已有导线 %s-%s 的端点在新块里' % (a, b)) elif a[0] == b[0] and BOX[0] <= a[0] <= BOX[2] and min(a[1], b[1]) < BOX[3] and max(a[1], b[1]) > BOX[1]: errs.append(u'已有竖线 x=%d 穿过新块' % a[0]) elif a[1] == b[1] and BOX[1] <= a[1] <= BOX[3] and min(a[0], b[0]) < BOX[2] and max(a[0], b[0]) > BOX[0]: errs.append(u'已有横线 y=%d 穿过新块' % a[1]) for (a, b, net) in W: assert (a[0] == b[0]) != (a[1] == b[1]) and all(v % 5 == 0 for v in a + b), net for it in PT: assert it[2] % 5 == 0 and it[3] % 5 == 0 if BOX[2] > FRAME[2] or BOX[3] > FRAME[3]: errs.append(u'新块越框 %s' % (BOX,)) # XTAL1 / XTAL2 这两个网在页上必须已经存在(同名端口才连得上) have = set(p[0] for p in geo['ports']) for net in ('XTAL1', 'XTAL2'): if net not in have: errs.append(u'页上没有 %s 端口 —— 同名端口连不上' % net) return errs def main(): push = '--push' in sys.argv geo = dump() print(u'当前页:元件 %d / 旗 %d / 端口 %d / 导线图元 %d' % (len(geo['parts']), len(geo['flags']), len(geo['ports']), len(geo['wires']))) errs = check(geo) if errs: print(u'检查 %d 条:' % len(errs)) for e in errs: print(u' ' + e) raise SystemExit(u'未通过,不下发') print(u'碰撞检查通过:%s 放在 %s,XTAL1/XTAL2 两个端口' % (DES, RC)) if not push: print(u'加 --push 执行') return open_page() call(""" await eda.sch_PrimitiveComponent.create({libraryUuid:'%s', uuid:'%s'}, %d, %d, undefined, 0, false, true, true); for (const c of await eda.sch_PrimitiveComponent.getAll()){ if (c.getState_ComponentType()!=='part') continue; if (Math.round(c.getState_X())!==%d || Math.round(c.getState_Y())!==%d) continue; await c.toAsync().setState_Designator('%s').done(); } return 1;""" % (LIB, R1M, RC[0], RC[1], RC[0], RC[1], DES)) got = call(""" for (const c of await eda.sch_PrimitiveComponent.getAll()){ if (c.getState_ComponentType()!=='part' || c.getState_Designator()!=='%s') continue; return (await c.getAllPins()).map(p=>[Math.round(p.getState_X()),Math.round(p.getState_Y())]).sort(); } return null;""" % DES) if sorted(got or []) != PINS: raise SystemExit(u'%s 引脚 %s,应为 %s —— 不连线,先查' % (DES, got, PINS)) print(u'%s 引脚坐标核对通过 %s' % (DES, got)) call("for (const s of %s) await eda.sch_PrimitiveWire.create(s); return 1;" % json.dumps([[a[0], a[1], b[0], b[1]] for a, b, _n in W])) call("for (const p of %s) await eda.sch_PrimitiveComponent.createNetPort(p[0],p[1],p[2],p[3],p[4],false); return 1;" % json.dumps(PT)) call("return await eda.sch_Document.save();") print(u'推送完成并存盘 —— 接着打开这一页跑 verify_sch.py --nets / port_audit.py,再截图给用户看') if __name__ == '__main__': main()