2. ADC采样与电机电流检测

任务目标

在C2000 DSP(以TMS320F28379D为例)上配置ADC模块,实现与PWM定时器同步的电机相电流采样,并对采样结果进行软件滤波,为后续电流环控制提供稳定的电流反馈值。

硬件与依赖

  • 硬件:TMS320F28379D LaunchPad或最小系统板,电机驱动板(电流采样电阻+运放电路),直流电机或BLDC电机。
  • 电流采样信号接入ADCINA0(对应相电流U相)与ADCINA1(对应相电流V相),电压范围0~3.3V(需经运放偏置到合适范围)。
  • 开发环境:CCS 12.x + C2000Ware(含driverlib库)。

实现原理

电机相电流采样需与PWM载波同步,在PWM计数归零或周期匹配时刻触发ADC转换,避免开关噪声干扰。本案例使用EPWM1作为时基,在时基周期事件触发ADC转换,采样两路相电流,并采用一阶低通滤波(IIR)对原始采样值进行平滑处理。

关键完整代码

#include "driverlib.h"
#include "device.h"

#define ADC_SAMPLE_NUM  8       // 每次转换序列长度
#define FILTER_ALPHA    0.2f    // 滤波系数(0~1,越小越平滑)

volatile uint16_t adcRaw_U[ADC_SAMPLE_NUM];
volatile uint16_t adcRaw_V[ADC_SAMPLE_NUM];
volatile uint16_t adcIndex = 0;
volatile float currentU_Filtered = 0.0f;
volatile float currentV_Filtered = 0.0f;

// 电流换算系数:假设运放输出0~3.3V对应电流-10A~+10A
#define CURRENT_SCALE   (20.0f / 3.3f)
#define CURRENT_OFFSET  (-10.0f)

void initADC(void)
{
    // 初始化ADC-A,采用12位模式,信号源为SOC触发
    ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
    ADC_setInterruptPulse(ADCA_BASE, ADC_IRQ_END_OF_CONVERSION);
    ADC_enableConverter(ADCA_BASE);
    DEVICE_DELAY_US(1000);

    // 配置SOC0~SOC1:分别采样ADCINA0和ADCINA1
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER1, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN1);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER2, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER3, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN1);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER4, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER5, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN1);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER6, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0);
    ADC_setSOCConfig(ADCA_BASE, ADC_SOC_NUMBER7, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN1);

    // 设置采样窗口
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER0, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER1, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER2, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER3, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER4, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER5, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER6, ADC_ACQPS_15);
    ADC_setInterruptSOCConfig(ADCA_BASE, ADC_SOC_NUMBER7, ADC_ACQPS_15);

    // 使能ADC中断(每次转换序列完成后触发)
    ADC_enableInterrupt(ADCA_BASE, ADC_INT_NUMBER1);
    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
    ADC_setInterruptSource(ADCA_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER7);
    Interrupt_enable(INT_ADCA1);
}

__interrupt void adca1ISR(void)
{
    uint16_t i;
    uint32_t sumU = 0, sumV = 0;

    // 读取本次转换序列的8个结果(4次U相 + 4次V相)
    for(i = 0; i < ADC_SAMPLE_NUM; i++)
    {
        adcRaw_U[i] = ADC_readResult(ADCA_BASE, ADC_SOC_NUMBER0 + i*2);
        adcRaw_V[i] = ADC_readResult(ADCA_BASE, ADC_SOC_NUMBER1 + i*2);
        sumU += adcRaw_U[i];
        sumV += adcRaw_V[i];
    }

    // 先求平均,再做一阶低通滤波
    float avgU = (float)sumU / (ADC_SAMPLE_NUM/2);
    float avgV = (float)sumV / (ADC_SAMPLE_NUM/2);

    currentU_Filtered = FILTER_ALPHA * (avgU * CURRENT_SCALE + CURRENT_OFFSET) +
                        (1.0f - FILTER_ALPHA) * currentU_Filtered;
    currentV_Filtered = FILTER_ALPHA * (avgV * CURRENT_SCALE + CURRENT_OFFSET) +
                        (1.0f - FILTER_ALPHA) * currentV_Filtered;

    ADC_clearInterruptStatus(ADCA_BASE, ADC_INT_NUMBER1);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

void initEPWM1_ForADC(void)
{
    // 配置EPWM1为ADC触发源,频率20kHz(时基80MHz / 4000)
    SysCtl_setClock(SYSCTL_OSCSRC_XTAL);
    DEVICE_DELAY_US(100);

    EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);
    EPWM_setTimeBasePeriod(EPWM1_BASE, 4000);
    EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN);
    EPWM_setTimeBaseCounter(EPWM1_BASE, 0);

    // 在时基周期事件(PRD)触发ADC SOC
    EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
    EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBPRD);
    EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
}

void main(void)
{
    // 初始化设备与GPIO
    Device_init();
    Interrupt_initModule();
    Interrupt_initVectorTable();

    // 注册ADC中断服务函数
    Interrupt_register(INT_ADCA1, &adca1ISR);

    // 初始化外设
    initEPWM1_ForADC();
    initADC();

    // 使能全局中断
    EINT;

    while(1)
    {
        // 主循环可读取 currentU_Filtered / currentV_Filtered 用于电流环
        DEVICE_DELAY_US(100);
    }
}

运行与验证

  1. 在CCS中新建工程,将上述代码保存为main.c,包含C2000Ware的driverlib库路径。
  2. 编译并烧录至F28379D。
  3. 使用信号发生器在ADCINA0引脚输入1.65V直流(对应0A电流),在ADCINA1输入2.475V(对应+5A)。
  4. 在CCS的Expressions窗口中添加全局变量currentU_Filtered和currentV_Filtered。
  5. 全速运行,观察变量值:currentU_Filtered应稳定在0A附近(±0.3A),currentV_Filtered应稳定在5A附近(±0.3A)。

通过条件:滤波后电流值与输入信号换算值误差小于±0.5A,且变量波动幅度明显小于未滤波的原始ADC值(可在中断中临时对比adcRaw_U的跳动范围)。

关键说明

  • ADC触发与PWM同步是电机电流采样的核心,确保每次采样发生在PWM载波谷底(或峰值),避开开关噪声。
  • 滤波系数FILTER_ALPHA需根据PWM频率和电流环带宽调整:20kHz采样率下,0.2对应约660Hz截止频率,适用于常规电机控制。
  • 若使用其他C2000型号,仅需修改ADC基址(如ADCB_BASE)和中断号,逻辑不变。