130 行
5.7 KiB
Python
130 行
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""板边可达性检查:每个接插件离**真板框**有多远?
|
||
|
||
/c/Python314/python .claude/edge_access.py # 下板 PCB1
|
||
/c/Python314/python .claude/edge_access.py --board2 # 载板 PCB2
|
||
|
||
## 为什么需要这条判据
|
||
|
||
摆位脚本自带的六项检查(净距 / 重叠 / 越框 / 定位孔 / 高度 / 锚点 / 去耦贴身)
|
||
**全是「元件之间」和「元件对板框」的检查,没有一条问「插头插得进去吗」。**
|
||
|
||
2026-09-08 实测后果:下板的板框常量从 160×125 改成真值 220×135 / 左下角 (−30,−5) 之后,
|
||
四个需要板边通道的接插件**全部悬在板子内部 30 mm**,而上方 13 mm 处压着载板:
|
||
|
||
| 位号 | 用途 | 离旧假框 | 离真板边 |
|
||
|---|---|---:|---:|
|
||
| `J1` | XT30 12V 进线,90° 板装 | 0.5 | **30.5** ⛔ |
|
||
| `J22` | 调试 USB-C | 0.4 | **30.4** ⛔ |
|
||
| `J26`/`J27` | 风扇 4P 直针座 | −29.0 | **1.2 / 9.0** ⛔ |
|
||
|
||
**六项检查全过、DRC 也全过 —— 因为它们确实没越框、也没压着谁。**
|
||
这类错误只有「离真板边多远」这一条判据抓得到。
|
||
|
||
⇒ 判据:凡是**需要从板外插入或引出线缆**的器件(连接器、USB、电源座、风扇座、
|
||
测试口),其本体包围盒到最近板框边的距离必须 ≤ `NEED_EDGE_MAX`。
|
||
板对板排针(与另一块板垂直对插)不在此列 —— 用 `VERTICAL_MATE` 排除。
|
||
|
||
⚠️ 顺带一条容易搞混的:**直针座(垂直插拔)也需要板边**。本项目的 4P 风扇座是直针,
|
||
插拔方向是 +Z,但两板间隙只有 13 mm 而座壳就 11.5 mm,插上线之后**线缆只能贴着板面
|
||
横向爬出去** —— 所以板边的意义是「线缆走得掉」,不是「插得进」。
|
||
"""
|
||
import io
|
||
import json
|
||
import os
|
||
import sys
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
sys.path.insert(0, HERE)
|
||
|
||
NEED_EDGE_MAX = 3.0 # 需要板边通道的器件,本体离板框最多这么远(mm)
|
||
|
||
#: **免检名单**:不是“从板外插线”的接插件。分两类:
|
||
#: ① 与另一块 PCB **垂直对插**(板对板排针 / 排母)
|
||
#: ② 与**放在载板上面的手机**对接(侧键中框式焊盘、尾插座、电池座)
|
||
#: 这两类都是 +Z 方向对接,板边对它们没有意义。
|
||
#: —— 第一版只写了①,载板上直接误报 30 条(J41~J65 / J81~J85 全是手机侧接口)。
|
||
VERTICAL_MATE = (
|
||
{'J24', 'J25', 'J31', 'J32'} # 两板之间的排针/排母
|
||
| {'J7%d' % n for n in range(1, 6)} # BM24 板对板
|
||
| {'J%d' % n for n in range(41, 66)} # 侧键焊盘 J41~J60 + 尾插 J61~J65
|
||
| {'J8%d' % n for n in range(1, 6)} # 手机位相关
|
||
)
|
||
|
||
|
||
def load(board2):
|
||
u"""从摆位脚本里取 POS / abs_box / 板框。两块板的摆位脚本结构一样。"""
|
||
fn = 'pcb2_place.py' if board2 else 'pcb1_place.py'
|
||
p = os.path.join(HERE, fn)
|
||
src = io.open(p, encoding='utf-8').read().replace('\nmain()\n', '\n')
|
||
g = {'__name__': 'm', '__file__': p}
|
||
exec(compile(src, fn, 'exec'), g)
|
||
if board2:
|
||
bx0, by0, bx1, by1 = 0.0, 0.0, g['BW'], g['BH']
|
||
else:
|
||
bx0, by0, bx1, by1 = g['BX0'], g['BY0'], g['BX1'], g['BY1']
|
||
return g, (bx0, by0, bx1, by1)
|
||
|
||
|
||
def _pcb2_adapter(g):
|
||
u"""载板 `pcb2_place.py` 的数据结构和下板不一样:摆位表由 `plan()` 现算、
|
||
包围盒是 `box(d, rot, bb, layer)`(相对元件原点)而不是 `abs_box(d)`。
|
||
这里把它包装成和下板一致的 `(POS, abs_box, fp)` 三件套。"""
|
||
pos = g['plan']()
|
||
bb = g['load_bb']() # {位号: 相对元件原点的焊盘包围盒}
|
||
|
||
def abs_box(d):
|
||
x, y, r, lay = pos[d]
|
||
b = g['box'](d, r, bb, lay)
|
||
return (x + b[0], y + b[1], x + b[2], y + b[3])
|
||
|
||
import json as _json, os as _os
|
||
_fp = {}
|
||
_f = _os.path.join(HERE, 'pcb2-parts.json')
|
||
if _os.path.exists(_f):
|
||
for c in _json.load(io.open(_f, encoding='utf-8')):
|
||
_fp[c['d']] = c.get('fp', '')
|
||
|
||
def fp(d):
|
||
return _fp.get(d, '')
|
||
return {k: v for k, v in pos.items()}, abs_box, fp
|
||
|
||
|
||
def main():
|
||
board2 = '--board2' in sys.argv
|
||
g, (BX0, BY0, BX1, BY1) = load(board2)
|
||
if board2:
|
||
POS, abs_box, fp = _pcb2_adapter(g)
|
||
else:
|
||
POS, abs_box, fp = g['POS'], g['abs_box'], g['fp']
|
||
print(u'%s 板框 x %.1f~%.1f y %.1f~%.1f' % (u'载板' if board2 else u'下板', BX0, BX1, BY0, BY1))
|
||
rows, bad = [], []
|
||
for d in sorted(POS):
|
||
f = (fp(d) or '')
|
||
if not (d[0] == 'J' or 'CONN' in f.upper() or 'USB' in f.upper() or 'HDR' in f.upper()):
|
||
continue
|
||
b = abs_box(d)
|
||
dist = min(b[0] - BX0, BX1 - b[2], b[1] - BY0, BY1 - b[3])
|
||
rows.append((dist, d, f, b))
|
||
if dist > NEED_EDGE_MAX and d not in VERTICAL_MATE:
|
||
bad.append((dist, d, f, b))
|
||
rows.sort()
|
||
print(u'\n%-7s %-36s %9s %s' % (u'位号', u'封装', u'离板边', u'本体包围盒'))
|
||
for dist, d, f, b in rows:
|
||
mark = u''
|
||
if d in VERTICAL_MATE:
|
||
mark = u' (垂直对插,免检)'
|
||
elif dist > NEED_EDGE_MAX:
|
||
mark = u' ⛔ 插头/线缆够不着'
|
||
print(u'%-7s %-36s %9.1f x %7.1f~%-7.1f y %7.1f~%-7.1f%s'
|
||
% (d, f[:36], dist, b[0], b[2], b[1], b[3], mark))
|
||
print()
|
||
if bad:
|
||
print(u'⛔ %d 个接插件离真板边超过 %.1f mm:%s'
|
||
% (len(bad), NEED_EDGE_MAX, u', '.join(x[1] for x in bad)))
|
||
sys.exit(1)
|
||
print(u'✅ 所有需要板边通道的接插件都在板边 %.1f mm 以内' % NEED_EDGE_MAX)
|
||
|
||
|
||
main()
|