139 行
5.6 KiB
Python
139 行
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
u"""自建「小米8 侧键触点焊盘」封装(2026-09-06 用户改方案:**顶针取消,改成中框式焊盘**)。
|
|
|
|
装配方式:螺丝把主板压在载板上(1 mm 间隙),主板自己的侧键弹片直接压在载板焊盘上,
|
|
完全复刻中框的接触方式。载板这一侧只要一个裸铜圆盘。
|
|
|
|
/c/Python314/python .claude/fp_keypad.py # 只打印几何
|
|
/c/Python314/python .claude/fp_keypad.py --push # 建封装(已存在则重画)+ 读回校验
|
|
|
|
## 为什么是 Ø1.5
|
|
|
|
四个触点的**实测**最小间距是 **1.78 mm**(中框金焊盘量的,见 pcb2_place.POGO_XY):
|
|
|
|
净距 0.20 -> 最大 Ø1.58 · 净距 0.25 -> Ø1.53 · 净距 0.30 -> Ø1.48
|
|
|
|
中框原盘 Ø1.35。取 **Ø1.5**:净距 0.28(好做),且比中框略大一点换位置余量
|
|
(按弹片尖 Ø0.5 算,容许 0.50 mm 位置误差)。
|
|
|
|
⚠️ 旧的 `POGO-SMD1P_D1.9` 是按「最近一对隔 2.217 mm」缩出来的,而真实是 1.78 ——
|
|
Ø1.9 在真实间距下是**负净距**,正是 `pcb2_place.py` 那 20 条「重叠(L1)」的来源。
|
|
|
|
## 两条工艺要求(生成不了,得在下单时说)
|
|
|
|
1. **这是接触盘,不上锡膏** —— 钢网必须开口为 0,否则弹片压在锡珠上接触不良。
|
|
2. 表面处理走 **ENIG(沉金)**,弹片反复摩擦,HASL 会磨穿。
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
from eda import run
|
|
import pcb2_place as P
|
|
|
|
LIB_ME = '543725e8f0454017a5691e7e570aeef6' # 个人库
|
|
FP_NAME = 'PAD-D1.5_MI8-KEY'
|
|
MIL = 39.3700787401575
|
|
PAD_D = P.POGO_PAD_D # 1.5,单一事实来源在 pcb2_place
|
|
|
|
|
|
def mil(v):
|
|
return round(v * MIL, 4)
|
|
|
|
|
|
def call(js, timeout=120):
|
|
r = run(js, timeout=timeout)
|
|
if not r.get('success'):
|
|
raise RuntimeError(json.dumps(r, ensure_ascii=False)[:600])
|
|
return r['result']
|
|
|
|
|
|
def _find_once():
|
|
r = call("const r = await eda.lib_Footprint.search('%s', '%s', undefined, 20, 1);"
|
|
"return (r||[]).map(f => [f.uuid, f.name, f.libraryUuid]);" % (FP_NAME, LIB_ME))
|
|
for u, n, l in r:
|
|
if n == FP_NAME:
|
|
return u
|
|
return None
|
|
|
|
|
|
def find_fp(retries=6):
|
|
import time
|
|
for i in range(retries):
|
|
u = _find_once()
|
|
if u:
|
|
return u
|
|
time.sleep(2)
|
|
return None
|
|
|
|
|
|
def push():
|
|
fp = find_fp()
|
|
if not fp:
|
|
call("await eda.lib_Footprint.create('%s', '%s', undefined, "
|
|
"'小米8 侧键触点盘 Ø%.1f,单焊盘顶层裸铜。弹片直压,不上锡膏,ENIG。');return 1;"
|
|
% (LIB_ME, FP_NAME, PAD_D))
|
|
fp = find_fp()
|
|
assert fp, u'封装建了但搜不到'
|
|
print(u'封装已建: %s %s' % (FP_NAME, fp))
|
|
else:
|
|
print(u'封装已存在: %s %s,重画' % (FP_NAME, fp))
|
|
|
|
body = ["await eda.pcb_PrimitivePad.create(1,'1',0,0,0,['ELLIPSE',%s,%s],undefined,null);"
|
|
% (mil(PAD_D), mil(PAD_D))]
|
|
# 装配层画一圈,方便摆位时看得见;丝印不画(盘周围没地方,且丝印压不到接触面)
|
|
n = 24
|
|
import math
|
|
r0 = PAD_D / 2.0 + 0.25
|
|
for k in range(n):
|
|
a1, a2 = 2 * math.pi * k / n, 2 * math.pi * (k + 1) / n
|
|
body.append("await eda.pcb_PrimitiveLine.create('',9,%s,%s,%s,%s,4);"
|
|
% (mil(r0 * math.cos(a1)), mil(r0 * math.sin(a1)),
|
|
mil(r0 * math.cos(a2)), mil(r0 * math.sin(a2))))
|
|
r = call("await eda.lib_Footprint.openInEditor('%s','%s');"
|
|
"await new Promise(r=>setTimeout(r,2000));"
|
|
"const op = await eda.pcb_PrimitivePad.getAllPrimitiveId(); if (op && op.length) await eda.pcb_PrimitivePad.delete(op);"
|
|
"const ol = await eda.pcb_PrimitiveLine.getAllPrimitiveId(); if (ol && ol.length) await eda.pcb_PrimitiveLine.delete(ol);"
|
|
"%s"
|
|
"await eda.pcb_Document.save();"
|
|
"return {pads:(await eda.pcb_PrimitivePad.getAllPrimitiveId()).length,"
|
|
" lines:(await eda.pcb_PrimitiveLine.getAllPrimitiveId()).length};"
|
|
% (fp, LIB_ME, ''.join(body)))
|
|
print(u'已画: %s' % r)
|
|
|
|
v = call("await eda.lib_Footprint.openInEditor('%s','%s'); await new Promise(r=>setTimeout(r,1500));"
|
|
"return (await eda.pcb_PrimitivePad.getAll()).map(p=>({n:p.padNumber,x:p.x,y:p.y,"
|
|
"layer:p.layer,pad:JSON.stringify(p.pad),hole:JSON.stringify(p.hole)}));" % (fp, LIB_ME))
|
|
if len(v) != 1:
|
|
sys.exit(u'❌ 焊盘数 %d,应为 1' % len(v))
|
|
p0 = v[0]
|
|
shape = json.loads(p0['pad'])
|
|
d = round(float(shape[1]) / MIL, 3)
|
|
bad = []
|
|
if p0['layer'] != 1:
|
|
bad.append(u'层 %s 应为 1(顶层)' % p0['layer'])
|
|
if abs(d - PAD_D) > 0.01:
|
|
bad.append(u'直径 %.3f ≠ %.2f' % (d, PAD_D))
|
|
if abs(p0['x']) > 0.5 or abs(p0['y']) > 0.5:
|
|
bad.append(u'不在原点 (%s,%s)' % (p0['x'], p0['y']))
|
|
if json.loads(p0['hole']) not in (None, [], ['ROUND', 0]):
|
|
bad.append(u'不该有孔: %s' % p0['hole'])
|
|
if bad:
|
|
sys.exit(u'❌ ' + '; '.join(bad))
|
|
print(u'✅ 封装校验过:顶层单焊盘 Ø%.2f,在原点,无孔' % d)
|
|
return fp
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import itertools, math
|
|
dmin = min(math.hypot(a[0] - b[0], a[1] - b[1])
|
|
for a, b in itertools.combinations(P.POGO_XY, 2))
|
|
print(u'四触点实测最小间距 %.2f mm ⇒ Ø%.1f 盘净距 %.2f mm' % (dmin, PAD_D, dmin - PAD_D))
|
|
if '--push' in sys.argv:
|
|
push()
|
|
else:
|
|
print(u'(加 --push 才真的建)')
|