Files
STM32F4-Base/tools/arch_diagram/gen_adc_frontend_svg.py
T
wandering1 9163a92d3b docs(adc): 前端拓扑改 SVG,③ 段定死 1:1(本板不是分压器)
ASCII 拓扑图把 C44 / R32 画成挂在 ADC7 下的一根竖线,读起来像 2:1 分压,
恰是文档要否掉的结论。改为两个节点分开画的 SVG,并加 STM32 侧对照面板。

- 新增 tools/arch_diagram/gen_adc_frontend_svg.py →
  docs/images/ADC前端电路.svg(纯标准库 + 文字溢出预检 + 幂等重跑)
- docs/测试说明/ADC前端注入验证流程.md §2、docs/硬件平台/
  ADC通道映射说明.md §7.1 的 ASCII 图换成该 SVG
- ③ 段(AINn ↔ 转换器引脚)定死变比 1:1:ADCn 节点只有
  {串阻.2, 滤波 C, 转换器引脚},无对地元件;"1k 到地"接在差分负端
- 305.18 / 1611.3 µV/码(读法 A)作废;数据可信.md §2.1 表改为
  引脚侧系数 152.59 / 805.66,卡 0 判据从"判别两种读法"降为
  "确认转换器 LSB 与逐通道映射"
- 顺带修 ADC通道映射说明.md §8 步骤 1 的 §5.1 自引用误判
  (文档名以 V0.0 开头被前缀规则误认,改写成全路径形式)

门禁:Keil 无关(纯文档 + 生成器);check_ids --strict、
check_doc_links --all --sections-strict、pre-commit 全过。
2026-10-27 17:08:56 +08:00

270 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 ('<text x="%s" y="%s" font-size="%g"%s%s fill="%s">%s</text>'
% (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 ('<line x1="%s" y1="%s" x2="%s" y2="%s" stroke="%s" stroke-width="%g"%s/>'
% (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 '<rect x="%s" y="%s" width="%s" height="%s" rx="%s" fill="%s"%s/>' % (
x, y, w, h, rx, fill, s)
def dot(x, y, r=5.5):
return '<circle cx="%s" cy="%s" r="%g" fill="%s"/>' % (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 = ('<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="%d" '
'viewBox="0 0 %d %d" font-family="%s">\n%s\n</svg>\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()