130 行
6.6 KiB
Python
130 行
6.6 KiB
Python
# -*- 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()
|