目录
[1. 先梳理数据链路](#1. 先梳理数据链路)
[2. 新旧版本 diff,圈定改动范围](#2. 新旧版本 diff,圈定改动范围)
[3. 真正的根因](#3. 真正的根因)
[4. 修复方案](#4. 修复方案)
标签:嵌入式 / FPGA / STM32 / 双向电源 / 调试笔记
一、背景
产品是一台双向直流电源,支持两种工作模式:
-
源模式(Source):向外输出功率,电流为正;
-
载模式(Sink) :吸收功率,电流为负。 系统架构是典型的"FPGA + MCU"方案:
AD7606(ADC采样) → FPGA(校准/滤波/多通道求和) → 并行总线 → STM32H7(M7内核) → 显示/上报
FPGA 负责把 AD 采样的原始码值做系数校准、偏置修正、低通滤波,以及两通道电流求和(并联运行时的总电流),再通过并行数据口发给 M7。M7 把 FPGA 的 Q19 定点数换算成浮点物理量,经过一阶低通后用于显示和上位机上报。 近期做了一次版本升级,改动主要有两块:
- FPGA:新增了 CH1+CH2 的电流/电压求和通路,并把求和结果回注到"并机采样"数据通道;
- M7:显示电流不再用本机值顶替,改为真正使用 FPGA 传来的并机求和值,同时给一批采样值加了"负值钳位为 0"的处理。
二、现象
改版后联调,源模式一切正常,载模式下电流显示直接是 0 。 在线调试观察,"FPGA 传过来的电流值"也是 0。第一反应很自然:是不是 FPGA 求和通路写错了,把数据算成了 0?
三、排查过程
1. 先梳理数据链路
遇到"某数据变成 0"的问题,第一步是把链路完整捋一遍,确认每一级是谁、改了谁:
AD7606 → FPGA校准/求和(DisSampleAdjust / CtrlSampleAdjust_offset)
→ 通信接口(armm4_if,显示电流在 addr 39/40)
→ M7 的 AppFpgaData(原始 int32)
→ APP_AICalculate(Q19→浮点换算)
→ 低通滤波 → 显示/Modbus上报
2. 新旧版本 diff,圈定改动范围
手上有新旧两套工程,直接逐文件 diff:
- FPGA 侧:通信接口和寄存器地址映射一字未动;新增的求和状态机逐步检查了流水线时序、饱和判断,逻辑上没有问题;
- M7 侧 :整个工程只有 4 个文件有差异,其中
APP_AICalculate.c是数据换算的核心。 到这里,FPGA"算错成 0"的嫌疑基本可以排除了------发送链路没变,接收端的换算代码变了。
3. 真正的根因
对比新旧代码,发现新版 M7 有两处关键改动:
改动一:给多个电流量新增了负值钳位
pCalcuData->IodisParall = (float)pFpgaData->IodisParall * Q19_TO_Q0;
if(pCalcuData->IodisParall < 0.0f) // ← 新增
{
pCalcuData->IodisParall = 0.0f;
}
Iodis(显示电流)、IodisParall(并机显示电流)、IoctrlParall、IoctrlParallcomm 全部加了 < 0 → 0 的钳位。
改动二:显示值强制改用并机求和值 各种运行模式下,Iodisall / Ioctrlall 在几乎所有分支里被替换成了上面这些钳位后的并机值。 问题就在这里:
载模式下电流本来就是负的 。系统里"源/载自动判别"甚至就是靠
Iodisall < 0来判断载模式的。钳位一加,负电流全部变成 0,显示自然是 0;顺带连源/载自动判别也一起失效了------钳位后的值恒非负,永远判成"源模式"。 所以"在线看 FPGA 传过来就是 0"其实是个误导:发送方传的是负数,是接收端把它钳成了 0 。如果你 watch 的是换算之后的变量(pCalcuData->IodisParall),看到的就是钳位后的 0;只有 watch 原始的pFpgaData->IodisParall(int32)才能看到真实的负值。 这也解释了为什么只有载模式出问题:源模式电流为正,钳位不起作用,一切正常。
4. 修复方案
修复思路:按工作模式区别对待------源模式保留钳位(吸收空闲时采样的小负偏移),载模式放行负值。 系统配置里已有工作模式字段(3 = 源模式,4 = 载模式),把四处钳位改成条件钳位:
// 仅源模式(PowerMode==3)钳位;载模式电流为负是物理事实,必须放行
if((pSysCfg->SysCfgControl.PowerMode == 3) && (pCalcuData->IodisParall < 0.0f))
{
pCalcuData->IodisParall = 0.0f;
}
同样的处理应用到 Iodis、IoctrlParall、IoctrlParallcomm 三处。电压类采样(输出电压等)的钳位保持不变------电压不存在负值的合法场景。 修改后重新编译下载:
- 源模式:空闲时的小负偏移仍被钳到 0,显示干净;
- 载模式:负电流正常显示,源/载自动判别恢复工作。
四、经验总结
- "传过来就是 0" ≠ 发送方的问题。 接收端的换算、钳位、滤波单元同样会"制造"0。判断时一定要区分"原始值"和"处理后的值",在线调试 watch 对变量。
- 符号是有语义的,钳位前先问一句"这个量合法地取负值吗"。 电流在双向系统里天然带符号(源为正、载为负),电压/温度才适合无脑钳非负。给"带符号语义的量"加钳位,是最容易埋雷的地方。
- 钳位的副作用可能蔓延到依赖它的其他逻辑。 本例中源/载自动判别、功率计算、并机内阻计算都依赖电流的符号。改一个数的取值范围,要 grep 一遍它的所有消费者。
- 新旧版本 diff 是定位回归问题最快的路径。 这次从"看到现象"到"锁定根因"只花了很短时间,靠的就是对相邻版本做逐文件 diff,把排查范围从几十万行代码压缩到 4 个文件。
- 联调现象要和版本改动对齐着看。 "源模式正常、载模式为 0"这个不对称现象本身就在暗示:问题出在符号处理上,而不是数据通路上。
