文件
phonerail/.claude/c2_xtal1m.py

130 行
6.6 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- 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()