4. FOC控制原理与电流环设计

ADC采样,说白了就是FOC控制系统的“眼睛”。你想想看,电机转得再快、算法算得再准,如果电流电压采回来就是错的,那后面全是白搭。我在刚接触C2000的时候,就吃过这个亏——PWM波形看着漂亮,电机却嗡嗡响,查了两天才发现是ADC触发时序没对齐。

这一章,我们就来把ADC的底细摸清楚。从SOC配置到过采样,从触发源到校准,我会把项目中踩过的坑、总结的经验,一股脑儿倒出来。

4.1 ADC模块的核心原理

C2000的ADC模块,本质上是一个逐次逼近型(SAR)转换器。它不像Δ-Σ型那样慢,也不像Flash型那样贵,刚好在速度和精度之间找到了平衡点。

我个人习惯把ADC的工作流程拆成三步:

  1. 采样保持:模拟信号被采样电容“抓住”
  2. 逐次比较:内部DAC逐位逼近,找到数字值
  3. 结果存储:转换结果写入结果寄存器

这里有个关键参数——采样窗口。窗口太短,电容充不满,采样值偏小;窗口太长,又浪费转换时间。我一般建议在FOC应用里,采样窗口设为6-8个ADC时钟周期,具体要看输入阻抗。

核心要点: C2000的ADC是12位精度,但实际有效位数(ENOB)受噪声和采样窗口影响,通常只能到10.5-11位。别被数据手册的12位忽悠了。

4.2 SOC配置:启动转换的“扳机”

SOC,全称Start-of-Conversion,就是告诉ADC“可以开始干活了”的信号。C2000的SOC机制很灵活,每个ADC模块有多个SOC通道,每个通道可以独立配置触发源和采样通道。

我记得第一次配置SOC时,被那堆寄存器搞得头大。后来总结了一个口诀:“SOCx = 触发源 + 采样通道 + 采样窗口”。就这么简单。

SOC通道 触发源 采样通道 采样窗口
SOC0 PWM1_ADCSOCA ADCINA0 7个周期
SOC1 PWM1_ADCSOCB ADCINA1 7个周期
SOC2 CPU定时器0 ADCINB0 10个周期

配置代码也很直接:

// 配置SOC0:PWM触发,采样通道A0,窗口7个周期
AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 4;  // PWM1_ADCSOCA
AdcRegs.ADCSOC0CTL.bit.CHSEL = 0;    // ADCINA0
AdcRegs.ADCSOC0CTL.bit.ACQPS = 6;    // 采样窗口 = 7个周期

小技巧: 如果你用多个SOC通道,记得把优先级高的通道放在前面。C2000的SOC优先级是编号越小越优先,SOC0最高。

4.3 PWM触发ADC采样:FOC的“心跳”

在FOC控制里,ADC采样必须和PWM同步。为什么?因为电流采样需要在PWM载波的特定时刻进行,才能避开开关噪声。

我习惯的做法是:在PWM载波的谷底或峰值触发ADC。这样采样时刻刚好是开关管切换的“安静期”,噪声最小。

具体配置分两步:

  1. PWM模块:设置EPWM1的SOC触发事件,比如CTR=ZERO(载波归零时触发)
  2. ADC模块:将SOC的触发源指向对应的PWM事件
// PWM配置:在载波归零时产生ADCSOCA信号
EPwm1Regs.ETSEL.bit.SOCAEN = 1;      // 使能SOCA
EPwm1Regs.ETSEL.bit.SOCASEL = 1;     // CTR=ZERO时触发
EPwm1Regs.ETPS.bit.SOCAPRD = 1;      // 每个事件触发一次

这里有个坑——触发延迟。从PWM事件产生到ADC真正开始采样,中间有几十纳秒的延迟。如果你用高频PWM(比如100kHz),这个延迟就不能忽略。我曾经在一个200kHz的PWM项目里,因为没算延迟,电流采样总是偏在开关噪声上,电机抖得像筛子。

避坑指南: 我曾经在PWM频率超过80kHz时,发现ADC采样值波动很大。后来用示波器一量,发现ADC触发信号和开关管切换刚好重叠。解决办法是把触发点从CTR=ZERO改成CTR=PRD,或者加一个小的延迟偏移。

4.4 过采样技术:用时间换精度

