R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,129 @@
|
||||
# -*- 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()
|
||||
在新工单中引用
屏蔽一个用户