"""工程符号库:从参考设计的 .kicad_sch 抽符号,或按引脚表程序化生成方框符号。 from schgen.symgen import grab_from_sch, box_symbol, write_lib blocks = [grab_from_sch(r"...\\SMOD\\07_PWR.kicad_sch", "IC_SCH:root_0_TPS7A92_IC_SCH.SchLib", "TPS7A9201DSK"), box_symbol("TPS65235", ref="U", value="TPS65235", left=[("2", "VIN", "power_in"), ...], right=[...], top=[...], bottom=[...])] write_lib(r"...\\lib\\myboard.kicad_sym", blocks) 先查参考设计有没有现成符号(抄过来最省事、引脚最不容易错),没有才自己生成。 """ import io import re from .kicad_lib import balanced PIN_TYPES = {"input", "output", "bidirectional", "tri_state", "passive", "free", "unspecified", "power_in", "power_out", "open_collector", "open_emitter", "no_connect"} def rename(block, old, new): """把顶层符号名和子单元名 old_x_y 一并改掉。""" block = block.replace('(symbol "%s"' % old, '(symbol "%s"' % new, 1) for o in (old.split(':')[-1], old): block = re.sub(r'\(symbol "%s(_\d+_\d+)"' % re.escape(o), lambda m: '(symbol "%s%s"' % (new, m.group(1)), block) return block def grab_from_sch(sch_path, lib_id, new_name): """从 .kicad_sch 的 lib_symbols 段里按 lib_id 抽出符号并改名。""" t = io.open(sch_path, encoding="utf-8").read() i = t.find('(symbol "%s"' % lib_id) if i < 0: raise KeyError("%s 里没有 %s" % (sch_path, lib_id)) return rename(balanced(t, i), lib_id, new_name) def pin_count(block): return len(re.findall(r'\(pin\s+\w+\s+\w+', block)) def box_symbol(name, ref="U", value=None, desc="", left=(), right=(), top=(), bottom=(), pitch=2.54, pin_len=5.08, min_w=15.24, min_h=10.16, hidden_num=False, footprint=""): """方框符号。每边是 [(号, 名[, 类型]), ...],按顺序从上到下 / 从左到右排开; 号为 None 表示那一格留空(用来把引脚分组)。宽高按引脚名长度与数量自动撑开。""" value = value or name def norm(side): out = [] for p in side: if p is None: out.append(None) continue num, nm = p[0], p[1] typ = p[2] if len(p) > 2 else "passive" assert typ in PIN_TYPES, typ out.append((num, nm, typ)) return out L, Rr, T, B = norm(left), norm(right), norm(top), norm(bottom) # pitch 可以是一个数,也可以按边分别给:{"left":7.62,"right":5.08,"top":12.7,"bottom":12.7} pd = pitch if isinstance(pitch, dict) else {} pl, pr_, pt, pb = (pd.get(k, 2.54) if pd else pitch for k in ("left", "right", "top", "bottom")) namew = lambda side: max([len(p[1]) for p in side if p] or [0]) * 1.1 nh = max(len(T), len(B), 0) h = max(min_h, (len(L) + 1) * pl, (len(Rr) + 1) * pr_) w = max(min_w, namew(L) + namew(Rr) + 5.08, (len(T) + 1) * pt, (len(B) + 1) * pb, (max([len(p[1]) for p in T + B if p] or [0]) * 1.1 + 2.54) * 2 if nh else 0) w = round(w / 2.54 / 2 + 0.49) * 2.54 * 2 # 宽高取 2.54 的偶数倍,引脚落格点 h = round(h / 2.54 / 2 + 0.49) * 2.54 * 2 hw, hh = w / 2.0, h / 2.0 lines = [' (symbol "%s"' % name, ' (pin_names (offset 0.762))%s' % (' (pin_numbers hide)' if hidden_num else ''), ' (exclude_from_sim no) (in_bom yes) (on_board yes)', ' (property "Reference" "%s" (at %.2f %.2f 0)' ' (effects (font (size 1.27 1.27)) (justify left)))' % (ref, -hw, hh + 1.27), ' (property "Value" "%s" (at %.2f %.2f 0)' ' (effects (font (size 1.27 1.27)) (justify left)))' % (value, -hw, -hh - 1.27), ' (property "Footprint" "%s" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))' % footprint, ' (property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))', ' (property "Description" "%s" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))' % desc.replace('"', "'"), ' (symbol "%s_0_1"' % name, ' (rectangle (start %.2f %.2f) (end %.2f %.2f)' ' (stroke (width 0.254) (type default)) (fill (type background)))' % (-hw, hh, hw, -hh), ' )', ' (symbol "%s_1_1"' % name] def pin(num, nm, typ, x, y, ang): lines.append(' (pin %s line (at %.2f %.2f %d) (length %.2f)' % (typ, x, y, ang, pin_len)) lines.append(' (name "%s" (effects (font (size 1.27 1.27))))' % nm) lines.append(' (number "%s" (effects (font (size 1.27 1.27)))))' % num) def col(n, pitch): # 竖边:从上往下,居中 top_y = (n - 1) * pitch / 2.0 return [round((top_y - k * pitch) / 1.27) * 1.27 for k in range(n)] def row(n, pitch): x0 = -(n - 1) * pitch / 2.0 return [round((x0 + k * pitch) / 1.27) * 1.27 for k in range(n)] for p, y in zip(L, col(len(L), pl)): if p: pin(p[0], p[1], p[2], -hw - pin_len, y, 0) for p, y in zip(Rr, col(len(Rr), pr_)): if p: pin(p[0], p[1], p[2], hw + pin_len, y, 180) for p, x in zip(T, row(len(T), pt)): if p: pin(p[0], p[1], p[2], x, hh + pin_len, 270) for p, x in zip(B, row(len(B), pb)): if p: pin(p[0], p[1], p[2], x, -hh - pin_len, 90) lines += [' )', ' )'] return "\n".join(lines) def write_lib(path, blocks): out = ['(kicad_symbol_lib (version 20231120) (generator "kicad_flow_schgen") (generator_version "8.0")'] out += blocks out.append(')') io.open(path, "w", encoding="utf-8").write("\n".join(out) + "\n") names = [re.match(r'\s*\(symbol "([^"]+)"', b).group(1) for b in blocks] for b, n in zip(blocks, names): print(" 符号 %-24s 引脚 %3d" % (n, pin_count(b))) return path