diff --git a/docs/images/ADC前端电路.svg b/docs/images/ADC前端电路.svg new file mode 100644 index 00000000..43b728da --- /dev/null +++ b/docs/images/ADC前端电路.svg @@ -0,0 +1,75 @@ + + +ADC 前端注入通路 —— 本板不是分压器 +网表 V0.1.tel 第 241 行:ADC7 ; C44.2 R31.2 U3.61 —— 该节点只有三个连接,没有任何对地元件 +所以端子侧与引脚侧斜率相同(TPAFE 6553.6 码/V、STM32 1241.2 码/V),注入点优先选端子侧焊盘 +A · UA 通道(TPAFE5160 侧) + + +端子侧 +U5.A3 · AIN1 + + +R31 · 1 kΩ +1 +2 + + +ADC7 +无对地分流电阻 + + + + +C44 + + +ADC7_GND +(差分负端) + + + +R32 · 1 kΩ + + + + + +AGND + +TPAFE5160 · U3 +±5 V 满量程 · 152.59 µV/LSB +61 +AIN_7P +62 +AIN_7GND +R31 的 1 脚在端子侧、2 脚在芯片侧 —— 现场找焊盘按此定位 +R31 + C44 构成差分 RC 低通;C44 另一端是 TPAFE 负输入脚,不是地 +B · STM32 侧(以 UB 为例)—— 更简单,连接地电阻都没有 + + +端子侧 +U5.B7 · AIN2 + + +R52 · 1 kΩ + + +ADC17 + + + + +C111 + + + + + +AGND + +STM32 ADC1 +0~3.3 V 单极性 +44 +PC4 + diff --git a/docs/任务规划/数据可信.md b/docs/任务规划/数据可信.md index f65dd93d..f76302ac 100644 --- a/docs/任务规划/数据可信.md +++ b/docs/任务规划/数据可信.md @@ -25,26 +25,30 @@ ### 2.1 通道与量纲 -| 语义通道 | 转换器 | 码值格式 | LSB | 前端 | 板级系数(输入侧) | 交流可用性 | +| 语义通道 | 转换器 | 码值格式 | LSB(@ 引脚) | 前端 | 板级系数(输入侧 = 引脚侧) | 交流可用性 | |---|---|---|---|---|---|---| -| UA | TPAFE5160 | 16 位双极性 ±5 V | 152.59 µV | 1k 串 + 1k 到地(2:1) | **305.18 µV/码** | ✅ 可用 | -| UN | TPAFE5160 | 同上 | 152.59 µV | 同上 | **305.18 µV/码** | ✅ 可用 | -| IA | TPAFE5160 | 同上 | 152.59 µV | 同上 | **305.18 µV/码** | ✅ 可用(缺 A/V 变比) | -| UB / UC | STM32 ADC1 | 12 位单极性 0~3.3 V | 805.66 µV | 同上 | **1611.3 µV/码** | ❌ 削顶(无中点偏置) | -| IB / IC / IN | STM32 ADC1 | 同上 | 805.66 µV | 同上 | **1611.3 µV/码** | ❌ 削顶 | +| UA | TPAFE5160 | 16 位双极性 ±5 V | 152.59 µV | 1k 串 + 滤波 C(③ = 1:1,无分压) | **152.59 µV/码** | ✅ 可用 | +| UN | TPAFE5160 | 同上 | 152.59 µV | 同上 | **152.59 µV/码** | ✅ 可用 | +| IA | TPAFE5160 | 同上 | 152.59 µV | 同上 | **152.59 µV/码** | ✅ 可用(缺 A/V 变比) | +| UB / UC | STM32 ADC1 | 12 位单极性 0~3.3 V | 805.66 µV | 同上 | **805.66 µV/码** | ❌ 削顶(无中点偏置) | +| IB / IC / IN | STM32 ADC1 | 同上 | 805.66 µV | 同上 | **805.66 µV/码** | ❌ 削顶 | | TEMP | STM32 ADC1 | 同上 | 805.66 µV | 同上 | 热敏非线性 | — 不纳入线性标定 | - 通道数:`ADC_SEM_COUNT = 9`(8 电气 + 温度),定义见 `App/algo/adc_input.h`。 -- 分压比出处:`docs/硬件平台/ADC通道映射说明.md` §7"前端偏置"行(每路 1k 串联 + 1k 到 `*_GND`)。 +- ✅ **无分压,③ 段变比 = 1:1(2026-10-27 网表追线定死)**:`docs/硬件平台/V0.1.tel` 第 241 行 + `ADC7 ; C44.2 R31.2 U3.61`——串联电阻靠转换器那一侧的节点上**只有**串阻、滤波电容、 + 转换器引脚三样,**没有对地元件**;"1k 到地"是接在**差分负端**(`ADC7_GND` → R32 → AGND) + 上的滤波网络,不在信号通路上。故端子侧系数 = 引脚侧系数。 + ⚠ 旧表按"2:1 分压"填的 **305.18 / 1611.3 µV/码 作废**,不得再引用 + (拓扑图见 `docs/硬件平台/ADC通道映射说明.md` §7.1)。 - ✅ **量程口径(2026-10-25 定)**:`docs/硬件平台/ADC通道映射说明.md` §1/§7 统一为 "±5 V 满量程 = 10 V 跨度、LSB 152.59 µV"("±10 V" 仅作芯片可选档括注,本板 RANGE 接 GND 未用); - 全仓搜 `±10 V` 无本板口径残留。分段链路口径(引脚侧已冻结 / 分压读法待卡 0 / 变比待卡 0b) + 全仓搜 `±10 V` 无本板口径残留。分段链路口径(引脚侧已冻结 / ③ 已定 1:1 / 变比待卡 0b) 见同文档 §7.1。 -- ⚠ **上表的"板级系数"只到 ADC 引脚,不到端子**(2026-10-23 补充):1k 串 + 1k 到地这组网络, - `docs/硬件平台/V0.0_网表详细分析报告.md` §4.7 定性为"**源阻抗匹配(手册推荐做法,非分压)**"; - 端子到引脚之间还有一级子板/端子板的变比,而**仓库里没有任何文档给出该变比** +- ⚠ **上表的"板级系数"只到 ADC 引脚,不到端子**:③ 段已定 1:1,但端子到 `AINn` 之间还有一级 + 子板/端子板变比(④ 段),而**仓库里没有任何文档给出该变比** (`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图)。故 - "端子二次值 = 码 × 上表系数"**不成立**,详见 §2.6 与卡 0。 + "端子二次值 = 码 × 上表系数"**不成立**,详见 §2.6 与卡 0b。 ### 2.3 三个必须同步重定的下游常数 diff --git a/docs/架构设计/图形绘制流程.md b/docs/架构设计/图形绘制流程.md index e86da4e0..a61cb5a5 100644 --- a/docs/架构设计/图形绘制流程.md +++ b/docs/架构设计/图形绘制流程.md @@ -49,6 +49,7 @@ | `gen_net_stack_svg.py` | `docs/images/BSD网络栈分层.svg` | BSD 网络栈分层 | | `gen_memory_svg.py` | `docs/images/内存布局_*.svg` | RAM / FLASH 布局 | | `gen_watchdog_svg.py` | `docs/images/看门狗心跳模型.svg` | `TASKS` / `REQ_BITS` / `POLL_STEPS` / `TIMELINE` | +| `gen_adc_frontend_svg.py` | `docs/images/ADC前端电路.svg` | `SUB1` / `NOTE_*` 等文案常量 + 面板坐标(电路拓扑固定,网表变了才重画) | --- @@ -142,3 +143,4 @@ sequenceDiagram | 2026-09-11 | 新增专门配图目录 `docs/images/`:收拢 `docs/` 根与 `docs/UI设计/images/` 下 5 张 SVG;同步更新 3 个生成器 OUT 路径、4 处文档图片引用、`AGENTS.md` 与 `.gitignore`(PNG 预览规则);流程沉淀为 `draw-diagram` skill | | 2026-09-11 | 存量 ASCII 框图清理:`STM32F4-Base存储架构说明.md` 两图(存储栈分层 / 物理分区布局)重绘为 SVG(生成器家族新增 `gen_storage_stack_svg.py` / `gen_partition_svg.py`);`RemoDispBus协议与接收设计说明.md` 删 ASCII 帧图(SVG 帧格式图已在,信息无损失);`测试说明/CH395F_Test_Guide.md` 4 图(2 组件架构 + 2 会话时序)转 Mermaid | | 2026-10-22 | 澄清 §派生物一句:`simulator/tests/ref/` 下作为像素黄金参考的 `.bin` 已入库,只有派生的 `.png` 目检图仍不入库(原表述把两者混为"参考图不入库",会诱导后来者把整个目录继续忽略掉) | +| 2026-10-27 | 新增 `gen_adc_frontend_svg.py` → `docs/images/ADC前端电路.svg`:把 `docs/硬件平台/ADC通道映射说明.md` §7.1 与 `docs/测试说明/ADC前端注入验证流程.md` §2 的 ASCII 拓扑图换成 SVG。ASCII 版把滤波支路画成挂在信号节点下的一根竖线,读起来像 2:1 分压——恰是要否掉的结论;SVG 把 `ADC7` / `ADC7_GND` 画成两个节点并加 STM32 侧对照面板。生成器沿用 §3.2 约定(纯标准库 / `html.escape` / `newline="\n"` / `ElementTree` 自检 + 文字溢出预检);目检用无头 Chrome 截图完成(`cairosvg` 未装,截图不入库) | diff --git a/docs/测试说明/ADC前端注入验证流程.md b/docs/测试说明/ADC前端注入验证流程.md index 93c5d8ad..ef505b3e 100644 --- a/docs/测试说明/ADC前端注入验证流程.md +++ b/docs/测试说明/ADC前端注入验证流程.md @@ -23,46 +23,45 @@ ## 2. 前置事实:这个前端不是分压器 -2026-10-27 按 `docs/硬件平台/V0.1.tel` 逐通道追线,以 UA 为例: +2026-10-27 按 `docs/硬件平台/V0.1.tel` 逐通道追线,以 UA 为例(图由 +`tools/arch_diagram/gen_adc_frontend_svg.py` 生成,改图请改脚本重跑,勿手改 SVG): -```text -U5.A3 (AIN1,端子侧) ──[R31 1k]── ADC7 ──┬── U3.61 (AIN_7P,正输入) - └──[C44]── ADC7_GND ──[R32 1k]── AGND - └── U3.62 (AIN_7GND,负输入) -``` +![UA 通道前端:端子 → R31 → ADC7 → TPAFE AIN_7P;C44 / R32 在差分负端,不在信号通路上](../images/ADC前端电路.svg) `ADC7` 这个节点上**只有** R31、C44、U3.61 三样东西,**没有对地电阻**。所谓"1k 到地"是 R32, 它接在 TPAFE 的**负输入脚 AIN_7GND**(芯片手册 pin62)上,是把差分负端经 1k 拴到 AGND, -属于差分滤波,不是信号分压。STM32 侧更直接:`AIN2 —[R52 1k]— ADC17 —[C111]— AGND`,同样没有分流电阻。 +属于差分滤波,不是信号分压。STM32 侧更直接(图 B):`AIN2 —[R52 1k]— ADC17 —[C111]— AGND`, +同样没有分流电阻,连 R32 那个接地电阻都没有。 **由此得到两条可直接用的结论**: -1. **不存在 2:1 分压**。`docs/任务规划/数据可信.md` §2.1 那组"板级系数 305.18 / 1611.3 µV/码" - (按 2:1 折算到 `AINn` 侧的条件值)在本板上不成立,且该文档已禁止它进代码——本次实测只需**确认**它不成立。 +1. **不存在 2:1 分压**。旧口径"板级系数 305.18 / 1611.3 µV/码"(按 2:1 折算到 `AINn` 侧的条件值) + 在本板上不成立;`docs/任务规划/数据可信.md` §2.1 已于 2026-10-27 改为引脚侧系数(152.59 / 805.66)。 + 所以卡 0 只需**确认转换器 LSB 与逐通道映射**,不必再判别两种读法。 2. **端子侧斜率 = 引脚侧斜率**。所以在**端子侧**注入既安全(有 1k 串阻护着芯片)又能直接验 LSB, 不必去碰 LQFP 引脚。 期望斜率(由转换器满量程事实推出,与前端无关): -| 转换器 | 满量程 | LSB | **期望斜率** | -|---|---|---|---| -| TPAFE5160 | ±5 V(10 V 跨度 / 65536 码) | 152.587890625 µV | **6553.6 码/V** | -| STM32 ADC1 | 0~3.3 V(3.3 V / 4096 码) | 805.6640625 µV | **1241.2 码/V** | +| 转换器 | 满量程 | LSB | **期望斜率** | +| ---------- | ----------------------- | ---------------- | -------------- | +| TPAFE5160 | ±5 V(10 V 跨度 / 65536 码) | 152.587890625 µV | **6553.6 码/V** | +| STM32 ADC1 | 0~3.3 V(3.3 V / 4096 码) | 805.6640625 µV | **1241.2 码/V** | ## 3. 注入点清单 串联电阻的两只焊盘就是两个注入层级:**1 脚 = 端子侧(靠 U5)**,**2 脚 = 引脚侧(靠转换器)**。 -| 语义 | 串阻 | 端子侧节点 | 引脚侧节点 | 转换器脚 | 期望斜率 | -|---|---|---|---|---|---| -| UA | R31 | `AIN1`(U5.A3) | `ADC7` | U3.61 | 6553.6 | -| UN | R28 | `AIN4`(U5.A4) | `ADC6` | U3.59 | 6553.6 | -| IA | R19 | `AIN8`(U5.B5) | `ADC3` | U3.53 | 6553.6 | -| UB | R52 | `AIN2`(U5.B7) | `ADC17` | U17.44(PC4) | 1241.2 | -| UC | R20 | `AIN3`(U5.C14) | `ADC12` | U17.37(PA3) | 1241.2 | -| IB | R49 | `AIN9`(U5.A8) | `ADC15` | U17.42(PA6) | 1241.2 | -| IC | R46 | `AIN10`(U5.C9) | `ADC10` | U17.35(PA1) | 1241.2 | -| IN | R48 | `AIN11`(U5.C10) | `ADC11` | U17.36(PA2) | 1241.2 | +| 语义 | 串阻 | 端子侧节点 | 引脚侧节点 | 转换器脚 | 期望斜率 | +| -- | --- | --------------- | ------- | ----------- | ------------- | +| UA | R31 | `AIN1`(U5.A3) | `ADC7` | U3.61 | 6553.6 | +| UN | R28 | `AIN4`(U5.A4) | `ADC6` | U3.59 | 6553.6 | +| IA | R19 | `AIN8`(U5.B5) | `ADC3` | U3.53 | 6553.6 | +| UB | R52 | `AIN2`(U5.B7) | `ADC17` | U17.44(PC4) | 1241.2 | +| UC | R20 | `AIN3`(U5.C14) | `ADC12` | U17.37(PA3) | 1241.2 | +| IB | R49 | `AIN9`(U5.A8) | `ADC15` | U17.42(PA6) | 1241.2 | +| IC | R46 | `AIN10`(U5.C9) | `ADC10` | U17.35(PA1) | 1241.2 | +| IN | R48 | `AIN11`(U5.C10) | `ADC11` | U17.36(PA2) | 1241.2 | | 温度 | R47 | `V_ZL2`(U5.C11) | `ADC14` | U17.41(PA5) | 不验(非线性,不参与标定) | > 引脚侧没有串阻保护,注入前**必须外串 10 k 限流**。除非端子侧结果对不上、需要分离 @@ -70,12 +69,12 @@ U5.A3 (AIN1,端子侧) ──[R31 1k]── ADC7 ──┬── U3.61 (AIN_7P ## 4. 读数出口(固件只给这四行,没有命令控制台) -| 日志行 | 频率 | 内容 | 用途 | -|---|---|---|---| -| `adc: UA=.. UB=.. UC=.. UN=.. IA=.. IB=.. IC=.. IN=.. T=..` | 每 10 s | **9 路原始码值** | 本次验证的主读数 | -| `cal: ua_mv=.. un_mv=.. ia_mv=.. vl=.. cl=..` | 每 10 s | UA/UN/IA 折算后毫伏 + 标定/削顶位图 | 旁证:码值 → 电压是否自洽 | -| `first: UA=.. … T=..` | 每次开机一次 | 9 路原始码值 | 与 `adc:` 行互校(同序同格式) | -| `frames=.. idle=.. adc1_err=.. tgap=..` | 每 10 s | 采集健康计数 | 步骤 5 的节奏判据 | +| 日志行 | 频率 | 内容 | 用途 | +| ----------------------------------------------------------- | ------ | ------------------------ | ------------------- | +| `adc: UA=.. UB=.. UC=.. UN=.. IA=.. IB=.. IC=.. IN=.. T=..` | 每 10 s | **9 路原始码值** | 本次验证的主读数 | +| `cal: ua_mv=.. un_mv=.. ia_mv=.. vl=.. cl=..` | 每 10 s | UA/UN/IA 折算后毫伏 + 标定/削顶位图 | 旁证:码值 → 电压是否自洽 | +| `first: UA=.. … T=..` | 每次开机一次 | 9 路原始码值 | 与 `adc:` 行互校(同序同格式) | +| `frames=.. idle=.. adc1_err=.. tgap=..` | 每 10 s | 采集健康计数 | 步骤 5 的节奏判据 | `adc:` 行是 2026-10-27 新增的(`App/task/adc_task.c` 心跳块),此前只有 `cal:` 的三路, 验 8 个通道要复位二十余次。现在**全程不必复位**。 @@ -98,10 +97,10 @@ U5.A3 (AIN1,端子侧) ──[R31 1k]── ADC7 ──┬── U3.61 (AIN_7P 把注入点**短接到 AGND**(短接点先用 0 Ω 档确认与 AGND 直通),读 `adc:` 行。 -| 通道 | 判据 | 当前空载实测(浮空) | -|---|---|---| -| UA / UN / IA | \|码\| ≤ 10 | **≈ 11430** | -| UB / UC / IB / IC / IN | \|码\| ≤ 5 | **≈ 1000** | +| 通道 | 判据 | 当前空载实测(浮空) | +| ---------------------- | -------- | ----------- | +| UA / UN / IA | |码| ≤ 10 | **≈ 11430** | +| UB / UC / IB / IC / IN | |码| ≤ 5 | **≈ 1000** | **为什么这一步最关键**:空载(输入浮空)读数比判据高两个数量级,最可能的解释是输入悬空后 漏电流在滤波电容上建立的平衡点——短接 AGND 后应塌回 0。但如果**短接后仍读 11430**,那就是 @@ -115,10 +114,10 @@ U5.A3 (AIN1,端子侧) ──[R31 1k]── ADC7 ──┬── U3.61 (AIN_7P `adc:` 行读对应码值。 | 注入(实测电压) | TPAFE 期望码 | STM32 期望码 | -|---|---|---| -| ≈ 0.3 V | 1966 | 372 | -| ≈ 1.0 V | 6554 | 1241 | -| ≈ 2.0 V | 13107 | 2482 | +| -------- | --------- | --------- | +| ≈ 0.3 V | 1966 | 372 | +| ≈ 1.0 V | 6554 | 1241 | +| ≈ 2.0 V | 13107 | 2482 | 判据:三点线性拟合 **R² ≥ 0.999**,斜率与期望值偏差 **≤ 2 %**。 @@ -142,8 +141,8 @@ U5.A3 (AIN1,端子侧) ──[R31 1k]── ADC7 ──┬── U3.61 (AIN_7P TPAFE 是双极性、二进制补码,可以注负压;STM32 是单极性,负半周测不了。给 UA/UN/IA 各加一个 **−1.0 V** 点: -| 注入 | 期望码 | -|---|---| +| 注入 | 期望码 | +| ------ | --------- | | −1.0 V | **−6554** | 码值为负、幅值与 +1.0 V 点对称(差值 ≤ 2 %)→ 符号位与补码解释正确。 @@ -164,14 +163,14 @@ TPAFE 是双极性、二进制补码,可以注负压;STM32 是单极性, ## 6. 判据汇总 -| 项 | 判据 | 不通过意味着 | -|---|---|---| -| 零点 | TPAFE \|码\| ≤ 10;STM32 \|码\| ≤ 5 | 有真实偏置,需查前端;可能超出补偿上限 | -| 线性 | R² ≥ 0.999 | 前端非线性或注入源带载失真 | -| 斜率 | 偏差 ≤ 2 % | 前端有未识别元件,或转换器档位/参考错 | -| 映射 | 注入哪路只动哪路 | 语义表与物理通道不符(`k_adc_map[]` 错) | -| 极性 | −1.0 V → −6554 码 | 补码符号处理错 | -| 节奏 | 注入期间仍 1000 Hz | 注入改变了输入共模,触发转换异常 | +| 项 | 判据 | 不通过意味着 | +| -- | ---------------------------- | --------------------------- | +| 零点 | TPAFE |码| ≤ 10;STM32 |码| ≤ 5 | 有真实偏置,需查前端;可能超出补偿上限 | +| 线性 | R² ≥ 0.999 | 前端非线性或注入源带载失真 | +| 斜率 | 偏差 ≤ 2 % | 前端有未识别元件,或转换器档位/参考错 | +| 映射 | 注入哪路只动哪路 | 语义表与物理通道不符(`k_adc_map[]` 错) | +| 极性 | −1.0 V → −6554 码 | 补码符号处理错 | +| 节奏 | 注入期间仍 1000 Hz | 注入改变了输入共模,触发转换异常 | ## 7. 安全红线 @@ -184,65 +183,66 @@ TPAFE 是双极性、二进制补码,可以注负压;STM32 是单极性, ## 8. 已知坑 -| 现象 | 原因 | 处置 | -|---|---|---| -| 空载读数 11430 / 1000 码,跨开机稳定 | 输入浮空后的漏电平衡点(推测,待步骤 1 证实) | 短接 AGND 后应塌回 0;不塌则按 §5.2 处理 | -| `cl=0xe6` 恒不变 | `UB/UC/IB/IC/IN` 五路单极性削顶,是 V0.1 硬件缺陷 | 正常;这五路只验直流,不验交流 | -| `vl=0x00` 恒不变 | 从未做过两点标定,`VALID` 未置位 | 正常;标定后才会变 | -| `meas: ua_mv=0` | 基波有效值,直流注入下本就为 0 | 正常;验交流要等信号源 | -| `adc:` 行某路恒 0 而 `frames` 在涨 | 该路取数失败 | 查该路是否真的接线/短路 | -| 斜率恰好差一倍 | 误用了 2:1 分压口径(305.18 / 1611.3) | 本板无分压,见 §2;按 152.59 / 805.66 重算 | +| 现象 | 原因 | 处置 | +| --------------------------- | ------------------------------------ | ------------------------------- | +| 空载读数 11430 / 1000 码,跨开机稳定 | 输入浮空后的漏电平衡点(推测,待步骤 1 证实) | 短接 AGND 后应塌回 0;不塌则按 §5.2 处理 | +| `cl=0xe6` 恒不变 | `UB/UC/IB/IC/IN` 五路单极性削顶,是 V0.1 硬件缺陷 | 正常;这五路只验直流,不验交流 | +| `vl=0x00` 恒不变 | 从未做过两点标定,`VALID` 未置位 | 正常;标定后才会变 | +| `meas: ua_mv=0` | 基波有效值,直流注入下本就为 0 | 正常;验交流要等信号源 | +| `adc:` 行某路恒 0 而 `frames` 在涨 | 该路取数失败 | 查该路是否真的接线/短路 | +| 斜率恰好差一倍 | 误用了 2:1 分压口径(305.18 / 1611.3) | 本板无分压,见 §2;按 152.59 / 805.66 重算 | ## 9. 记录表(台架现场填) ### 9.1 零点 -| 通道 | 短接后码值 | 判据 | 结论 | -|---|---|---|---| -| UA | | ≤ 10 | | -| UN | | ≤ 10 | | -| IA | | ≤ 10 | | -| UB | | ≤ 5 | | -| UC | | ≤ 5 | | -| IB | | ≤ 5 | | -| IC | | ≤ 5 | | -| IN | | ≤ 5 | | +| 通道 | 短接后码值 | 判据 | 结论 | +| -- | ----- | ---- | -- | +| UA | | ≤ 10 | | +| UN | | ≤ 10 | | +| IA | | ≤ 10 | | +| UB | | ≤ 5 | | +| UC | | ≤ 5 | | +| IB | | ≤ 5 | | +| IC | | ≤ 5 | | +| IN | | ≤ 5 | | ### 9.2 三点线性(UA) | 注入电压(万用表) | 码值 | 反算 µV/码 | -|---|---|---| -| | | | -| | | | -| | | | +| --------- | -- | ------- | +| | | | +| | | | +| | | | -拟合 R² = ______ 斜率 = ______ 码/V 偏差 = ______ % +拟合 R² = \______ 斜率 = \______ 码/V 偏差 = \______ % ### 9.3 逐通道映射 | 注入路 | 注入 1.0 V 后变动的路 | 码值 | 是否符合预期 | -|---|---|---|---| -| UA | | | | -| UB | | | | -| UC | | | | -| UN | | | | -| IA | | | | -| IB | | | | -| IC | | | | -| IN | | | | +| --- | -------------- | -- | ------ | +| UA | | | | +| UB | | | | +| UC | | | | +| UN | | | | +| IA | | | | +| IB | | | | +| IC | | | | +| IN | | | | ### 9.4 极性 | 通道 | +1.0 V 码值 | −1.0 V 码值 | 对称性偏差 | -|---|---|---|---| -| UA | | | | -| UN | | | | -| IA | | | | +| -- | --------- | --------- | ----- | +| UA | | | | +| UN | | | | +| IA | | | | --- ## 变更记录 -| 日期 | 内容 | -|---|---| +| 日期 | 内容 | +| ---------- | ---------------------------------------------------------------------------------------------------------------------------- | | 2026-10-27 | 建档。承接 `docs/任务规划/数据可信.md` 卡 0 的执行侧;固化"前端无分压"的网表结论、端子侧注入点清单、期望码值表与五步流程。同日 `App/task/adc_task.c` 心跳新增 `adc:` 行(9 路原始码值),使全程免复位 | +| 2026-10-27 | §2 的 ASCII 拓扑图换成 SVG(`docs/images/ADC前端电路.svg`,生成器 `tools/arch_diagram/gen_adc_frontend_svg.py`)。ASCII 版把 C44 / R32 画成挂在 ADC7 下的一根竖线,读起来像 2:1 分压——正是本节要否掉的结论;新图把 `ADC7` 与 `ADC7_GND` 画成两个节点,并加 STM32 侧对照面板 | diff --git a/docs/硬件平台/ADC通道映射说明.md b/docs/硬件平台/ADC通道映射说明.md index 12b171b4..3716f8e2 100644 --- a/docs/硬件平台/ADC通道映射说明.md +++ b/docs/硬件平台/ADC通道映射说明.md @@ -134,7 +134,7 @@ STM32(U17)为 LQFP144 脚号。 | ADC 时钟 | CubeMX 的 F4 ADC 面板**有** Clock Prescaler 一项(此前"面板没有该项、固定 DIV2"的判断有误),已选 **PCLK2/4 → ADCCLK = 21 MHz** ✓。面板里 **DIV2 是灰的、选不到**:DB 里 `FadcConstraint Max=36000000`,APB2 = 84 MHz → DIV2 = 42 MHz 超限,该候选值被 `Action="DISABLE"` 禁掉,超规值进不来 | **不设额外防护(2026-10-09 决定)**:约束已由 CubeMX 把关;`.ioc` 虽不保存 `ADC1.ClockPrescaler` 键,重生成也只会回落到约束允许的值。按 144+12 cycles 算 ≈ 7.4 µs/通道、9 通道一轮 ≈ 67 µs | | `adc_task` | **已实现(B1,2026-10-10;L1 层 v2.3;P1-5 事件驱动消费,2026-10-10)**:`App/task/adc_task.c` 每拍调 `adc_input_poll()`(`App/algo/adc_input.c`)= TPAFE `tpafe5160_snapshot()` 原子快照(8 通道)+ 消费一帧 STM32 ADC1 DMA 乒乓缓冲,**经 `adc_input_raw()` 按语义取数**(不再直接索引驱动缓冲);**消费唤醒由 TIM3 ISR 置位 `ADC_WAKE_FLAG`**(不再 1 ms 轮询);任务在 `Src/freertos.c` 的 `USER CODE` 区创建为 `adcTask`(512 words / **AboveNormal**) | 数据路径已通(台架 `@test`:`frames` 严格 1000 Hz 递增、**`idle=0`**、`adc1_rc=0` / `adc1_err=0`) | | TPAFE 采样触发 | **周期 CONVST 已实现(2026-10-10)**:`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback` 在 **TIM3 更新事件(1 kHz)** 内调 `tpafe5160_start_conv_irq()`;`app_main_init()` 用 `HAL_TIM_Base_Start_IT(&htim3)` 启动节拍(在 `tpafe5160_init()` 之后)。同一 TIM3 更新事件还经 **TRGO** 硬件触发 ADC1 一轮 9 通道(DMA 搬运),并调 `adc_tick_irq_notify()` **唤醒消费者**(P1-5 方案 a,2026-10-10;内含"消费者就绪"门)→ 两片转换器由同一硬件事件同源对齐 | 连续波形(1 kHz);详见 `docs/子系统设计/ADC采样节拍设计说明.md` §4(DMA 乒乓搬运)与 §6(事件驱动唤醒) | -| 前端偏置 | 每路 1k 串联 + 1k 到 `*_GND` + 滤波 C,**无中点偏置** | STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若要算 RMS/功率需硬件补偏置或明确算法口径 | +| 前端偏置 | 每路 1k 串联 + 滤波 C 跨接差分输入;差分负端(`*_GND` 网)经 1k 拴 AGND。**不是对地分压**(2026-10-27 网表追线定死,拓扑图见 §7.1),**无中点偏置** | STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若要算 RMS/功率需硬件补偏置或明确算法口径 | | TPAFE 量程(**2026-10-25 卡 1 定死**) | 板上 RANGE 接 GND → **±5 V 双极性量程**(芯片手册输入量程表:RANGE = 0 → −5 ~ +5 V),16 位二进制补码,**±5 V 满量程 = 10 V 跨度**,LSB = 10 V / 65536 ≈ **152.59 µV/码(@ 转换器引脚)**。“±10 V” 是芯片的另一可选档(RANGE = HIGH,跨度 20 V、LSB ≈ 305.18 µV),**本板未用**——历史版本曾把本板误标为“±10 V 双极性”,与 §7 本行矛盾、且会让 TPAFE 侧系数差一倍,更正记录见 §9 | 用 `tpafe5160_to_voltage()` 换算;端到端口径见 §7.1 | | 取数契约(**2026-10-10 已修**,E-44 / E-45 / E-58 闭环) | 旧的 `get_buf` / `data_ready` / `clear_ready` **已删除**(删除前确认全仓库零调用者):`get_buf` 只给指针、任务再拷 → 被 BUSY EXTI 抢占就是**两帧混合**;`s_ready` 电平标志被 `start_conv_irq` 触发前清 0 → **重触发即丢未消费帧**;`read_all` 从不看 FRSTDATA(宏有定义、代码零引用)→ 芯片死亡时返回 OK + 常量垃圾。现口径:**`tpafe5160_snapshot(dst, count)`**(临界区原子拷贝,返回 1/0/-1,**仅任务上下文**)+ **`tpafe5160_frame_seq()`**(帧序号只增不清)+ **`tpafe5160_frstdata_err()`**(无跳变帧计数);`tpafe5160_init()` 增**首帧自检**——一帧读下来 FRSTDATA 无跳变(芯片不驱动并行总线;板上 R24 4.7k 上拉会把它钉死为高)→ 返回 `-3` + `[ERR]` | 消费者**只能**用 `snapshot()` 取数(`AGENTS.md` 已立为铁律);真机实测开机 `[ADC] Init OK` 现只在自检帧通过后才打印 → 判据在本板成立、非空转 | @@ -154,30 +154,39 @@ STM32(U17)为 LQFP144 脚号。 “±10 V”只允许出现在“芯片可选档”语境。本文件 2026-10-09 建档时曾把文首摘要写成“TPAFE … ±10 V 双极性”,与 §7 的 ±5 V 档矛盾、**TPAFE 侧系数因此会差一倍**——2026-10-23 `docs/任务规划/数据可信.md` §2.1 挂账该冲突并要求卡 1 定死;现**定死为“±5 V 满量程 = 10 V 跨度、LSB 152.59 µV”**,文首摘要与 §7 已按此更正。全仓其余“±10 V”字样仅剩芯片手册转写与 `tpafe5160.h` 中 RANGE 枚举可选档注释(均为芯片能力描述,非本板配置口径),不构成歧义。 +**③ 段的实际拓扑**(2026-10-27 按 `V0.1.tel` 逐通道追线,UA 为例;图由 +`tools/arch_diagram/gen_adc_frontend_svg.py` 生成,改图请改脚本重跑,勿手改 SVG): + +![UA 通道前端:端子 → R31 → ADC7 → TPAFE AIN_7P;C44 / R32 在差分负端,不在信号通路上](../images/ADC前端电路.svg) + +`ADC7` 节点上**只有** R31、C44、U3.61 三样(网表第 241 行),**没有对地元件**——所以 ③ 段 +不是分压器,变比 **= 1:1**。C44 / R32 那一串接在 `ADC7_GND`(= TPAFE 负输入 `AIN_7GND`)上, +是差分滤波,不在信号通路上。STM32 侧同构且更简(图 B:`C111` 直落 AGND,连接地串阻都没有)。 + **链路分段口径**: | 段 | 关系 | 状态 | |---|---|---| | ① 语义 ↔ 网名 ↔ 板级网名 ↔ 转换器代码通道 | §2/§3/§4/§5 的 17 路映射表 | **定死**(本文件为唯一权威) | | ② 转换器引脚电压 ↔ 码值 | TPAFE `U_pin = code × 152.59 µV`(code 带符号);STM32 `U_pin = code × 805.66 µV` | **定死**(纯转换器事实,上表) | -| ③ `AINn`(串联电阻子板侧)↔ `ADCn`/转换器引脚 | 每路 1k 串联 + 1k 到 `*_GND` + 滤波 C(§7“前端偏置”行)。两种读法并存:**A** 按 2:1 电阻分压折算到 `AINn` 侧 → 系数 305.18(TPAFE)/ 1611.3(STM32)µV/码;**B** [`V0.0_网表详细分析报告.md`](V0.0_网表详细分析报告.md) §4.7 定性“源阻抗匹配(手册推荐做法,非分压)”→ 两侧系数相同 | **待台架实测**——`docs/任务规划/数据可信.md` 卡 0 注入斜率判别:引脚侧注入验 ②(期望 ≈6553 码/V),`AINn`/端子侧注入的斜率即 ③(或 ③+④)的合成变比。本卡不裁决 A/B | -| ④ 语义端子(二次端子)↔ `AINn` | 子板/端子板变比:仓库内**无任何文档**(`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图;§8 步骤 4) | **缺口**——卡 0b 实测确认;拿不到变比则“端子二次值”不成立、`eng` 保持 0 | +| ③ `AINn`(串联电阻子板侧)↔ `ADCn`/转换器引脚 | **变比 = 1:1(2026-10-27 定死)**:`ADCn` 节点只有 {串阻.2, 滤波 C, 转换器引脚},无对地元件(拓扑图见上)。旧"读法 A(2:1 分压 → 305.18 / 1611.3 µV/码)"**作废**——它是把接在差分负端的 1k 误读成对地分压 | **定死**(网表级结论)。卡 0 不再判别分压读法,只确认转换器 LSB 与逐通道映射 | +| ④ 语义端子(二次端子)↔ `AINn` | 子板/端子板变比:仓库内**无任何文档**(`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图;§8 步骤 4) | **缺口**——卡 0b 实测确认;拿不到变比则"端子二次值"不成立、`eng` 保持 0 | **可评审结论**: 1. **ROM 默认系数按 ② 引脚口径冻结**:TPAFE 通道 `k = 152.59 µV/码`、STM32 通道 `k = 805.66 µV/码`;标定折算的输出单位 = **ADC 引脚电压 V**(`eng = 0`)。该口径**不依赖** ③④ 两个未决项,自本卡定死即成立——这就是 M4 承诺边界止于“引脚电压”的依据(`docs/任务规划/数据可信.md` §2.6)。 -2. `docs/任务规划/数据可信.md` §2.1 表中“板级系数(输入侧)305.18 / 1611.3 µV/码”是**按 ③ 读法 A(2:1 分压)折算到 `AINn` 侧**的条件值,而同一网络已被 V0.0 §4.7 定性为非分压(读法 B)。卡 0 判别 A/B 之前,305.18 / 1611.3 **不得**进任何代码或换算;引脚侧一律用 152.59 / 805.66。 -3. **满量程必须写明参考点**:TPAFE 通道满量程 = ±5 V **@ 引脚**(跨度 10 V);③④ 定死前,端子侧满量程只允许写“引脚侧数值 × ③ 比 × ④ 比”,两个比值当前均未知,**严禁**把“端子电压 = 码 × 305.18 µV”写进代码/文档当作已成立结论。 -4. **待台架实测占位(本轮按用户指示跳过)**:卡 0(三点注入:线性 R² ≥ 0.999、斜率、零点、逐通道映射确认)、卡 0b(端子→引脚变比)、卡 12(两点标定)完成后,回写本节 ③④ 两行与 §8。实测斜率与 ② 理论值偏差 >2 % 时**先查前端**(分压/变比/焊接),不得用系数凑错斜率(`docs/任务规划/数据可信.md` §8 风险表)。 +2. `docs/任务规划/数据可信.md` §2.1 旧表的“板级系数(输入侧)305.18 / 1611.3 µV/码”**已随 ③ 段定死而删除**(2026-10-27):该值按 2:1 分压折算,而网表证明 `ADCn` 节点无对地元件。板级系数 = 引脚侧系数(152.59 / 805.66);305.18 / 1611.3 今后只允许出现在“±10 V 档 LSB”语境(芯片手册口径),不得再当本板系数。 +3. **满量程必须写明参考点**:TPAFE 通道满量程 = ±5 V **@ 引脚**(跨度 10 V);③ 已定 1:1、④ 仍未知,故端子侧满量程只允许写“引脚侧数值 × ④ 比”,**严禁**把“码 × 152.59 µV”当端子二次值写进代码/文档(那是引脚电压)。 +4. **待台架实测占位(本轮按用户指示跳过)**:卡 0(三点注入:线性 R² ≥ 0.999、斜率、零点、逐通道映射确认)、卡 0b(端子→引脚变比)、卡 12(两点标定)完成后,回写本节 ④ 行与 §8。实测斜率与 ② 理论值偏差 >2 % 时**先查前端**(分压/变比/焊接),不得用系数凑错斜率(`docs/任务规划/数据可信.md` §8 风险表)。 --- ## 8. 逐通道验证方法(上板自检口径) -1. **TPAFE 侧**:对目标 `AIN` 注入 1 V DC → 观察对应 `buf[i]` 变化(UA→`buf[6]`、UN→`buf[5]`、IA→`buf[2]`),同时 U3.14 `BUSY` 有脉冲;**语义与下标不符即映射错**。⚠ **本步无执行记录**(2026-10-23 核查;`V0.0_网表详细分析报告.md` §5.1 第 6 条的同一验证也从未执行)。已排入 `docs/任务规划/数据可信.md` 卡 0,并把两条判据降为**不需要示波器**的口径:① "`BUSY` 有脉冲"改用固件帧计数——注入前后 `tpafe5160_frame_seq()` 仍严格 1000 Hz 递增即等价;② 幅值判据改用万用表读注入点实际电压 + 同时读 `adc_input_raw()` 码值,三点线性拟合(R² ≥ 0.999)。 +1. **TPAFE 侧**:对目标 `AIN` 注入 1 V DC → 观察对应 `buf[i]` 变化(UA→`buf[6]`、UN→`buf[5]`、IA→`buf[2]`),同时 U3.14 `BUSY` 有脉冲;**语义与下标不符即映射错**。⚠ **本步无执行记录**(2026-10-23 核查;`docs/硬件平台/V0.0_网表详细分析报告.md` §5.1 第 6 条的同一验证也从未执行)。已排入 `docs/任务规划/数据可信.md` 卡 0,并把两条判据降为**不需要示波器**的口径:① "`BUSY` 有脉冲"改用固件帧计数——注入前后 `tpafe5160_frame_seq()` 仍严格 1000 Hz 递增即等价;② 幅值判据改用万用表读注入点实际电压 + 同时读 `adc_input_raw()` 码值,三点线性拟合(R² ≥ 0.999)。 2. **STM32 侧**:对目标引脚(如 PC4=UB)注入 0.5 V → 读 `ADC_CHANNEL_14` 应变化;若不变而相邻通道变化 → 通道宏写错(见 §5 警告)。⚠ **本步尚未执行**(2026-10-10 「STM32 ADC1 启动与扫描」台架只验证了"9 通道全部转换成功 + 零超时 + 互异非饱和 + 跨次开机读数不同",台架无信号源);映射的**代码侧**一致性有两道保证:① `App/algo/adc_input.c` 的 `k_adc_map[]` 与 CubeMX 标签(`ADC17_UB`=PC4 等)逐条对齐;② 开机自检 `adc_input_init()` 校验每个 STM32 语义通道都落在 `k_stm32_rank_ch[]` 扫描序列内(挡"映射到不扫描的通道 → 恒读陈旧值")。 3. **备用通道**:RESERVED 路不接入任何语义,UI 与上报里不得出现;若实测有读数漂移,只记录不消费。 -4. **端子侧变比(2026-10-23 补)**:上面 1/2 两条都只到 **ADC 引脚**。**"端子电压 → ADC 引脚电压"的变比在仓库里没有任何文档给出**(`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图),而 `V0.0_网表详细分析报告.md` §4.7 已把 `1k 串联 + 1k 到地` 这组网络定性为"**源阻抗匹配(手册推荐做法,非分压)**"。故**不得**用"2:1 分压"直接换算出二次值;该变比由 `docs/任务规划/数据可信.md` 卡 0b 实测确认。 +4. **端子侧变比(2026-10-23 补,2026-10-27 收窄)**:上面 1/2 两条都只到 **ADC 引脚**。③ 段(`AINn` ↔ 转换器引脚)已由网表追线定死为 **1:1**(`ADCn` 节点无对地元件,拓扑图见 §7.1),**板内不再有未知比值**;剩下的未知量只有 ④ **子板/端子板变比**——它在仓库里没有任何文档给出(`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图)。故**不得**用"2:1 分压"换算出二次值;该变比由 `docs/任务规划/数据可信.md` 卡 0b 实测确认。 --- @@ -191,3 +200,4 @@ STM32(U17)为 LQFP144 脚号。 | 2026-10-10 | **STM32 ADC1 消费者落地(L1-3/L1-4)**:新增 `App/algo/adc_input.c/h`——语义映射表 `k_adc_map[]` 按本文件 §3/§5 逐条落地(`UA/UN/IA`→TPAFE `buf[6]/[5]/[2]`,`UB/UC/IB/IC/IN/TEMP`→`CH14/3/6/1/2/5`),并提供 `adc_input_raw()` **按语义取数**;`stm32_scan()` 实现 9 通道软件触发顺序扫描;`adc_input_init()` 增**映射自检**(STM32 映射通道必在扫描序列内)。§7 的"STM32 ADC 外设 / `adc_task` / TPAFE 采样触发"三行同步更新(后两者原为陈旧描述:TIM6 周期触发、L1 层取数)。§8 步骤 2 标注**引脚级注入验证尚未执行**。台架 `@test` PASS(`adc1_rc=0` / `adc1_err=0`、`ADC input layer init: OK`;首帧 `UA=11429 UN=11454 IA=11452`、`UB=1018 UC=1044 IB=943 IC=1069 IN=1052 T=850`——互异非饱和,且两次开机读数不同 → 实时转换而非常量),证据 `测试说明/boot_log_data/20261010_prod_adc_l1_boot_log.txt` | | 2026-10-23 | **验证现状核查与排期**:确认 §8 步骤 1/2 与 `V0.0_网表详细分析报告.md` §5.1 第 6 条的"首次功能级 ADC 验证"**均无执行记录**——现有台架判据只证"通路活"(9 通道转换成功、零超时、互异非饱和、跨次开机不同),不证读数正确,连极性都未验。新增 §8 步骤 4:**"端子电压 → ADC 引脚电压"的变比在仓库内无任何文档**(无子板原理图),且 `1k 串联 + 1k 到地` 已被 §4.7 定性为"源阻抗匹配、非分压",故撤销"按 2:1 换算二次值"的用法。验证方法同步降为**不需要示波器 / 信号源**:`BUSY` 脉冲改用 `tpafe5160_frame_seq()` 帧计数,幅值改用万用表 + 三点线性拟合。排期见 `docs/任务规划/数据可信.md` 卡 0 / 0b | | 2026-10-25 | **量程口径定死(本波卡 1)**:文首摘要行“TPAFE5160(16 位、8 通道同步、±10 V 双极性)”更正为“±5 V 满量程 = 10 V 跨度、LSB ≈ 152.59 µV(板上 RANGE 接 GND)”;§1“缺陷实质 2”补“满量程 = 10 V 跨度”;§7“TPAFE 量程”行重写并括注“±10 V 是芯片可选档(RANGE=HIGH)、本板未用”;新增 §7.1 固化“端子 → 子板变比 → 网名 AINn → 电阻网络 → 引脚 → 码值 → 满量程”链路分段口径——② 引脚侧 LSB 系数(TPAFE 152.59 / STM32 805.66 µV/码)**定死**,③ 1k+1k 网络“2:1 分压 vs 源阻抗匹配”两读法待卡 0 斜率判别,④ 端子变比为仓库缺口待卡 0b;“板级系数 305.18 / 1611.3 µV/码”定性为读法 A 条件值、判别前禁入代码;新增内容全部收进 §7.1 子节、本章编号刻意保持 §9 不动(`docs/算法/继电保护功能复现规划.md` §4 有指向本文件 §9 的既有跨文档引用,挪号会打断 doc-links 章节校验)。实测数值本轮按用户指示未采(台架未跑),相关段均标“待台架实测” | +| 2026-10-27 | **③ 段定死为 1:1(网表追线)**:逐通道追 `V0.1.tel`,`ADCn` 节点(如 UA 的 `ADC7`,第 241 行 `ADC7 ; C44.2 R31.2 U3.61`)只有 {串阻.2, 滤波 C, 转换器引脚} 三者,**无对地元件**——"1k 到地"实际接在差分负端(`ADC7_GND` → R32 → AGND,第 151 行),属差分滤波。故 ③ 段不是分压器、变比 1:1,旧"读法 A(2:1 分压 → 305.18 / 1611.3 µV/码)"**作废**,`docs/任务规划/数据可信.md` §2.1 旧表同步改为引脚侧系数。§7.1 新增拓扑 SVG(`docs/images/ADC前端电路.svg`,生成器 `tools/arch_diagram/gen_adc_frontend_svg.py`),含 STM32 侧对照面板;§7"前端偏置"行、§8 步骤 4 同步改写。卡 0 的判据随之从"判别 A/B 读法"降为"确认转换器 LSB 与逐通道映射" | diff --git a/tools/arch_diagram/README.md b/tools/arch_diagram/README.md index 07924c27..7263ec9a 100644 --- a/tools/arch_diagram/README.md +++ b/tools/arch_diagram/README.md @@ -16,6 +16,7 @@ | `gen_cfg_arch_svg.py` | 配置引擎架构图 | 配置分层与持久化 | | `gen_partition_svg.py` | 分区图 | 存储分区 | | `gen_watchdog_svg.py` | 看门狗心跳模型图 | IWDG + 任务心跳位图(置位→判定→喂狗 + 时间轴) | +| `gen_adc_frontend_svg.py` | ADC 前端电路图 | 端子 → 串阻 → 转换节点 → 转换器引脚;含差分负端与 STM32 侧对照 | ## 使用方式 diff --git a/tools/arch_diagram/gen_adc_frontend_svg.py b/tools/arch_diagram/gen_adc_frontend_svg.py new file mode 100644 index 00000000..eee60ca8 --- /dev/null +++ b/tools/arch_diagram/gen_adc_frontend_svg.py @@ -0,0 +1,269 @@ +#!/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()