#!/usr/bin/env python3 # -*- coding: utf-8 -*- """生成 docs/images/ADC前端电路.svg —— UA 通道前端拓扑(端子 → R31 → ADC7 → TPAFE), 并对照 STM32 侧(UB)拓扑,回答"这个前端到底是不是分压器"(纯标准库)。 为什么不用 Mermaid:本图要读的是"ADC7 节点上到底挂了几个器件"——Mermaid 会自动加 有向边、按文本自适应盒宽,恰好把"有没有对地分流电阻"这个唯一要看的东西抹掉。 用法(仓库根目录,任意 python3): python tools/arch_diagram/gen_adc_frontend_svg.py 输出: docs/images/ADC前端电路.svg 改内容:本图是固定电路拓扑(依据 docs/硬件平台/V0.1.tel 第 151 / 241 / 245 / 248 行), 网表变了才重画;调坐标 / 文案编辑下方常量后重跑。 """ import html import os import xml.etree.ElementTree as ET ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) OUT = os.path.join(ROOT, "docs", "images", "ADC前端电路.svg") # ----------------------------- 画布与尺寸 ----------------------------- W = 980 H = 776 PAD = 60 RIGHT = 940 FONT = "Microsoft YaHei, Segoe UI, PingFang SC, sans-serif" TITLE_FONT = 20 SUB_FONT = 13 HEAD_FONT = 14 BOX_FONT = 14 PIN_FONT = 13 NOTE_FONT = 12 TINY_FONT = 11 INK = "#0f172a" LINE = "#334155" MUTED = "#64748b" FAINT = "#94a3b8" RULE = "#cbd5e1" WHITE = "#ffffff" C_BLUE, BG_BLUE = "#2563eb", "#eff6ff" C_TEAL, BG_TEAL = "#0d9488", "#f0fdfa" C_GRAY, BG_GRAY = "#475569", "#f1f5f9" C_AMBER, BG_AMBER = "#d97706", "#fffbeb" # ----------------------------- 数据(改这里) ----------------------------- TITLE = "ADC 前端注入通路 —— 本板不是分压器" SUB1 = ("网表 V0.1.tel 第 241 行:ADC7 ; C44.2 R31.2 U3.61 —— 该节点只有三个连接," "没有任何对地元件") SUB2 = ("所以端子侧与引脚侧斜率相同(TPAFE 6553.6 码/V、STM32 1241.2 码/V)," "注入点优先选端子侧焊盘") HEAD_A = "A · UA 通道(TPAFE5160 侧)" HEAD_B = "B · STM32 侧(以 UB 为例)—— 更简单,连接地电阻都没有" NOTE_A1 = "R31 的 1 脚在端子侧、2 脚在芯片侧 —— 现场找焊盘按此定位" NOTE_A2 = "R31 + C44 构成差分 RC 低通;C44 另一端是 TPAFE 负输入脚,不是地" NOTE_TAG = "无对地分流电阻" # ----------------------------- 工具函数 ----------------------------- def esc(s): return html.escape(s, quote=True) def text_w(s, fs): """粗略估算文本像素宽:CJK/全角 ≈ 1.0em,其余 ≈ 0.58em(宁可偏大)。""" return sum(fs * (1.0 if ord(ch) >= 0x2E80 else 0.58) for ch in s) OVERFLOW = [] def check_fit(s, fs, avail, where): if text_w(s, fs) > avail: OVERFLOW.append("%s: %.0f > %.0f [%s]" % (where, text_w(s, fs), avail, s)) def txt(x, y, s, fs, fill, weight=400, anchor=None): a = "" if anchor is None else ' text-anchor="%s"' % anchor w = "" if weight == 400 else ' font-weight="%d"' % weight return ('%s' % (x, y, fs, a, w, fill, esc(s))) def line(x1, y1, x2, y2, color=LINE, width=2.0, dash=None): d = "" if dash is None else ' stroke-dasharray="%s"' % dash return ('' % (x1, y1, x2, y2, color, width, d)) def rect(x, y, w, h, fill, stroke=None, rx=10, sw=1.6): s = "" if stroke is None else ' stroke="%s" stroke-width="%g"' % (stroke, sw) return '' % ( x, y, w, h, rx, fill, s) def dot(x, y, r=5.5): return '' % (x, y, r, INK) def res(x, cy, w, h, label): """电阻:白底灰框 + 上方标号。x = 左边界,cy = 中心线。""" out = [rect(x, cy - h / 2.0, w, h, WHITE, LINE, rx=3, sw=1.6)] out.append(txt(x + w / 2.0, cy - h / 2.0 - 14, label, NOTE_FONT, MUTED, anchor="middle")) return out def cap(x, cy, half=20, gap=16): """电容:两条平行板(信号线在 x 上竖直走)。""" return [line(x - half, cy, x + half, cy, INK, 2.4), line(x - half, cy + gap, x + half, cy + gap, INK, 2.4)] def gnd(x, y, label_y, label="AGND"): """接地符号:y = 接线点。""" out = [line(x, y, x, y + 16, LINE, 2.0)] for i, half in enumerate((28, 20, 12)): out.append(line(x - half, y + 16 + i * 8, x + half, y + 16 + i * 8, LINE, 2.0)) out.append(txt(x, label_y, label, NOTE_FONT, MUTED, anchor="middle")) return out def panel_head(y, s): return [txt(PAD, y, s, HEAD_FONT, INK, weight=700), line(PAD, y + 12, RIGHT, y + 12, RULE, 1.0)] # ----------------------------- 生成 ----------------------------- def build(): out = [] add = out.append # ---- 文字溢出预检 ---- avail = RIGHT - PAD for s, fs, where in ((TITLE, TITLE_FONT, "title"), (SUB1, SUB_FONT, "sub1"), (SUB2, SUB_FONT, "sub2"), (HEAD_A, HEAD_FONT, "head A"), (HEAD_B, HEAD_FONT, "head B"), (NOTE_A1, NOTE_FONT, "note A1"), (NOTE_A2, NOTE_FONT, "note A2")): check_fit(s, fs, avail, where) check_fit(NOTE_TAG, NOTE_FONT, 110, "note tag") if OVERFLOW: for o in OVERFLOW: print("OVERFLOW WARN " + o) add(rect(0, 0, W, H, WHITE, rx=0)) # ---- 标题区 ---- add(txt(PAD, 44, TITLE, TITLE_FONT, INK, weight=700)) add(txt(PAD, 72, SUB1, SUB_FONT, MUTED)) add(txt(PAD, 92, SUB2, SUB_FONT, MUTED)) # ================= 面板 A:UA 通道 ================= for e in panel_head(138, HEAD_A): add(e) AY = 250 # 主线(正端信号) GY = 390 # 负端参考线 # 端子 add(rect(PAD, AY - 36, 130, 72, BG_BLUE, C_BLUE)) add(txt(PAD + 65, AY - 8, "端子侧", BOX_FONT, C_BLUE, weight=700, anchor="middle")) add(txt(PAD + 65, AY + 14, "U5.A3 · AIN1", NOTE_FONT, C_BLUE, anchor="middle")) # 端子 → R31 → ADC7 add(line(PAD + 130, AY, 250, AY)) for e in res(250, AY, 90, 20, "R31 · 1 kΩ"): add(e) add(txt(257, AY + 28, "1", TINY_FONT, FAINT)) add(txt(333, AY + 28, "2", TINY_FONT, FAINT, anchor="end")) add(line(340, AY, 600, AY)) # ADC7 节点 add(dot(600, AY)) add(txt(600, AY - 36, "ADC7", BOX_FONT, INK, weight=700, anchor="middle")) add(txt(600, AY - 18, NOTE_TAG, NOTE_FONT, C_AMBER, weight=700, anchor="middle")) add(line(600, AY, 720, AY)) # C44:ADC7 → ADC7_GND add(line(600, AY, 600, 290)) for e in cap(600, 290): add(e) add(txt(630, 300, "C44", NOTE_FONT, MUTED)) add(line(600, 306, 600, GY)) # ADC7_GND 节点 add(dot(600, GY)) add(txt(600, GY + 28, "ADC7_GND", PIN_FONT, INK, weight=700, anchor="middle")) add(txt(600, GY + 46, "(差分负端)", TINY_FONT, MUTED, anchor="middle")) add(line(600, GY, 720, GY)) # ADC7_GND → R32 → AGND add(line(600, GY, 520, GY)) for e in res(430, GY, 90, 20, "R32 · 1 kΩ"): add(e) add(line(430, GY, 380, GY)) for e in gnd(380, GY, GY + 50): add(e) # TPAFE 芯片 add(rect(720, 196, 200, 244, BG_TEAL, C_TEAL, rx=12)) add(txt(820, 222, "TPAFE5160 · U3", BOX_FONT, C_TEAL, weight=700, anchor="middle")) add(txt(820, 316, "±5 V 满量程 · 152.59 µV/LSB", NOTE_FONT, FAINT, anchor="middle")) add(txt(736, AY, "61", TINY_FONT, FAINT)) add(txt(762, AY, "AIN_7P", PIN_FONT, INK, weight=700)) add(txt(736, GY, "62", TINY_FONT, FAINT)) add(txt(762, GY, "AIN_7GND", PIN_FONT, INK, weight=700)) # 面板 A 的两条注记(写在左侧空白区) add(txt(PAD, 306, NOTE_A1, NOTE_FONT, MUTED)) add(txt(PAD, 330, NOTE_A2, NOTE_FONT, MUTED)) # ================= 面板 B:STM32 侧 ================= for e in panel_head(500, HEAD_B): add(e) BY = 590 add(rect(PAD, BY - 36, 130, 72, BG_BLUE, C_BLUE)) add(txt(PAD + 65, BY - 8, "端子侧", BOX_FONT, C_BLUE, weight=700, anchor="middle")) add(txt(PAD + 65, BY + 14, "U5.B7 · AIN2", NOTE_FONT, C_BLUE, anchor="middle")) add(line(PAD + 130, BY, 250, BY)) for e in res(250, BY, 90, 20, "R52 · 1 kΩ"): add(e) add(line(340, BY, 720, BY)) add(dot(720, BY)) add(txt(720, BY - 28, "ADC17", BOX_FONT, INK, weight=700, anchor="middle")) add(line(720, BY, 800, BY)) # C111:ADC17 → AGND(直落,无串阻) add(line(720, BY, 720, 650)) for e in cap(720, 650): add(e) add(txt(750, 660, "C111", NOTE_FONT, MUTED)) add(line(720, 666, 720, 690)) for e in gnd(720, 690, 742): add(e) # STM32 芯片 add(rect(800, 530, 140, 120, BG_GRAY, C_GRAY, rx=12)) add(txt(870, 552, "STM32 ADC1", PIN_FONT, C_GRAY, weight=700, anchor="middle")) add(txt(870, 622, "0~3.3 V 单极性", TINY_FONT, FAINT, anchor="middle")) add(txt(816, BY, "44", TINY_FONT, FAINT)) add(txt(840, BY, "PC4", PIN_FONT, INK, weight=700)) svg = ('\n%s\n\n' % (W, H, W, H, FONT, "\n".join(out))) d = os.path.dirname(OUT) if not os.path.isdir(d): os.makedirs(d) with open(OUT, "w", encoding="utf-8", newline="\n") as fp: fp.write(svg) ET.fromstring(svg) print("OK %d bytes, canvas %dx%d -> %s" % (len(svg.encode("utf-8")), W, H, OUT)) if OVERFLOW: print("!! %d 处文字可能溢出,见上方 WARN" % len(OVERFLOW)) if __name__ == "__main__": build()