初始导入:PhoneRail 五路手机直供电源板
原理图/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
这个提交包含在:
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""Inner2 上**没有落脚点**的那些 GND 底填碎片 -> 补缝合孔(接到 L2 的完整地平面)。
|
||||
|
||||
/c/Python314/python .claude/pcb1_gnd_stitch.py # 只找位置,不动板
|
||||
/c/Python314/python .claude/pcb1_gnd_stitch.py --push # 校核过了就下发
|
||||
|
||||
## 这块浮铜是怎么来的(2026-09-06 查清)
|
||||
|
||||
板子南沿 y 0.5~9 那一整条 GND 底填(**1312.8 mm²**)在 Inner2 上是孤立的:
|
||||
南边是板框,北边被 `12V-SW+BR`(y9~34, x0.5~72.6)、`3V3-SE`(y9~41, x73~115)、
|
||||
`12V-SE+RC`(y9~34, x116~159.5) 三块电源面封死,原本只剩两条缝通到别处 ——
|
||||
x 72.6~73(**0.4 mm**,本来就走不了)与 x 115~116(1.0 mm)。
|
||||
后者被 `K5_VUP(114.80,28.70)` / `VDAC5(116.70,29.50)` / `EFUSE_EN3(115.00,32.40)`
|
||||
三个**信号布线打的过孔**的反焊盘掐断了。而这一条里**一个过孔、一个通孔焊盘都没有**,
|
||||
于是整条铜浮着。
|
||||
|
||||
⚠️ 三道检查全都看不见它:
|
||||
· 铺铜的孤岛判定按**铺铜对象**算,`GND-FILL-L3` 是一个对象铺出十块,
|
||||
单独某一块有没有落脚点它不管;
|
||||
· 连接性 DRC 只看网络连没连上(GND 靠别处照样连通);
|
||||
· `pcb1_pour.py` 下发前的 `report()` 也看不见 —— 它的栅格**不含别的铺铜**,
|
||||
算出来 GND 是一整块。**只有下发后 `verify()` 回读实测铜才量得出来。**
|
||||
|
||||
而这条浮铜正贴着 ESP32 的天线禁铺区(x 78~98, y 0.8~8.9),160 mm 长的悬空铜条
|
||||
在 2.4 GHz 附近不是个能忽略的东西。
|
||||
|
||||
## 做法
|
||||
|
||||
每块浮铜里挑几个「三层都让得开、钻孔间距也够」的点打 GND 过孔。
|
||||
过孔是通孔 ⇒ 一个孔就把这块 Inner2 的铜接到 **Inner1 的完整地平面**上。
|
||||
落点判据直接借 `pcb1_i2c_l3.Field.via_ok()`(外层真净距 + L3 真铜 + 不分网络的钻孔间距)。
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
|
||||
import numpy as np # noqa: E402
|
||||
|
||||
import pcb1_i2c_l3 as M # noqa: E402
|
||||
import pcb1_netlist as NL # noqa: E402
|
||||
import pcb1_route_sig as RS # noqa: E402
|
||||
|
||||
#: 想在哪些地方缝。每条是 (说明, x0, y0, x1, y1, 要几个孔)。
|
||||
#: 南沿那条避开 ESP32 天线禁铺区 x 78~98(`pcb_fanout.ANT`,全层不许有铜)。
|
||||
ZONES = [
|
||||
(u'南沿西段', 3.0, 2.0, 74.0, 8.0, 3),
|
||||
(u'南沿东段', 101.0, 2.0, 157.0, 8.0, 3),
|
||||
]
|
||||
|
||||
MINGAP = 8.0 # 同一次补的孔彼此至少隔这么远,别扎堆
|
||||
|
||||
|
||||
def pick(zone, f, used):
|
||||
u"""在一个矩形里挑 n 个合法孔位,尽量摊开。"""
|
||||
name, x0, y0, x1, y1, n = zone
|
||||
ok = f.via_ok()
|
||||
out = []
|
||||
for k in range(n):
|
||||
# 目标位置:把区间等分,然后在目标点附近找最近的合法格
|
||||
tx = x0 + (x1 - x0) * (k + 0.5) / n
|
||||
ty = (y0 + y1) / 2.0
|
||||
best = None
|
||||
i0, j0 = f._idx(x0, y0)
|
||||
i1, j1 = f._idx(x1, y1)
|
||||
for j in range(max(0, j0), min(f.ny, j1 + 1)):
|
||||
for i in range(max(0, i0), min(f.nx, i1 + 1)):
|
||||
if not ok[j, i]:
|
||||
continue
|
||||
x, y = f.x0 + i * f.g, f.y0 + j * f.g
|
||||
if any(math.hypot(x - ux, y - uy) < MINGAP for ux, uy in used + out):
|
||||
continue
|
||||
d = math.hypot(x - tx, y - ty)
|
||||
if best is None or d < best[0]:
|
||||
best = (d, round(x, 3), round(y, 3))
|
||||
if best is None:
|
||||
print(u' ⚠️ %s 第 %d 个孔位没找到(净距或钻孔间距都不够)' % (name, k + 1))
|
||||
continue
|
||||
out.append((best[1], best[2]))
|
||||
return out
|
||||
|
||||
|
||||
def main():
|
||||
push = '--push' in sys.argv
|
||||
pads, vias, lines = NL.geom(push or '--refresh' in sys.argv)
|
||||
print(u'板上:过孔 %d / 走线 %d' % (len(vias), len(lines)))
|
||||
got, used = [], []
|
||||
for z in ZONES:
|
||||
f = M.Field((z[1] - 2.0, z[2] - 2.0, z[3] + 2.0, z[4] + 2.0),
|
||||
'GND', pads, vias, lines, [])
|
||||
pts = pick(z, f, used)
|
||||
used += pts
|
||||
got += pts
|
||||
print(u' %s:%s' % (z[0], ' '.join('(%.2f,%.2f)' % p for p in pts) or u'无'))
|
||||
if not got:
|
||||
sys.exit(u'一个孔位都没挑出来')
|
||||
|
||||
# 校核:借 pcb1_i2c_l3.verify 的孔判据(路径写成"原地换层",只出一个孔、没有线段)
|
||||
routes = [('GND', [(x, y, M.L3, M.WIRE), (x, y, M.L_TOP, M.WIRE),
|
||||
(x, y, M.L_TOP, M.WIRE)]) for x, y in got]
|
||||
bad = M.verify(routes, pads, vias, lines)
|
||||
for b in bad[:20]:
|
||||
print(u' ✗ ' + b)
|
||||
print(u' 校核:%s' % (u'✅ 全过(%d 个孔)' % len(got) if not bad
|
||||
else u'❌ %d 条' % len(bad)))
|
||||
if bad:
|
||||
sys.exit(1)
|
||||
if not push:
|
||||
print(u'\n(只算不下发。要下发加 --push)')
|
||||
return
|
||||
RS.ROUTES = os.path.join(HERE, '_pcb1_gnd_stitch.json')
|
||||
RS.push({('GND%s%03d' % (chr(0), i)): p for i, (_n, p) in enumerate(routes)})
|
||||
print(u'⚠️ 新过孔穿过内层平面 —— 现在必须重铺一次:'
|
||||
u'/c/Python314/python .claude/pcb1_pour.py --push --refresh')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
在新工单中引用
屏蔽一个用户