过采样,说白了就是“多采几次,取平均”。C2000的ADC硬件支持过采样,不需要CPU干预。

为什么需要过采样?因为单次采样的噪声太大。尤其是在电机控制环境里,IGBT开关、PWM斩波、电机反电动势,到处都是噪声源。过采样可以把这些噪声平均掉,提高信噪比。

C2000的过采样机制是这样的:

  • 硬件自动对同一个通道连续采样N次
  • 累加后右移(相当于除以N)
  • 结果存入结果寄存器

我一般用4倍或8倍过采样。再高就没意义了,因为噪声已经被平均得差不多了,反而浪费转换时间。

过采样倍数 有效位数提升 转换时间开销 推荐场景
2x +0.5位 2倍 一般电流采样
4x +1.0位 4倍 相电流采样
8x +1.5位 8倍 母线电压采样
16x +2.0位 16倍 温度/慢速信号

配置代码示例:

// 使能过采样,4倍过采样
AdcRegs.ADCCTL2.bit.RESOLUTION = 0;  // 12位模式
AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; // 中断在转换结束后产生
AdcRegs.ADCSOC0CTL.bit.ACQPS = 6;    // 采样窗口

// 过采样配置(以4倍为例)
AdcRegs.ADCOVERSAMPLE.bit.OVERSAMPLE = 2; // 4倍过采样

经验之谈: 过采样不是万能的。如果你的噪声是低频漂移(比如温度引起的),过采样反而会“平均”掉有用信号。我一般先用示波器看噪声频谱,再决定要不要用过采样。

4.5 采样数据的读取与校准

数据读回来了,但直接拿来用?别急,还有两步要走:读取和校准。

4.5.1 数据读取方式

C2000提供两种读取方式:

  • 轮询:循环检查ADC中断标志位,简单但浪费CPU
  • 中断:转换完成后自动触发中断,效率高

我强烈建议用中断方式。FOC控制里,CPU本来就忙得够呛,别再让它傻等了。

// 中断方式读取
interrupt void adc_isr(void)
{
    // 读取结果
    uint16_t ia = AdcResult.ADCRESULT0;
    uint16_t ib = AdcResult.ADCRESULT1;
    
    // 转换为实际电流值
    float ia_amp = (ia * 3.3 / 4096.0 - 1.65) / gain;
    
    // 清除中断标志
    AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

4.5.2 校准:别信“出厂值”

C2000的ADC出厂时做过校准,但那个校准值只是“大概准”。如果你要精度,必须自己做校准。

我一般做两步校准:

  1. 偏移校准:输入接地,读到的值应该是0,但实际会有偏移。把这个偏移值记下来,后续采样值减去它。
  2. 增益校准:输入一个已知电压(比如1.65V),读到的值应该对应2048,但实际会有偏差。计算一个增益系数,后续采样值乘以它。

校准代码示例:

// 偏移校准
#define ADC_OFFSET  12    // 实测偏移值
#define ADC_GAIN    1.002 // 实测增益系数

uint16_t adc_calibrated_read(uint16_t raw)
{
    float calibrated = (raw - ADC_OFFSET) * ADC_GAIN;
    if(calibrated < 0) calibrated = 0;
    if(calibrated > 4095) calibrated = 4095;
    return (uint16_t)calibrated;
}

注意: 校准值会随温度变化。如果你做的是工业级产品,建议在-40°C到85°C范围内做温度补偿。我曾经有一个项目,冬天调试好好的,夏天批量生产时电流采样全偏了,就是因为没做温度校准。

4.6 本章知识体系总览

下面这张图,把ADC采样与触发的核心流程串起来了。你可以把它当作一个“检查清单”,配置时对着走,不容易漏。

ADC采样与触发知识体系 PWM触发 CPU定时器触发 软件触发 SOC配置:触发源选择 + 采样通道 + 采样窗口 单次采样 过采样(4x/8x/16x) 连续转换 数据读取:轮询模式 / 中断模式 偏移校准 增益校准 提示:配置顺序建议 触发源 → SOC → 采样方式 → 读取 → 校准,不要跳步

嗯,这一章的内容就到这里。ADC采样看起来是基础,但基础不牢,地动山摇。我见过太多工程师在ADC上翻车,最后查来查去,发现就是触发时序没配好,或者校准没做。希望你能把这些经验带走,少走弯路。