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,让数据自己跑。
配置思路:
- 两个ADC的转换结果分别对应DMA的两个通道
- DMA设置为循环模式,自动搬运到数组
- 每次转换完成触发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。
时序图如下:
优化要点:
- 采样窗口宽度:确保ADC采样时间 + 转换时间 < 下桥臂导通时间。如果占空比接近0%或100%,窗口会变得很窄,需要特殊处理。
- 触发延迟:从PWM事件到ADC实际开始采样,有硬件延迟。我习惯在定时器比较值里提前几个时钟周期触发,补偿这个延迟。
- 双采样策略:在PWM周期的两个谷底各采一次,取平均,可以抑制开关噪声。代价是计算量翻倍,低功耗场景要权衡。
实战建议:采样时序的最终验证要靠示波器。把PWM输出和ADC触发信号同时抓出来,看采样点是否落在下桥臂导通的中间区域。我曾经在一个项目里发现采样点刚好落在开关边沿,噪声尖峰直接灌进ADC,电流波形全是毛刺。把触发点往后挪了2μs,世界清净了。
3.5 本章小结
电流采样是FOC的感知层,ADC配置是感知层的核心。咱们今天讲了:
- 三电阻采样的原理和选型
- GD32双ADC同步采样的配置方法
- DMA传输让数据自动流转
- 采样时序优化的关键点和坑