初始导入: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
这个提交包含在:
+135
@@ -0,0 +1,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""从器件的 **3D Model Transform** 直接读出**本体框** —— 不用再截图量。
|
||||
|
||||
/c/Python314/python .claude/fp_body.py --scan KF2510 # 按关键词列候选
|
||||
/c/Python314/python .claude/fp_body.py --scan XT30 --3p # 只看 3 脚 2.54 的
|
||||
/c/Python314/python .claude/fp_body.py --check # 和 pcb1_place.FP_BODY 对账
|
||||
|
||||
## 为什么能这么读(2026-09-06 验证)
|
||||
|
||||
§26.6 写过「板边连接器的本体框,除了实测没有别的来源」——
|
||||
那一条是**错的**,只是当时没找到路子。`lib_Device.search()` 回来的
|
||||
`otherProperty['3D Model Transform']` 是一串逗号分隔的数:
|
||||
|
||||
v[0], v[1] = 3D 本体的 X / Y 尺寸(mil)
|
||||
v[6], v[7] = 本体中心相对**封装原点**的偏移(mil)
|
||||
|
||||
⇒ 本体框 = (ox − sx/2, oy − sy/2, ox + sx/2, oy + sy/2)。
|
||||
|
||||
**六个已经用放大截图实测过的封装全部对上**(误差 ≤ 0.31 mm,就是截图标定的精度):
|
||||
|
||||
| 封装 | 3D 算出的 y 向框 | 截图实测 |
|
||||
|---|---|---|
|
||||
| `CONN-TH_KF2510-3A-1`(弯针,§26.6 栽过一次)| −0.31 .. +11.70 | −0.20 .. +11.70 |
|
||||
| `CONN-TH_P5.08_KF128-5.08-2P` | −5.31 .. +5.38 | −5.20 .. +5.20 |
|
||||
| `USB-C_SMD-TYPE-C-31-M-12_1` | −5.05 .. +2.85 | −5.20 .. +3.12 |
|
||||
| `WIRELM-SMD_ESP32-S3-WROOM-1` | −9.10 .. +16.40 | −9.35 .. +16.75 |
|
||||
| `IND-SMD_L7.1-W6.5` | −3.25 .. +3.25 | −3.35 .. +3.35 |
|
||||
| `CAP-TH_BD8.0-P3.50-D0.6-FD` | −4.00 .. +4.00 | −4.10 .. +4.10 |
|
||||
|
||||
⚠️ 它给的是 **3D 模型**的框,不是丝印框,也不含公差。当成「本体大致占多少」够用
|
||||
(这正是摆位要的),要卡到 0.1 mm 的净距还是得看焊盘。**没有 3D 模型的器件读不出来**,
|
||||
那种还是只能截图。
|
||||
|
||||
## 顺带:立式 / 弯针一眼就分得出
|
||||
|
||||
同一个 3P 2.54 端子座:
|
||||
|
||||
* **立式(直针)**:本体 ≈ 7.6 × 6,偏移 ≈ 0 —— 本体骑在焊盘上
|
||||
* **弯针(90°)**:本体 ≈ 7.6 × 12,偏移 ≈ ±5.7 —— 本体整个甩到一边
|
||||
|
||||
⚠️ **名字完全不可靠**:`KF2510-3A`(C2940511) 名字像直针,3D 说它是弯针(+5.69);
|
||||
`KF2510-3AW`(C457341) 也是弯针,只是甩向 −Y。要立式的得看数,不能看后缀。
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
from eda import run # noqa: E402
|
||||
|
||||
MIL = 39.3700787401575
|
||||
|
||||
|
||||
def parse(tr):
|
||||
u"""'3D Model Transform' -> (本体 w, h, 框 x0,y0,x1,y1),读不出来返回 None。"""
|
||||
if not tr:
|
||||
return None
|
||||
try:
|
||||
v = [float(x) for x in str(tr).split(',')]
|
||||
except ValueError:
|
||||
return None
|
||||
if len(v) < 8:
|
||||
return None
|
||||
sx, sy = v[0] / MIL, v[1] / MIL
|
||||
ox, oy = v[6] / MIL, v[7] / MIL
|
||||
if sx <= 0 or sy <= 0:
|
||||
return None
|
||||
return (sx, sy, ox - sx / 2, oy - sy / 2, ox + sx / 2, oy + sy / 2)
|
||||
|
||||
|
||||
def search(kw, n=40):
|
||||
js = ("const r = await eda.lib_Device.search(%s, undefined, undefined, undefined, %d, 1);"
|
||||
"return (r||[]).map(d => [d.name||'', d.footprintName||'', d.supplierId||'',"
|
||||
" (d.otherProperty&&d.otherProperty['3D Model Transform'])||'', d.uuid, d.libraryUuid]);"
|
||||
% (json.dumps(kw), n))
|
||||
r = run(js, timeout=180)
|
||||
if not r.get('success'):
|
||||
sys.exit(u'搜索失败: ' + json.dumps(r, ensure_ascii=False)[:400])
|
||||
return r['result']
|
||||
|
||||
|
||||
def show(rows, only=None):
|
||||
print(u'%-30s %-32s %-11s %-15s %s'
|
||||
% (u'器件', u'封装', u'立创', u'本体 w x h', u'框 y0..y1 判断'))
|
||||
for name, fp, lcsc, tr, _u, _l in rows:
|
||||
if only and only.lower() not in (name + fp).lower():
|
||||
continue
|
||||
b = parse(tr)
|
||||
if b is None:
|
||||
print(u'%-30s %-32s %-11s —— 没有 3D 模型,读不出来' % (name[:30], fp[:32], lcsc))
|
||||
continue
|
||||
w, h, x0, y0, x1, y1 = b
|
||||
# 立式 = 本体基本骑在焊盘上;弯针 = 整个甩到一边
|
||||
kind = u'立式' if abs((y0 + y1) / 2) < h * 0.25 else u'弯针/偏置'
|
||||
print(u'%-30s %-32s %-11s %6.2f x %6.2f %+6.2f..%+6.2f %s'
|
||||
% (name[:30], fp[:32], lcsc, w, h, y0, y1, kind))
|
||||
|
||||
|
||||
def check():
|
||||
u"""拿 `pcb1_place.FP_BODY` 的手工实测值和 3D 模型对账。"""
|
||||
import pcb1_place as P
|
||||
hit = {}
|
||||
for fp in P.FP_BODY:
|
||||
kw = fp.split('_')[-1] or fp
|
||||
for row in search(kw, 40):
|
||||
if row[1] == fp and row[3]:
|
||||
hit[fp] = row
|
||||
break
|
||||
print(u'%-34s %-22s %-22s %s' % (u'封装', u'3D 框 y', u'FP_BODY 框 y', u''))
|
||||
bad = 0
|
||||
for fp, want in sorted(P.FP_BODY.items()):
|
||||
row = hit.get(fp)
|
||||
if not row:
|
||||
print(u'%-34s —— 没搜到带 3D 的器件,跳过' % fp[:34])
|
||||
continue
|
||||
b = parse(row[3])
|
||||
ok = abs(b[3] - want[1]) < 0.6 and abs(b[5] - want[3]) < 0.6
|
||||
bad += 0 if ok else 1
|
||||
print(u'%-34s %+7.2f..%+7.2f %+7.2f..%+7.2f %s'
|
||||
% (fp[:34], b[3], b[5], want[1], want[3], u'✔' if ok else u'✘ 对不上'))
|
||||
print(u'\n对不上的 %d 个' % bad)
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
argv = sys.argv[1:]
|
||||
if '--check' in argv:
|
||||
sys.exit(1 if check() else 0)
|
||||
if '--scan' in argv:
|
||||
kw = argv[argv.index('--scan') + 1]
|
||||
only = argv[argv.index('--only') + 1] if '--only' in argv else None
|
||||
show(search(kw, 40), only)
|
||||
else:
|
||||
sys.exit(__doc__)
|
||||
在新工单中引用
屏蔽一个用户