4、ADC采样与电流检测:双电阻采样原理、ADC注入组配置、同步触发模式、电流重构算法、采样偏移校准
电流检测是FOC的「眼睛」。你想想看,如果连电机三相电流都读不准,后面的Clark变换、Park变换全是白搭。这一章我们就把ADC采样这块硬骨头啃下来。
4.1 双电阻采样原理
为什么用双电阻,而不是三电阻?说白了,成本与精度的平衡。三电阻采样需要三个电流传感器,双电阻只用在两相下桥臂串采样电阻,第三相电流通过基尔霍夫定律算出来:IC = -(IA + IB)。
我在项目中遇到过一个问题:双电阻采样对PWM占空比有「盲区」。当某一相占空比接近0%或100%时,采样窗口太短,ADC根本来不及转换。这时候就需要电流重构算法来补位。
| 采样方式 | 电阻数量 | 优点 | 缺点 |
|---|---|---|---|
| 双电阻 | 2 | 成本低,计算量小 | 存在采样盲区 |
| 三电阻 | 3 | 无盲区,精度高 | 成本高,PCB面积大 |
| 单电阻 | 1 | 成本最低 | 重构算法复杂,低速性能差 |
4.2 ADC注入组配置
GD32的ADC有个好东西叫「注入组」。常规的规则组用于连续采样,而注入组可以「插队」——优先级更高,适合在PWM中断里抢时间采样。
我个人习惯这样配置注入组:
- 使能ADC时钟,配置分频系数(一般ADC时钟不要超过14MHz)
- 设置注入组通道数为2(对应A相和B相电流)
- 配置采样时间:我一般设7.5个周期,够稳
- 使能注入组中断,用于读取结果
// 注入组配置示例
adc_insermode_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_0, ADC_SAMPLETIME_7POINT5);
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_1, ADC_SAMPLETIME_7POINT5);
adc_external_trigger_inserted_config(ADC0, ENABLE, ADC_EXT_TRIGGER_INSERTED_T0TRGOUT);
adc_interrupt_enable(ADC0, ADC_INT_INSERTED);
4.3 同步触发模式
ADC采样必须和PWM同步,否则采到的电流值毫无意义。GD32的定时器可以输出TRGO信号,直接触发ADC注入组转换。
为什么会这样?因为FOC需要在PWM周期的中点采样电流,此时相电流最稳定,纹波最小。
我建议的配置流程:
- 定时器0配置为主模式,输出TRGO信号
- TRGO触发源选择为「更新事件」或「OC1REF」
- ADC0配置为外部触发注入组模式,触发源选择T0TRGOUT
// 定时器TRGO配置
timer_master_slave_mode_config(TIMER0, TIMER_MASTER_SLAVE_MODE_ENABLE);
timer_master_output_trigger_source_select(TIMER0, TIMER_MASTER_UPDATE);
4.4 电流重构算法
前面提到双电阻有采样盲区,怎么解决?靠重构算法。
当某一相占空比太小时,该相电流无法采样。这时候我们利用另外两相电流和三相电流之和为零的特性,反推出缺失的那一相。
我曾经踩过一个坑:重构算法在低速大扭矩时频繁触发,导致电流波形畸变。后来发现是采样窗口设置太短,ADC转换没完成就进入了重构分支。
重构算法的核心逻辑:
- 判断当前扇区(根据SVPWM的扇区号)
- 确定哪一相电流无法采样
- 用另外两相电流计算缺失相:Imiss = -(Ia + Ib)
- 如果两相都无法采样(极端情况),保持上一次采样值
// 电流重构伪代码
if (sector == 1 || sector == 4) {
// A相无法采样
I_a = -(I_b + I_c);
} else if (sector == 2 || sector == 5) {
// B相无法采样
I_b = -(I_a + I_c);
} else {
// C相无法采样
I_c = -(I_a + I_b);
}
4.5 采样偏移校准
ADC采样有个「零漂」问题。即使电机不转,ADC读到的电流值也不是0,而是几十到几百个LSB的偏移。这个偏移如果不校准,FOC会认为电机一直在转,导致失控。
校准方法很简单:电机静止时,连续采样N次(我一般取100次),取平均值作为偏移量。然后在每次采样结果中减去这个偏移量。
// 偏移校准示例
#define CALIBRATION_SAMPLES 100
uint16_t offset_a = 0, offset_b = 0;
void current_calibration(void) {
uint32_t sum_a = 0, sum_b = 0;
for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
sum_a += adc_inserted_read(ADC0, 0);
sum_b += adc_inserted_read(ADC0, 1);
}
offset_a = sum_a / CALIBRATION_SAMPLES;
offset_b = sum_b / CALIBRATION_SAMPLES;
}
// 使用时
int32_t current_a = (int32_t)adc_value_a - (int32_t)offset_a;
4.6 本章知识结构图
下面这张图把ADC采样与电流检测的完整链路串起来了,从硬件采样到软件重构,一目了然。
4.7 常见问题与避坑
- 采样值跳变:检查ADC参考电压是否稳定,VREF引脚加104电容
- 重构算法频繁触发:调整盲区阈值,或者增加采样窗口时间
- 偏移校准后仍有偏差:可能是运放电路失调,需要硬件上做偏置调整
- ADC转换结果始终为0:检查触发信号是否到达,用示波器量TRGO引脚