做电机控制和数字电源的工程师,对STM32G474这颗芯片都不会陌生。相比早先的F1/F4系列,G474在模拟外设上做了大量增强,集成多个ADC、运放、比较器等,这让它在电流环、电压环这类应用里非常顺手。而ADC单端输入和差分输入的选择,看似只在CubeMX里改一个下拉框、或者设一个寄存器位,实际对电压测量的量程、精度和抗干扰能力影响非常大。这篇文章就围绕“用G474做电压测量”这件事,聊聊这两种模式在硬件电路、代码配置和实测数据上的差异。如果你正在调电机驱动、数字电源或传感器采样的项目,这篇内容应该能帮你省下不少调试时间。
1. 先把G474的ADC家底摸清楚
1.1 和F1/F4相比,G4的ADC到底强在哪
很多从F103、F407切到G474的人,第一次用ADC会有点不习惯:CubeMX里多了一堆以前没见过的选项,什么Differential、Oversampling、Offset Calibration,甚至还有Linear Calibration。这不是G4在堆配置项,而是它的ADC模块确实换了一套更接近工业控制需求的设计。
G474内部有多个独立的ADC模块,可以各自独立采样,也可以组合成同步采样模式。这一点对电机控制特别重要:三相电流需要尽量同时采集,如果用单ADC轮询,三个通道之间存在时间差,电流波形有相位误差,算出来的角度和力矩就会有偏差。G4的多ADC结构配合DMA,可以做到多通道近乎同步的采集。
更关键的变化是三点:一是支持差分输入,这是F1/F4时代基本没有的(少部分F373系列支持);二是内置硬件过采样,可以把12位ADC等效扩展成16位精度;三是ADC可以配合内置运放(OPAMP)和比较器,构成完整的模拟信号链。也就是说,信号从传感器出来,经过内部运放放大,再进到ADC,全程在芯片内部完成,不用在外部电路堆料。这三个特性叠加起来,才让G474敢说自己是为电机控制和数字电源而生的。
1.2 单端输入:测的就是“相对地的电压”
单端输入是大家最熟悉的玩法。ADC引脚上施加的电压,相对于VREF-(通常是VSSA,也就是地)进行转换。G474的ADC分辨率默认最高12位,如果VDDA是3.3V,那满量程就是0到3.3V,对应的数字量是0到4095。每一个LSB对应的电压是3.3/4096,大约0.806mV。
单端模式的特点就一句话:简单直接,但是怕地噪声。因为它测量的是“引脚对地”的电压,地平面的噪声会直接叠加到测量结果里。如果信号源离MCU比较远,或者地线上有较大的电流回流,噪声就会串进来。比如用长线接一个电位器到ADC引脚,线越长、阻抗越高,地噪声和电磁干扰就越明显。
在实际项目中,单端模式主要用于这些场景:电池电压监测、通过分压电阻测母线电压、温度传感器输出、电位器/电位计位置检测。共同点是信号源本身是以地为参考的电压信号,且不要求很高的噪声抑制能力。
1.3 差分输入:测的是“两线之间的电压差”
差分输入在G4上是通过两个引脚配合来实现的,一个正输入(ADC_INP),一个负输入(ADC_INN)。ADC转换的结果是这两个引脚之间的电压差,而不是对地电压。这就带来了两个非常实用的好处。
第一个好处是能测负压差。单端输入只能测0到正电压,而差分输入的两个引脚谁高谁低无所谓,差值可正可负。比如用一个采样电阻做电流检测,电流方向不同时采样电阻两端的电压是反向的,用差分模式就能直接读出正负电流。
第二个好处是共模噪声抑制。假设两个引脚上都叠加了同幅度的干扰,比如电源波动、地弹等,计算差值时这部分干扰就会被抵消。这就是为什么在电机驱动、工业现场这种电磁环境比较恶劣的场合,差分输入更受青睐。
不过差分输入也不是无脑选。它有一个容易被忽视的约束条件:两个输入引脚各自的电压不能超出ADC的输入共模范围。也就是说,虽然我们关心的是差值,但每一根引脚对地的电压都必须落在允许范围内(通常介于0和VDDA之间,实际范围需要查数据手册)。如果共模电压过高或过低,即使差值很小,ADC也会出现饱和或非线性失真。
1.4 单端和差分,一张表看懂选择逻辑
| 对比维度 | 单端输入 | 差分输入 |
|---|---|---|
| 硬件引脚 | 1个引脚 | 2个引脚(INP、INN) |
| 测量对象 | 引脚对地电压 | 两引脚之间的压差 |
| 量程方向 | 0 ~ VREF+ | 可正可负,取决于差值 |
| 抗共模噪声 | 弱,地噪声直接影响 | 强,共模干扰被抵消 |
| 信号参考 | 必须与MCU共地 | 不严格要求共地 |
| 输入阻抗要求 | 相对宽松 | 两输入源阻抗最好对称 |
| 典型应用 | 母压、电池、电位器 | 采样电阻电流、电桥、差分传感器 |
| 配置复杂度 | 简单 | 需要关注共模范围和校准 |
这张表我建议收藏一下,画原理图之前先对照一遍,很多设计问题其实在选型阶段就能避免。
2. 硬件电路怎么设计:模式选错,后面全是坑
2.1 单端模式的电路设计要点
单端输入最经典的电路就是电阻分压。比如测直流母线电压,几百伏的电压先用两个电阻分压,缩放到ADC允许的0到3.3V范围,再接一个RC低通滤波器。分压电阻的精度直接决定了测量误差,所以选型时优先用1%甚至0.1%精度的电阻,温漂系数也要看应用场景,工业产品一般选25ppm或更低。
RC低通滤波的截止频率要算清楚。假设分压电阻是1MΩ和7.5kΩ,等效内阻接近7.4kΩ,如果并联一个1nF电容,截止频率大概是1/(2π×7.4k×1nF),约21.5kHz。这个带宽对常见的母线电压采样已经足够,还能滤掉一部分开关噪声。但要注意,源阻抗越大,ADC采样瞬间给内部采样电容充电就越慢,如果采样时间不够,采到的电压会偏低。这个在下文采样时间计算部分会详细展开。
另外一个很多人忽略的点:如果信号源内阻在几十千欧以上,最好加一级电压跟随器(运放缓冲)。运放输入阻抗极高,输出阻抗极低,能保证ADC前端的阻抗足够小,采样结果更稳定。G474内部自带OPAMP,可以把这部分电路收进芯片里,外部只需要配几个电阻电容。
2.2 差分模式的电路设计要点
用差分模式测采样电阻两端的压降,是电机控制里最常见的做法。这里有几个硬件层面的细节,比单端模式要讲究得多。
第一,两根差分线要从采样电阻两端独立引出,不要共用一根线接到某个公共接地点再分开。要直接以“开尔文连接”(Kelvin connection)的方式,把采样电阻的两端分别引到ADC的正负输入引脚。因为电流返回路径上的压降会被差分测量“算进去”,如果走线走错了,测量结果就不是采样电阻上的真实压降。
第二,正负两个输入路径上的阻抗要尽量对称。差分ADC内部采样时,两个输入端都要给采样电容充电,如果一边串了大电阻一边没有,会造成采样不对称,导致测量误差。所以差分输入线的串联电阻、滤波电容都要成对出现,数值保持一致。
第三,两个输入引脚对地的电压必须在共模范围内。比如给电机控制板供电后,电源地的电位可能有较大波动,如果采样电阻处于半桥电路的高压侧,其共模电压可能很高,不能直接接到ADC差分输入引脚,否则温度一上来就可能烧引脚。这种情况需要做电平位移或隔离,把共模电压拉回合适范围。
2.3 VREF+参考电压:你测的精度上限其实在这
不管单端还是差分,ADC测量的本质都是“输入电压与参考电压的比值”。G474数据手册里对VREF+引脚的要求是:必须接一个低噪声、低阻抗的参考电压源,通常在3.3V左右,并且要在靠近引脚的位置配退耦电容。不少人在这里翻车,VDDA和VREF+直接共用一个引脚、退耦电容随手一放,采集出来的数据在几百个LSB之间乱跳,以为是程序问题,查半天发现是参考电压纹波太大。
我的习惯是:VDDA和VREF+如果电压相同,至少也要用磁珠或小电阻隔开,并且分别在引脚附近放0.1μF陶瓷电容和1μF~2.2μF钽电容。如果对精度有更高要求,比如采集压力传感器、电桥信号,那就直接用外部基准源芯片,比如REF3033这类,给VREF+提供一个独立的低噪声参考。
2.4 两个典型场景:母线电压用单端,电流检测用差分
拿一个实际的电机驱动项目来举例。母线电压采样,通常是直流母线上分压后对地测量,信号天然以地为参考,用单端模式就够了。分压电阻把400V降到3.3V以内,经过RC滤波进ADC,读回数字量后按比例反算电压。这个场景如果用差分模式,反而多此一举,因为负输入引脚接哪里都别扭,还白白占用两个ADC通道。
相电流采样就不一样了。常见的方案是下桥臂串联采样电阻,采样电阻两端的压差可能只有几十毫伏,而且共模电压会随着开关动作剧烈变化。这时候用差分模式,配合G474内置的OPAMP先做放大,能有效抑制共模干扰,把小信号干净地送到ADC。负输入端可以直接接到采样电阻的端点,不用管它是“地上的一点”,只需保证共模电压在允许范围内即可。
所以选型的逻辑并不复杂:信号本来就是“对地”的,用单端;信号是“两线之差”的,或者处在较大的共模干扰环境里,用差分。
3. 代码实现:从CubeMX配置到HAL库实操
3.1 CubeMX里的通道配置与差分模式入口
用CubeMX配置G474的ADC其实很快,但有几个地方需要注意。打开ADC模块后,在Mode栏勾选需要的Channel,如果是支持差分输入的通道,下拉菜单里会有Single-ended和Differential Mode两个选项。选择Differential Mode之后,CubeMX会自动分配对用的负输入引脚,界面上也会显示出来。
接下来在Parameter Settings里,重点检查这几项:Resolution(12位)、Sampling Time(采样周期)、Continuous Conversion Mode(连续转换)、Overrun(溢出处理)。其中Sampling Time需要根据信号源阻抗来定,不要图快直接选最小值。CubeMX那里有一个下拉框可以直接选,默认值可能在6.5周期左右,实际应用中我一般至少选12.5或24.5周期,信号源阻抗大的时候甚至会选到47.5周期以上。
DMA配置那一步,记得选择Circular模式,数据宽度根据你定义的缓冲区类型来选,通常用Half Word(16位)或Word(32位)。因为G4的ADC数据寄存器是32位的,右对齐时数据存放在低16位,如果用字节宽度搬运,缓冲区里会错位得很厉害。很多人第一次用ADC+DMA时遇到的“数据全是乱的”,八成就是这里宽度没选对。
3.2 ADC初始化与校准代码(单端模式)
CubeMX生成代码后,核心的初始化函数是这个样子,我加了注释:
void MX_ADC1_Init(void) { hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; /* 分频,保证ADC时钟在允许范围内 */ hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc1.Init.LowPowerAutoWait = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.NbrOfConversion = 1; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; hadc1.Init.SamplingTimeCommon = ADC_SAMPLINGTIME_COMMON_1; HAL_ADC_Init(&hadc1); /* G4的ADC必须做校准,否则精度会受影响 */ HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED); }通道配置部分要注意,即便只采集一个通道,也要按规则组来配置:
void ADC1_Config_Channel(void) { ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_1; /* PA0,根据实际引脚选择 */ sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLINGTIME_47_5; /* 47.5个ADC时钟周期 */ sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; HAL_ADC_ConfigChannel(&hadc1, &sConfig); }启动转换和读取结果就很简单了:
HAL_ADC_Start(&hadc1); if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK) { uint32_t raw = HAL_ADC_GetValue(&hadc1); float voltage = raw * 3.3f / 4095.0f; /* 12位分辨率,换算成实际电压 */ }这里有个细节顺手提一下:HAL_ADC_GetValue返回的是uint32_t,但单端模式下右对齐数据就是12位有效值,直接换算即可。换算系数按4095还是4096取决于算法习惯,用4095能让满量程出电压正好等于VDDA,用4096更贴近理论转换公式。两者在12位下差异不超过0.1%,不要在这个问题上纠结。
3.3 差分模式代码:必须关心的DIFSEL与符号解析
在G4上,单端和差分模式是通过ADCx_DIFSEL寄存器来切换的,每一个bit对应一个通道,写入1表示该通道为差分输入,0表示单端输入。CubeMX勾选Differential Mode时,自动帮你把这个位设置了。手动操作时如果发现切了模式没生效,先检查这个寄存器有没有写对。
在HAL库中,配置差分通道的代码和单端差别就在SingleDiff这个字段:
void ADC1_Config_Channel_Diff(void) { ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_2; /* 以ADC2_INP2/ADC2_INN2为例 */ sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLINGTIME_47_5; sConfig.SingleDiff = ADC_DIFFERENTIAL_ENDED; /* 关键点:差分模式 */ sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; HAL_ADC_ConfigChannel(&hadc1, &sConfig); }读取方式和单端一样,但差分模式结果需要按有符号解析。以12位分辨率为例,差分转换结果以二进制补码表示,当正输入等于负输入时,转换结果约等于0对应的中间值(约2048),或者按带符号数看是0附近。具体地,把HAL_ADC_GetValue的返回值强转成int32_t或int16_t后再做判断,才能区分正负电压差。
uint32_t raw = HAL_ADC_GetValue(&hadc1); int32_t diff_signed = (int32_t)raw; /* 处理偏移:以中间值约2048为0点 */ float diff_voltage = (diff_signed - 2048.0f) * 3.3f / 4096.0f;要不要拆掉这个2048的中间偏移,取决于你配置的是“真差分”还是“偏移二进制”输出。G4手册里差分数据分两种格式,实际使用时最稳妥的办法是:先把两个输入引脚短接到同一个电压,读出原始值作为零点,再在代码里减去这个零点。这也算是做系统校零了。
3.4 采样时间到底怎么设:一个公式帮你算明白
采样时间设置得太短,最典型的现象是:用一个高内阻的信号源给ADC输入,采集出来的结果比实际电压偏低,而且随着温度变化还会漂。原因在于ADC内部采样电容在采样阶段需要充电,如果外部阻抗大,充电时间不够,电容上的电压还没来得及跟上输入信号,就被切断了。
G4系列手册中会给出采样电路等效参数,典型值大致为:内部采样电容几个皮法量级,内部开关电阻几百欧到几千欧。外部源阻抗越大,需要的采样时间越长,可以用下面这个公式估算:
其中N是ADC分辨率,R_source是外部信号源等效内阻,R_switch是ADC内部开关电阻,C_sample是采样电容。假如外部源阻抗10kΩ,内部开关电阻按2kΩ估算,采样电容按8pF估算,目标12位精度,算下来大约需要1微秒左右的采样时间。如果ADC时钟是60MHz,最小采样周期约16.7ns,1微秒差不多要60个周期,所以配置时选47.5周期是个很稳妥的值。这个计算结果和我的实际调试经验基本吻合。
当然,G474还有一个更省心的方案:外部源阻抗比较高的时候,直接启用内部OPAMP做缓冲,把高阻抗信号转换成低阻抗输出再进ADC。这样采样周期完全可以用短档位,还能顺带放大微弱信号,一举两得。
3.5 DMA搬运数据与硬件过采样的配合
当采样频率高、或者要连续采集多个通道时,DMA基本是标配。启动DMA的代码很简单:
static uint32_t adc_buffer[64]; HAL_ADC_Start_DMA(&hadc1, adc_buffer, 64);DMA回调里处理数据即可:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc->Instance == ADC1) { /* 此时adc_buffer里是最近一次转换的数据 */ } }G4的硬件过采样是个很有意思的功能。在HAL库的Init结构体里,有Oversampling的相关字段,配置后可以把多次采样的结果自动累加并右移,等效分辨率从12位提高到16位。这个功能对低频信号测量特别友善,比如测母线电压、温度、电池电量,一帧数据里做16次过采样,噪声被压得很低。对于高频信号不建议滥用,因为过采样会占用大量ADC转换时间,实时性会被拖垮。
4. 实测数据对比:单端和差分在真实场景下的差异
4.1 我自己搭的测试方案
为了把两种模式的差异展现得直观一点,我搭了一套简单的测试环境:信号源用了一块高精度稳压源,输出直流电压;同时用一个信号发生器在共模线上注入一个固定频率的小干扰,模拟真实的工业噪声环境。G474的ADC1单端通道采集信号源对地电压,ADC2差分通道采集同一信号源经过两线传输后的差分电压。
因为没有专业屏蔽室,测试环境里本来就有一些开关电源、电机驱动器干扰,反而更贴近实际现场。记录方式很简单,每个条件下连续采样1024个点,算平均值和标准差。平均值反映准确度,标准差反映噪声水平。
4.2 直流电压测量:两者差异不大,但差分在高干扰下更稳
先看直流电压测试。信号源输出1.000V,分别用单端和差分模式采集,换算成电压后统计如下(测试数据经过多次重复采样后的典型结果):
| 测量条件 | 单端平均值 | 单端标准差 | 差分平均值 | 差分标准差 |
|---|---|---|---|---|
| 干净环境 | 1.0021V | 1.8mV | 1.0018V | 1.6mV |
| 注入2kHz干扰 | 1.0048V | 4.7mV | 1.0022V | 1.9mV |
可以看到,在干净环境下,单端和差分差异不大,都在1mV量级。但一旦注入共模干扰,单端测量数据的标准差翻了一倍以上,平均值也出现了明显偏差,而差分模式几乎不受影响。这说明在存在地电位波动、开关噪声的场合,差分输入的抗干扰优势是实打实的。
4.3 小信号测量:差分模式明显更占优
再把信号源调低到50mV,模拟采样电阻这种小信号场景。这时候单端模式测量结果受地噪声的影响被放大了,因为有用信号本来就小,噪声稍大一点就会淹没它。而差分模式因为那两个引脚的噪声是共模的,在减法的过程中被抵消,小信号能比较清晰地读出来。
| 输入信号 | 单端读数 | 差分读数 | 实际值 |
|---|---|---|---|
| 50mV | 55.3mV | 50.6mV | 50.0mV |
| 20mV | 24.1mV | 20.3mV | 20.0mV |
| -20mV | 无法正确读取 | -20.5mV | -20.0mV |
第三行特别说明问题:单端模式根本测不了负电压,输入为-20mV时读数会停在接近0的位置,而差分模式可以正确读出-20mV。这也是为什么双向电流检测必须用差分方案。
4.4 波形质量对比:从FFT看噪声底
把采集到的1024个点做FFT,可以更直观地看到两者的差异。单端模式下,频谱上50Hz、1kHz附近的噪声分量明显更高,而差分模式下这些频率的幅度明显下降,噪声底至少降低了10dB到15dB。如果你的产品需要过传导发射测试,或者在噪声环境里做高精度采样,差分模式的这个优势会帮你大大减轻后级软件滤波压力。
4.5 软件滤波:G4硬件过采样之外的另一个选择
差分模式和硬件过采样能解决大部分问题,但代码里保留一层软件滤波依然是成本最低的兜底手段。我常用的一个一阶低通滤波函数,写成C语言就几行:
uint32_t LowPass_Filter(uint32_t new_val, uint32_t last_val, uint8_t coeff) { /* coeff取0~255,越小滤波越强,响应也越慢 */ return (last_val + ((new_val - last_val) * coeff) / 256); }调用时把上一次滤波结果和当前ADC值传进去,每次更新即可。这个滤波器平移了相位,不适合直接放在电流环里,但在温控、电压显示这类对实时性要求不高的场景里非常适用。另外,在启用DMA时也可以利用G4的硬件均值:直接配置过采样,让内核少干活,这个我上一节说了,两个方案可以叠加,先用硬件过采样把信噪比做上去,再用软件滤波润色。
5. 避坑指南:这些坑我踩过,你不要再踩
5.1 切换单端/差分后忘了重新校准
G4的ADC校准分偏移校准和线性校准两类,HAL库中调用HAL_ADCEx_Calibration_Start时,第三个参数可以选择单端或差分模式。问题是,单端和差分模式下的偏置是不一样的,如果你之前用单端校准好的系数,然后直接用同一套设置去采差分信号,你会发现零点明显不对,最严重时能偏几十个LSB。
解决方法是:每次切换SingleDiff配置后,先做一次对应的校准再启动转换。校准本身耗时很短,在应用启动阶段完成即可:
HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_DIFFERENTIAL_ENDED);如果在实际产品里同时使用单端和差分通道,那就要在初始化流程里分别校准,并确保后续不随意切换模式。记住这条经验能帮你省掉很多“读数不对”的排查时间。
5.2 差分输入引脚接反,符号变成反的
差分输入是有方向的。ADC_INP接采样电阻的一端,ADC_INN接另一端,如果反了,采集结果的正负就反了。最常见的情况是电机电流方向判断错误,导致控制环直接发疯了。排查方法很简单:给系统一个已知方向的电流,比如让电机正转,看采样值符号是否正确。或者直接在板子上短接两个输入引脚(让差值归零),看读数是否回落到中间零点。
5.3 共模电压超出ADC允许范围,读数和真实值完全对不上
上一节提到过,差分模式要求两个输入引脚的电压都落在芯片允许的共模电压范围内。举个例子,在电机驱动中采样下桥臂电流,采样电阻位于功率地附近,工作正常时共模电压不高;但如果你把采样电阻放在上桥臂,共模电压可能直接超过VDDA,读数就会饱和,严重的还会损坏引脚。遇到这种测量值无论如何都异常的情况,先用万用表量一下两个输入引脚对地的直流电压,判断共模电压有没有超范围。
5.4 VREF+噪声大导致采集值跳动
采集值跳动但电路连接看起来没问题,很多人的第一反应是修改软件滤波,其实是VREF+引脚没处理好。G4的VREF+如果和VDDA直接用一个走线长、去耦差的电源,那么ADC内部的参考电压就会随负载波动,直接表现为读数抖动。解决办法是把VREF+单独通过磁珠接VDDA,引脚附近放0.1μF和2.2μF电容,PCB布局上紧贴MCU。如果你用外部基准芯片,要求就更高了,布局和地层处理都要跟上。
5.5 CubeMX生成的代码默认配置和预期不一致
CubeMX虽然能省事,但生成的ADC初始化代码是按最常见的配置走的,未必符合你的项目。比如默认采样时间可能较短,默认DMA数据宽度可能不合适。我习惯在生成代码后,把ADC初始化部分整体检查一遍,尤其是采样时间和数据对齐方式这两项。不要因为“CubeMX生成的就一定对”而跳过检查,这两个参数直接影响测量准确性,我见过太多因为不检查而踩坑的案例。
5.6 DMA中断和主循环共享缓冲区
用DMA搬运ADC数据时,如果主循环在主程序里直接读取DMA缓冲区,而DMA正在更新同一块缓冲区,就可能读到“一半新一半旧”的错位数据。标准做法是双缓冲,或者在DMA传输完成回调里处理数据,主循环只消费处理结果。G4的DMA支持双缓冲模式,配置好之后,两块缓冲区轮流接收ADC数据,CPU在其中一块数据稳定后才读取,有效避免数据撕裂问题。
5.7 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 差分模式下读数恒为满量程或零 | DIFSEL未设置、共模超范围 | 检查寄存器配置,量输入共模电压 |
| 单端采集值随采样时间变化 | 采样时间不足、源阻抗过大 | 加大采样周期,或加运放缓冲 |
| 切换模式后零点漂移明显 | 没有重新校准 | 重新执行HAL_ADCEx_Calibration_Start |
| 电流方向判断反了 | INP/INN接反 | 互换输入端或软件修正符号 |
| 采集值跳动剧烈 | VREF+噪声大、接地不好 | 增加退耦电容,检查参考电压 |
| DMA数据错位 | 数据位宽配置错误 | 检查DMA宽度和缓冲区对齐 |
| 过采样后数据不对 | 过采样右移位数配置错误 | 核对右移位数与累加次数 |
说实话,ADC单端和差分模式的选择本身不难,难的是背后那整套“从硬件到代码”的配合。我踩过最深的坑就是图上差分模式很兴奋,结果共模电压没考虑,板子一上电读数就飘;也见过同事拿单端模式去测采样电阻两端的微小压差,被地噪声折磨了好几天。G474是一颗模拟外设非常强的芯片,但它的强是留给用好这些外设的人的。
如果你现在正要画板子或者调代码,我给个最直接的建议:先确认信号的参考点在哪,再决定用什么模式。信号以地为参考就单端,信号是两线之差就差分,不要为了追求高级就盲目选差分。差分模式下,优先检查共模电压是否合法,跑通后第一时间做校零和校准。把这个流程走顺了,G474的ADC精度会给你很大信心。