原理图/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
112 行
4.5 KiB
Python
112 行
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
u"""按**实际铜**算某几个网络的连通性(不看 EDA 的 DRC,自己算)。
|
|
|
|
/c/Python314/python .claude/pcb2_net_conn.py [网络名 ...] # 省略 = 22 个差分网络
|
|
|
|
DRC 面板的「连接性错误」是按对象列的、还分页,查起来费劲;
|
|
这个脚本直接把一个网络的**焊盘 / 过孔 / 导线**当图的节点,
|
|
两两相触就连边,最后数连通域 —— 1 个就是通的,>1 就指出每个碎片里有什么。
|
|
|
|
判据(都按铜的实际外形,不留净距):
|
|
线-线:段距 <= (w1+w2)/2 且同层 线-焊盘:段到矩形 <= w/2(焊盘 layer 12 = 两个信号层都算)
|
|
线-过孔:点到段 <= w/2 + r(过孔穿所有层)
|
|
"""
|
|
import json
|
|
import math
|
|
import os
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
from pcb2_route_dp import geom, seg_pt, seg_seg, seg_rect
|
|
|
|
NETS = (['USB%d_%s' % (n, s) for n in range(1, 6) for s in ('DP', 'DM', 'DPJ', 'DMJ')]
|
|
+ ['UP_DP', 'UP_DM'])
|
|
EPS = 1e-6
|
|
|
|
|
|
def layers_of(lay):
|
|
return (1, 2) if lay == 12 else (lay,)
|
|
|
|
|
|
def main():
|
|
want = [a for a in sys.argv[1:] if not a.startswith('--')] or NETS
|
|
pads, vias, lines = geom()
|
|
bad = 0
|
|
for net in want:
|
|
items = []
|
|
for p in pads:
|
|
if p['net'] == net:
|
|
items.append(('pad', set(layers_of(p['lay'])), p))
|
|
for v in vias:
|
|
if v['net'] == net:
|
|
items.append(('via', set((1, 2)), v))
|
|
for l in lines:
|
|
if l['net'] == net:
|
|
items.append(('line', set((l['lay'],)), l))
|
|
n = len(items)
|
|
par = list(range(n))
|
|
|
|
def find(i):
|
|
while par[i] != i:
|
|
par[i] = par[par[i]]
|
|
i = par[i]
|
|
return i
|
|
|
|
def touch(a, b):
|
|
(ka, la, oa), (kb, lb, ob) = a, b
|
|
if not (la & lb):
|
|
return False
|
|
if ka == 'line' and kb == 'line':
|
|
return seg_seg((oa['x0'], oa['y0']), (oa['x1'], oa['y1']),
|
|
(ob['x0'], ob['y0']), (ob['x1'], ob['y1'])) <= (oa['w'] + ob['w']) / 2 + EPS
|
|
if ka == 'line' and kb == 'pad':
|
|
return seg_rect((oa['x0'], oa['y0']), (oa['x1'], oa['y1']),
|
|
ob['x'], ob['y'], ob['hw'], ob['hh']) <= oa['w'] / 2 + EPS
|
|
if ka == 'line' and kb == 'via':
|
|
return seg_pt(oa['x0'], oa['y0'], oa['x1'], oa['y1'],
|
|
ob['x'], ob['y']) <= oa['w'] / 2 + ob['r'] + EPS
|
|
if ka == 'via' and kb == 'via':
|
|
return math.hypot(oa['x'] - ob['x'], oa['y'] - ob['y']) <= oa['r'] + ob['r'] + EPS
|
|
if ka == 'via' and kb == 'pad':
|
|
return seg_rect((oa['x'], oa['y']), (oa['x'], oa['y']),
|
|
ob['x'], ob['y'], ob['hw'], ob['hh']) <= oa['r'] + EPS
|
|
if ka == 'pad' and kb == 'pad':
|
|
return (abs(oa['x'] - ob['x']) <= oa['hw'] + ob['hw'] + EPS
|
|
and abs(oa['y'] - ob['y']) <= oa['hh'] + ob['hh'] + EPS)
|
|
return touch(b, a)
|
|
|
|
for i in range(n):
|
|
for j in range(i + 1, n):
|
|
if touch(items[i], items[j]):
|
|
a, b = find(i), find(j)
|
|
if a != b:
|
|
par[a] = b
|
|
groups = {}
|
|
for i in range(n):
|
|
groups.setdefault(find(i), []).append(i)
|
|
npads = sum(1 for k, _, _ in items if k == 'pad')
|
|
ok = len(groups) == 1 and npads >= 2
|
|
bad += 0 if ok else 1
|
|
print(u' %-11s %2d 焊盘 / %2d 过孔 / %3d 线 -> %d 个连通域 %s'
|
|
% (net, npads, sum(1 for k, _, _ in items if k == 'via'),
|
|
sum(1 for k, _, _ in items if k == 'line'), len(groups),
|
|
u'OK' if ok else u'!!'))
|
|
if not ok:
|
|
for g in sorted(groups.values(), key=len, reverse=True):
|
|
desc = []
|
|
for i in g:
|
|
k, _, o = items[i]
|
|
if k == 'pad':
|
|
desc.append(u'焊盘 %s.%s' % (o['ref'], o['num']))
|
|
elif k == 'via':
|
|
desc.append(u'过孔(%.2f,%.2f)' % (o['x'], o['y']))
|
|
print(u' 碎片 %2d 个对象: %s'
|
|
% (len(g), u' / '.join(desc) if desc else u'(只有导线)'))
|
|
print(u'%d / %d 个网络不连通' % (bad, len(want)))
|
|
return 1 if bad else 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|