3、电流采样与ADC配置:三相电流采样原理、GD32 ADC多通道同步采样、DMA传输配置、采样时序优化

好,咱们进入第三章。这一章讲的是FOC控制里最关键的感知环节——电流采样。你想想看,FOC的核心就是精确控制电流矢量,如果连电流都测不准,后面算法再漂亮也是白搭。我刚开始做电机驱动时,就吃过采样时序的亏,波形乱七八糟,查了三天才发现是ADC触发时刻没算对。嗯,咱们今天就把这块彻底讲透。

3.1 三相电流采样原理

FOC需要知道电机三相绕组的瞬时电流。但实际硬件上,我们通常只采样两相,第三相通过基尔霍夫电流定律算出来:I_c = -(I_a + I_b)。为什么只采两相?省一个ADC通道,省一个运放,省PCB面积,对低功耗设计尤其重要。

采样电阻的位置有讲究。常见方案有两种:

方案 位置 优点 缺点
三电阻采样 下桥臂MOSFET源极与GND之间 成本低,适合低压 占空比接近100%时采样窗口窄
单电阻采样 直流母线负端 极低成本,省空间 重构算法复杂,低速性能差
霍尔/磁通门 相线上 隔离,无损耗 贵,体积大

咱们这个课程用的是三电阻采样。为什么?因为GD32的ADC够快,三电阻方案在低功耗场景下更容易优化采样窗口。我在一个风机项目里用过单电阻,低速时电流重构失败率很高,后来老老实实换回三电阻,问题一次解决。

关键点:采样电阻的阻值选择要平衡功耗和信噪比。1mΩ到10mΩ是常见范围。阻值太大,发热严重;太小,信号幅度低,容易被噪声淹没。我个人习惯先按R = V_sense_max / I_peak估算,V_sense_max通常取100~200mV。

3.2 GD32 ADC多通道同步采样

GD32的ADC模块支持多种工作模式。咱们需要的是同步采样——在同一个触发时刻,同时采集两相电流。为什么必须同步?因为FOC的电流环计算需要同一时刻的电流值,如果分时采样,转子位置已经变了,算出来的矢量就是错的。

GD32的ADC可以配置为规则组+注入组,或者使用双ADC模式(如果芯片有双ADC)。对于GD32F303系列,我推荐用ADC0和ADC1的同步规则模式:

  • ADC0采样A相电流,ADC1采样B相电流
  • 两个ADC使用同一个外部触发源(定时器PWM事件)
  • 采样时间保持一致

配置代码片段如下:

// 使能ADC时钟
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_ADC1);

// ADC模式配置:独立模式,但同步触发
adc_mode_config(ADC_MODE_FREE);

// 配置ADC0
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(ADC0, REGULAR_CHANNEL, 1);

// 配置ADC1,与ADC0相同参数
adc_special_function_config(ADC1, ADC_SCAN_MODE, ENABLE);
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
adc_channel_length_config(ADC1, REGULAR_CHANNEL, 1);

// 设置采样时间:55.5个周期,保证信号稳定
adc_sample_time_config(ADC0, ADC_CHANNEL_0, ADC_SAMPLETIME_55POINT5);
adc_sample_time_config(ADC1, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);

// 外部触发源:定时器0的通道0输出
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T0_CH0);
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T0_CH0);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);

// 使能ADC
adc_enable(ADC0);
adc_calibration(ADC0);
adc_enable(ADC1);
adc_calibration(ADC1);

小技巧:采样时间别设太短。我见过有人为了追求速度设成1.5个周期,结果采出来的值跳得像心电图。对于电机电流这种低频信号,55.5个周期完全够用,还能顺便滤掉一些高频毛刺。

3.3 DMA传输配置

ADC转换结果怎么搬到内存?用中断一个一个读?那CPU就别干别的了。咱们用DMA,让数据自己跑。

配置思路:

  1. 两个ADC的转换结果分别对应DMA的两个通道
  2. DMA设置为循环模式,自动搬运到数组
  3. 每次转换完成触发DMA请求,不占用CPU

代码示例:

// DMA通道配置
dma_parameter_struct dma_init_struct;

// ADC0 -> DMA0通道0
dma_deinit(DMA0, DMA_CH0);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)&adc_value[0];
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = 1;
dma_init_struct.periph_addr = (uint32_t)&ADC_RDATA(ADC0);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPH_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_init_struct);
dma_circulation_enable(DMA0, DMA_CH0);
dma_channel_enable(DMA0, DMA_CH0, ENABLE);

// ADC1 -> DMA0通道1,配置类似,略
// 注意:ADC1的数据寄存器地址是 ADC_RDATA(ADC1)

// 使能DMA传输完成中断(可选,用于同步)
dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_FTF);

DMA配置好之后,ADC每次转换完,数据自动写入adc_value[0]和adc_value[1]。主循环里直接读这两个变量就行,零等待。

曾经踩过的坑:DMA的memory地址一定要是全局变量或者静态变量,不能是局部变量。局部变量在栈上,地址每次调用都可能变,DMA搬着搬着就飞了。我调试时发现电流值偶尔跳变,查了半天,就是这里的问题。

3.4 采样时序优化

这是本章的重头戏。采样时序没优化好,FOC性能直接打五折。

核心问题:什么时候触发ADC采样?

对于三电阻采样,采样窗口在下桥臂导通期间。PWM周期里,下桥臂导通时,电流流过采样电阻,此时采样最准确。如果在上桥臂导通时采样,采样电阻被短路,读到的就是0。

所以触发时机要选在PWM的中心对齐模式的计数谷底或峰值附近。我一般用定时器更新事件或通道比较事件来触发ADC。

时序图如下:

三相电流采样时序优化示意图 PWM三角载波(中心对齐) 下桥导通 下桥导通 下桥导通 下桥导通 采样点 采样点 采样点 采样点 采样点位于三角载波谷底,此时下桥臂导通稳定,电流信号最干净

优化要点:

  • 采样窗口宽度:确保ADC采样时间 + 转换时间 < 下桥臂导通时间。如果占空比接近0%或100%,窗口会变得很窄,需要特殊处理。
  • 触发延迟:从PWM事件到ADC实际开始采样,有硬件延迟。我习惯在定时器比较值里提前几个时钟周期触发,补偿这个延迟。
  • 双采样策略:在PWM周期的两个谷底各采一次,取平均,可以抑制开关噪声。代价是计算量翻倍,低功耗场景要权衡。

实战建议:采样时序的最终验证要靠示波器。把PWM输出和ADC触发信号同时抓出来,看采样点是否落在下桥臂导通的中间区域。我曾经在一个项目里发现采样点刚好落在开关边沿,噪声尖峰直接灌进ADC,电流波形全是毛刺。把触发点往后挪了2μs,世界清净了。

3.5 本章小结

电流采样是FOC的感知层,ADC配置是感知层的核心。咱们今天讲了:

  • 三电阻采样的原理和选型
  • GD32双ADC同步采样的配置方法
  • DMA传输让数据自动流转
  • 采样时序优化的关键点和坑