文件
phonerail/.claude/r2/pinmap_base.py
T
zhou yang和Claude Opus 5 efcba30a0c 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
2026-09-10 21:02:10 +08:00

118 行
4.7 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""底板 ESP32(`U20`)的 EFUSE_EN / PRSNT# 引脚**重新分配**(M9,2026-09-09 用户提出)。
/c/Python314/python .claude/r2/pinmap_base.py # 只打计划 + 读回现状
/c/Python314/python .claude/r2/pinmap_base.py --push # 真改(删端口 → 重建)
## 为什么要换
槽阵转 90° 之后,五个槽沿 y 从南到北是 **1→5**,而 `U20` 上这两组脚的顺序**正好是反的**:
U20 东沿(南→北):EFUSE_EN5 EN4 EN3 EN2 EN1
U20 南沿(西→东):PRSNT4# PRSNT3# PRSNT2# PRSNT1#
五根线因此互相全交叉,两层板上每个交叉都要打过孔换层 —— 而**这不是布线器的问题,
是引脚分配的问题**,布线器只会报「出不来 / 没布通」。
## 为什么可以随便换(组内互换,零电气风险)
`EFUSE_EN*` 是普通 GPIO 输出、`PRSNT*#` 是普通 GPIO 输入,全部经 ESP32-S3 的
GPIO matrix 出去,不挑脚。**组内互换**不碰 strapping(IO0/45/46)、不碰 ADC1
(NTC 要的 IO1~IO10)、不碰 USB PHY(IO19/20)—— 换的只是固件里的编号。
⚠️⚠️ **`setState_Net` 对 netport 是坏的**(R1 `pinmap.py` 2026-09-06 实测):
调用之后网名变空字符串、标签也没了,而**几何验证抓不到**。⇒ 只能删掉重建。
"""
import io
import json
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(HERE))
sys.path.insert(0, HERE)
from eda import run # noqa: E402
PAGE = '38327f806bc13133' # A3-CTRL(r2-uuids.json)
# 只动 `U20` 那一侧的端口:它们在 x=340 / 470;上拉那一侧在 x=145,不许动。
U20_SIDE_X = (340, 470)
SWAP = {
'EFUSE_EN1': 'EFUSE_EN5', 'EFUSE_EN5': 'EFUSE_EN1',
'EFUSE_EN2': 'EFUSE_EN4', 'EFUSE_EN4': 'EFUSE_EN2',
'PRSNT1#': 'PRSNT4#', 'PRSNT4#': 'PRSNT1#',
'PRSNT2#': 'PRSNT3#', 'PRSNT3#': 'PRSNT2#',
}
JS_READ = u"""
await eda.dmt_EditorControl.openDocument('%s');
await new Promise(r => setTimeout(r, 1500));
const out = [];
for (const c of await eda.sch_PrimitiveComponent.getAll()) {
if (c.getState_ComponentType() !== 'netport') continue;
let comp = ''; try { comp = (c.getState_Component() || {}).name || ''; } catch (e) {}
out.push([c.getState_PrimitiveId(), String(c.getState_Net() || ''),
c.getState_X(), c.getState_Y(), c.getState_Rotation
? c.getState_Rotation() : (c.rotation || 0), comp]);
}
return out;
"""
JS_APPLY = u"""
await eda.dmt_EditorControl.openDocument('%s');
await new Promise(r => setTimeout(r, 1500));
const DEL = %s, MK = %s;
if (DEL.length) await eda.sch_PrimitiveComponent.delete(DEL);
for (const p of MK) await eda.sch_PrimitiveComponent.createNetPort(p[0], p[1], p[2], p[3], p[4], false);
await eda.sch_Document.save();
await new Promise(r => setTimeout(r, 1000));
return {del: DEL.length, mk: MK.length};
"""
def read():
r = run(JS_READ % PAGE, timeout=180)
assert r.get('success'), json.dumps(r, ensure_ascii=False)[:400]
return r['result']
def plan(ports):
out = []
for pid, net, x, y, rot, comp in ports:
if net not in SWAP:
continue
if round(x) not in U20_SIDE_X:
continue # 上拉那一侧不动
out.append((pid, net, SWAP[net], x, y, rot, comp))
return out
def main():
ports = read()
pl = plan(ports)
print(u'A3-CTRL 上端口 %d 个;要改 %d 个(只动 U20 侧 x=%s)'
% (len(ports), len(pl), '/'.join(str(v) for v in U20_SIDE_X)))
for pid, old, new, x, y, rot, comp in sorted(pl, key=lambda t: t[1]):
print(u' (%4d,%4d) %-11s → %-11s [%s]' % (x, y, old, new, comp or '?'))
if '--push' not in sys.argv:
print(u'(没加 --push,没有下发)')
return
dele = [t[0] for t in pl]
mk = [['BI' if (t[6] or '').endswith('BI') else ('IN' if (t[6] or '').endswith('IN') else 'OUT'),
t[2], t[3], t[4], t[5]] for t in pl]
r = run(JS_APPLY % (PAGE, json.dumps(dele), json.dumps(mk, ensure_ascii=False)), timeout=600)
assert r.get('success'), json.dumps(r, ensure_ascii=False)[:500]
print(u' 删端口 %d / 重建 %d' % (r['result']['del'], r['result']['mk']))
# 读回校验
now = {(round(x), round(y)): net for _p, net, x, y, _r, _c in read()}
bad = []
for _pid, old, new, x, y, _rot, _c in pl:
got = now.get((round(x), round(y)))
if got != new:
bad.append(u'(%d,%d) 应为 %s,读回 %s' % (x, y, new, got))
print(u'[%s] 读回校验%s' % ('OK' if not bad else 'X',
u'逐个对上' if not bad else u' %d 条不过: %s' % (len(bad), bad[:4])))
if __name__ == '__main__':
main()