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);
}
}
运行与验证
- 在CCS中新建工程,将上述代码保存为
main.c,包含C2000Ware的driverlib库路径。 - 编译并烧录至F28379D。
- 使用信号发生器在ADCINA0引脚输入1.65V直流(对应0A电流),在ADCINA1输入2.475V(对应+5A)。
- 在CCS的Expressions窗口中添加全局变量
currentU_Filtered和currentV_Filtered。 - 全速运行,观察变量值:
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)和中断号,逻辑不变。