初始导入: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
这个提交包含在:
zhou yang
2026-09-06 18:18:14 +08:00
共同撰写人 Claude Opus 5
当前提交 2bfe308b30
共修改 526 个文件,包含 80633 行新增和 0 行删除
+111
查看文件
@@ -0,0 +1,111 @@
# -*- 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())