9163a92d3b
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 全过。
270 lines
9.5 KiB
Python
270 lines
9.5 KiB
Python
#!/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()
|