R2 底板改 4 层(D15):规则 / 几何 / 背面摆位 / 4 层布线与铺铜,已下发
- 规则:铜层 2→4,差分 0.28/0.18,间距 0.15,过孔 0.3/0.2(rules_base) - 几何:98×98 板框 + 4×M3,槽阵居中,L1 12V 大铜 + 指头(pcb_base) - 摆位:五插槽留 L1,其余 133 件翻到 L4;背面镜像规则实测;Hub 放走廊 2、 东移 2.5 mm;扇出圈 / 小件间距 1.5(去耦例外)/ 9 A 过孔阵禁区(place_base) - 叠层按用户决定改为 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面 - 布线:D± 七对按构造布通(USB3 掉头、USB1/2 北去掉头、USB4/5 东绕、 J23 桥),+3V3 / GND / +12V 平面网每脚一孔,0.125 网格 + PathFinder 开关(route_base、remote_route.ps1);剩 37 个网待手工补 - 铺铜:4 层重写,入口链自南向北切段 + 断面校核(pour_base); 已下发布线 / 缝合孔 / 铜面轮廓,填铜未成功(待查) - 工具:plot_base / plot_routes / shot_base,board_check 通孔两层都算 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
这个提交包含在:
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""§7.2 四条关键回路的**机器判据**(在真实焊盘坐标上算,不看图)。
|
||||
|
||||
/c/Python314/python .claude/r2/loop_check.py
|
||||
|
||||
## 为什么要有这个
|
||||
|
||||
§7.2 的四条回路(Buck 输入环路 / SW 面积 / FB 远离 SW / Kelvin 取样)
|
||||
以前只能「布完线截图人眼看」。但**摆位阶段就已经决定了这四条能不能成立** ——
|
||||
布线器再聪明也救不回「Cin 离 U1 12 mm」。M4c 第一版就是这么过的关:
|
||||
几何自检(重叠 / 越框 / 禁布区)全绿,四条回路破了三条,**没有任何一条检查报错**。
|
||||
|
||||
⇒ 这里把四条写成数:读 `_blade_pads.json`(`pcb_route.py --dump` 抓的真实焊盘),
|
||||
按网络名找到相关焊盘,算距离 / 面积 / 线段是否交叉。**摆位一改就跑一次。**
|
||||
|
||||
⚠ 判据只覆盖**摆位**。布完线还要跑 `pcb_route.py --check`(Kelvin 那条的走线判据)
|
||||
和 `router_check.py`(净距),三个不互相替代。
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
PADS = os.path.join(HERE, '_blade_pads.json')
|
||||
|
||||
SW = '$1N137' # U1 的开关节点
|
||||
FB = '$1N126' # 反馈节点(R1/R2 分压 + U1 p7)
|
||||
VIN = '+12V_F' # 保险之后的 12V
|
||||
CIN = ['C4', 'C1', 'C2', 'C3'] # 输入电容,由近到远
|
||||
|
||||
|
||||
def pads():
|
||||
d = json.load(io.open(PADS, encoding='utf-8'))
|
||||
out = []
|
||||
for r in d:
|
||||
out.append({'ref': r[0], 'pin': str(r[1]), 'x': r[2], 'y': r[3],
|
||||
'w': r[4], 'h': r[5], 'layer': r[6], 'net': r[7]})
|
||||
return out
|
||||
|
||||
|
||||
def sel(ps, ref=None, net=None):
|
||||
return [p for p in ps
|
||||
if (ref is None or p['ref'] == ref) and (net is None or p['net'] == net)]
|
||||
|
||||
|
||||
def gap(a, b):
|
||||
u"""两个矩形焊盘的**净距**(边到边,重叠算 0)。"""
|
||||
dx = max(abs(a['x'] - b['x']) - (a['w'] + b['w']) / 2.0, 0.0)
|
||||
dy = max(abs(a['y'] - b['y']) - (a['h'] + b['h']) / 2.0, 0.0)
|
||||
return math.hypot(dx, dy)
|
||||
|
||||
|
||||
def dist(a, b):
|
||||
return math.hypot(a['x'] - b['x'], a['y'] - b['y'])
|
||||
|
||||
|
||||
def cross(p1, p2, p3, p4):
|
||||
u"""线段 p1p2 与 p3p4 是否相交。"""
|
||||
def s(a, b, c):
|
||||
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])
|
||||
d1, d2 = s(p3, p4, p1), s(p3, p4, p2)
|
||||
d3, d4 = s(p1, p2, p3), s(p1, p2, p4)
|
||||
return ((d1 > 0) != (d2 > 0)) and ((d3 > 0) != (d4 > 0))
|
||||
|
||||
|
||||
def main():
|
||||
ps = pads()
|
||||
bad, note = [], []
|
||||
|
||||
# ── 1. Buck 输入环路:Cin 的两个焊盘各自到 U1 最近的 VIN / GND 脚 ──
|
||||
u1_vin = sel(ps, 'U1', VIN)
|
||||
u1_gnd = [p for p in sel(ps, 'U1', 'GND') if p['w'] < 2] # 去掉散热焊盘
|
||||
for ref in CIN:
|
||||
hot = sel(ps, ref, VIN)
|
||||
gnd = sel(ps, ref, 'GND')
|
||||
if not hot or not gnd:
|
||||
bad.append(u'%s 找不到 %s/GND 焊盘' % (ref, VIN))
|
||||
continue
|
||||
a = min(gap(hot[0], q) for q in u1_vin)
|
||||
b = min(gap(gnd[0], q) for q in u1_gnd)
|
||||
note.append(u' 输入环路 %-3s: VIN 边 %.2f + GND 边 %.2f = **%.2f mm**'
|
||||
% (ref, a, b, a + b))
|
||||
if ref == CIN[0] and a + b > 3.0:
|
||||
bad.append(u'最近的输入电容 %s 环路 %.2f mm > 3.0(§7.2 第一条)' % (ref, a + b))
|
||||
|
||||
# ── 2. SW 面积:所有 SW 焊盘的外接矩形 ──
|
||||
sw = sel(ps, net=SW)
|
||||
if sw:
|
||||
x0 = min(p['x'] - p['w'] / 2 for p in sw)
|
||||
x1 = max(p['x'] + p['w'] / 2 for p in sw)
|
||||
y0 = min(p['y'] - p['h'] / 2 for p in sw)
|
||||
y1 = max(p['y'] + p['h'] / 2 for p in sw)
|
||||
area = (x1 - x0) * (y1 - y0)
|
||||
refs = sorted(set(p['ref'] for p in sw))
|
||||
note.append(u' SW 节点: %s,外接矩形 %.1f×%.1f = **%.1f mm²**'
|
||||
% ('/'.join(refs), x1 - x0, y1 - y0, area))
|
||||
u1sw = sel(ps, 'U1', SW)
|
||||
l1sw = sel(ps, 'L1', SW)
|
||||
if u1sw and l1sw:
|
||||
g = min(gap(a, b) for a in u1sw for b in l1sw)
|
||||
note.append(u' U1 的 SW 脚到 L1 的 1 脚: 焊盘净距 **%.2f mm**' % g)
|
||||
if g > 2.0:
|
||||
bad.append(u'U1→L1 的 SW 焊盘净距 %.2f mm > 2.0(§7.2 第二条)' % g)
|
||||
if area > 60:
|
||||
bad.append(u'SW 铜面积 %.1f mm² 偏大(§7.2 第二条)' % area)
|
||||
|
||||
# ── 3. FB 远离 SW ──
|
||||
fb = sel(ps, net=FB)
|
||||
if fb and sw:
|
||||
g = min(gap(a, b) for a in fb for b in sw)
|
||||
who = min(((gap(a, b), a['ref']) for a in fb for b in sw))[1]
|
||||
note.append(u' FB(%s) 最近的一脚到 SW: **%.2f mm**(%s)' % (FB, g, who))
|
||||
if g < 2.0:
|
||||
bad.append(u'FB 离 SW 只有 %.2f mm < 2.0(§7.2 第三条)' % g)
|
||||
|
||||
# ── 4. Kelvin:U3 的 9/10 脚 → Rs1 的两端,不许交叉 ──
|
||||
rs = {p['pin']: p for p in sel(ps, 'Rs1')}
|
||||
u3 = {p['pin']: p for p in sel(ps, 'U3')}
|
||||
if '9' in u3 and '10' in u3 and '1' in rs and '2' in rs:
|
||||
# 9 脚接 VO_P 那端(Rs1.1),10 脚接 $1N165 那端(Rs1.2)
|
||||
assert u3['9']['net'] == rs['1']['net'], (u3['9']['net'], rs['1']['net'])
|
||||
assert u3['10']['net'] == rs['2']['net'], (u3['10']['net'], rs['2']['net'])
|
||||
a = (u3['9']['x'], u3['9']['y'])
|
||||
b = (rs['1']['x'], rs['1']['y'])
|
||||
c = (u3['10']['x'], u3['10']['y'])
|
||||
d = (rs['2']['x'], rs['2']['y'])
|
||||
la, lb = math.dist(a, b), math.dist(c, d)
|
||||
note.append(u' Kelvin: U3.9→Rs1.1 %.2f mm / U3.10→Rs1.2 %.2f mm,%s'
|
||||
% (la, lb, u'交叉' if cross(a, b, c, d) else u'不交叉'))
|
||||
if cross(a, b, c, d):
|
||||
bad.append(u'Kelvin 两条取样线交叉 —— 一交叉就得打过孔,§7.2 第四条明令不许')
|
||||
if max(la, lb) > 8.0:
|
||||
bad.append(u'Kelvin 取样线 %.2f mm 偏长' % max(la, lb))
|
||||
if abs(la - lb) > 2.0:
|
||||
bad.append(u'Kelvin 两条取样线长度差 %.2f mm(应对称)' % abs(la - lb))
|
||||
|
||||
# ── 5. 顺带:输出链的两处 4 A 接口 ──
|
||||
for (r1, n1, r2, n2, lim) in (('L1', 'VO', 'C6', 'VO', 3.0),
|
||||
('U2', '$1N165', 'Rs1', '$1N165', 4.0)):
|
||||
a, b = sel(ps, r1, n1), sel(ps, r2, n2)
|
||||
if a and b:
|
||||
g = min(gap(p, q) for p in a for q in b)
|
||||
note.append(u' 4 A 接口 %s(%s)→%s: 焊盘净距 **%.2f mm**' % (r1, n1, r2, g))
|
||||
if g > lim:
|
||||
bad.append(u'%s→%s 的 4 A 接口 %.2f mm > %.1f' % (r1, r2, g, lim))
|
||||
|
||||
print(u'§7.2 关键回路(摆位阶段判据)')
|
||||
for n in note:
|
||||
print(n)
|
||||
if bad:
|
||||
print(u'[X] %d 条不过:' % len(bad))
|
||||
for b in bad:
|
||||
print(u' ' + b)
|
||||
return 1
|
||||
print(u'[OK] 四条关键回路全部达标')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
在新工单中引用
屏蔽一个用户