4、电流采样与ADC配置:电流检测电阻、运放电路、STM32 ADC多通道采样、DMA传输

电机控制里,电流采样是闭环控制的基础。说白了,你PID算得再准,电流采回来是错的,那一切都是白搭。我刚开始做FOC的时候,就吃过这个亏——电流波形看着挺好,电机一转起来就抖,查了半天,结果是ADC采样时序没配好。

这一章,咱们就把电流采样这条链路从头到尾捋一遍。从硬件上的检测电阻和运放,到STM32的ADC多通道配置,再到DMA传输,一步都不能省。

4.1 电流检测电阻与运放电路

先说说硬件端。电流采样最常用的方式就是串联一个毫欧级电阻,然后通过运放把电阻两端的微弱电压放大到ADC能采的范围。

4.1.1 检测电阻选型

检测电阻的选择有几个关键点:

  • 阻值:一般在1mΩ~50mΩ之间。阻值越大,信号越强,但功耗也大。我一般控制在额定电流下压降不超过50mV。
  • 功率:要留够余量。比如电机峰值电流10A,用10mΩ电阻,峰值功耗就是1W,选2W以上的比较稳妥。
  • 温漂:尽量选低温度系数的,比如±50ppm/℃以下。温漂大了,电流采样会随温度飘,你想想看,PID参数调好了,跑一会儿电机热了,电流值就变了,那还怎么控?
我的习惯: 采样电阻尽量用四端开尔文接法,把电流回路和电压检测回路分开,能有效避免PCB走线电阻引入的误差。

4.1.2 运放电路设计

运放的作用是把差分小信号放大到ADC的输入范围(通常是0~3.3V)。常用的电路是差分放大或仪表放大。

这里有个坑——运放的共模输入范围。电机驱动是高压环境,母线电压可能几十伏,而采样电阻两端的共模电压就是电机相电压。如果运放的共模输入范围不够,输出就会失真。

我曾经踩过的坑: 选了一款看似不错的运放,共模范围只到VCC-1.5V,结果在电机低速重载时,相电压接近母线,运放直接饱和了,电流波形全乱套。后来换了轨到轨输入的运放才解决。

运放电路的关键参数:

参数 推荐值 说明
增益 10~100倍 根据采样电阻和ADC范围计算
带宽 >1MHz PWM频率通常10~20kHz,带宽要够
失调电压 <1mV 失调大了,低电流时误差很大
共模抑制比 >80dB 抑制共模干扰,保证差分信号质量

4.2 STM32 ADC多通道采样

硬件搭好了,接下来就是STM32的ADC配置。电机控制通常需要同时采集两相或三相电流,所以要用到多通道采样。

4.2.1 ADC硬件资源

STM32的ADC有几种模式:

  • 单次转换:每次触发只转一个通道,适合低速场景。
  • 连续转换:自动循环转换,但CPU负担重。
  • 扫描模式:一次触发转换多个通道,配合DMA使用最合适。

电机控制里,我一般用规则组扫描模式,把三相电流的ADC通道放在一个规则组里,一次触发就把所有电流都采完。

4.2.2 采样时序与触发源

这里有个关键点——采样时刻。PWM开关管导通和关断时,会有很大的开关噪声。如果ADC在噪声大的时候采样,数据肯定不准。

我的做法是:用PWM的定时器作为ADC触发源,把采样点放在PWM周期的中间时刻。这时候开关管已经稳定了,噪声最小。

核心思路: ADC采样时刻 = PWM中心对齐模式下的计数器上溢或下溢时刻,避开开关动作瞬间。

配置示例(以STM32G4为例):

// 配置ADC1,规则组扫描模式,3个通道
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;      // 扫描模式
hadc1.Init.ContinuousConvMode = DISABLE;        // 不连续,由外部触发
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_TRGO; // TIM1触发
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;                 // 3个通道
HAL_ADC_Init(&hadc1);

4.3 DMA传输

ADC转换完的数据,如果不及时搬走,就会被下一次转换覆盖。用DMA是最优雅的方式——转换完成自动把数据搬到内存,CPU只管算PID就行。

4.3.1 DMA配置要点

DMA配置有几个地方要注意:

  • 传输方向:外设到内存,ADC是源,内存数组是目标。
  • 数据宽度:ADC是12位,但寄存器是16位或32位,对齐好就行。
  • 循环模式:开启,这样每次触发都会自动传输,不用反复配置。
  • 内存地址递增:开启,让数据依次存到数组的不同位置。
// DMA配置,把ADC数据搬到电流数组
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;        // 内存地址递增
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;             // 循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_adc1);

// 连接DMA和ADC
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

4.3.2 数据对齐与处理

DMA搬过来的数据是原始ADC值(0~4095),要转换成实际电流值。转换公式很简单:

// 电流计算
float current = (adc_value - offset) * vref / 4096.0f / gain / Rshunt;

// adc_value: DMA搬来的原始值
// offset: 零电流时的ADC值(中点偏置)
// vref: ADC参考电压,通常3.3V
// gain: 运放增益
// Rshunt: 采样电阻阻值
一个小技巧: offset值不是固定的,会随温度漂移。我一般在电机启动前先采100次取平均,作为当前offset。这样能有效消除温漂影响。

4.4 整体架构与流程图

把上面这些串起来,整个电流采样链路就是:

电流 → 采样电阻 → 运放放大 → STM32 ADC多通道采样 → DMA传输 → 内存数组 → 电流计算 → 送给PID

下面这张图把整个流程和关键节点都画出来了,你可以对照着看:

电流采样与ADC配置整体架构 电机相电流 Ia / Ib / Ic 采样电阻 1mΩ~50mΩ 运放电路 差分放大 10~100倍 ADC输入 0~3.3V PWM定时器触发 TIM1 TRGO ADC规则组扫描 3通道 12位 DMA循环传输 外设→内存 电流数组 adc_val[3] 电流计算 ADC值→实际电流 坐标变换 Clark / Park PID控制器 电流环调节 关键时序要求 ADC采样点必须在PWM中心时刻,避开开关噪声 DMA传输必须在下次ADC转换完成前结束,否则数据会被覆盖

4.5 常见问题与避坑指南

最后,分享几个我在实际项目中遇到的问题:

  • 采样噪声大:检查采样时刻是否在PWM开关噪声区,调整触发点。
  • 电流零点漂移:运放失调电压和温漂导致,用软件校准offset。
  • DMA数据错位:检查内存地址递增是否开启,通道顺序是否和DMA数组对应。
  • ADC转换时间不够:采样周期太短,适当降低ADC时钟或增加采样时间。
我曾经遇到的一个诡异问题: 电机低速时电流波形正常,高速时突然出现毛刺。查了两天才发现,是DMA传输速度跟不上ADC转换速度,数据被覆盖了。后来把DMA优先级调高,问题解决。

嗯,电流采样这块就讲到这里。硬件选型、运放设计、ADC配置、DMA传输,每一步都关系到最终的控制效果。你把这些吃透了,后面的PID整定才能建立在可靠的数据基础上。