1. 项目概述:硬核FOC驱动开发全流程
作为一名深耕电机控制领域多年的工程师,我始终认为真正掌握FOC(磁场定向控制)技术的标志,是能够从寄存器级别开始构建整个驱动系统。这次我们选择TI C2000系列DSP作为硬件平台,其高精度PWM模块和快速ADC采样非常适合电机控制场景。
整个项目包含三个关键阶段:首先是DSP外设的裸机配置,包括PWM、ADC和中断系统;其次是FOC算法核心模块的实现与优化;最后是系统集成与调试。这种开发方式虽然比使用现成的电机库更耗时,但能让我们完全掌控每个环节的细节,这对后续的性能调优和故障排查至关重要。
提示:建议准备数字示波器和电流探头,调试阶段需要同时观测三相电流波形和PWM信号
2. 硬件平台与外设配置
2.1 DSP选型与基础配置
我们选用TMS320F28335作为主控芯片,主要看中其:
- 150MHz主频带来的实时性保障
- 16通道12位ADC(80ns采样保持时间)
- 18路高分辨率PWM(HRPWM模块支持150ps分辨率)
- 硬件除法器和三角函数加速单元
芯片初始化时需要特别注意时钟树配置:
c复制void InitSysCtrl(void)
{
// 禁用看门狗
DisableDog();
// 设置PLL为10倍频(晶振30MHz → 300MHz)
InitPll(0xA);
// 外设时钟使能
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;
SysCtrlRegs.PCLKCR1.bit.EPWMENCLK = 1;
}
2.2 PWM模块深度配置
电机驱动的核心是PWM生成,我们配置ePWM1模块产生三对互补PWM:
c复制void InitEPwm(void) {
EPwm1Regs.TBCTL.bit.CTRMODE = 2; // 增减计数模式
EPwm1Regs.TBPRD = SYSTEM_FREQ/(2*PWM_FREQ) - 1; // 20kHz PWM
EPwm1Regs.CMPA.half.CMPA = 0; // 初始占空比0%
// 死区配置(根据MOS管规格调整)
EPwm1Regs.DBCTL.bit.OUT_MODE = 0x3; // 使能上升沿和下降沿死区
EPwm1Regs.DBFED = DEAD_TIME; // 上升沿延迟500ns
EPwm1Regs.DBRED = DEAD_TIME; // 下降沿延迟500ns
// ADC触发配置(关键!)
EPwm1Regs.ETSEL.bit.SOCAEN = 1; // 使能SOCA触发
EPwm1Regs.ETSEL.bit.SOCASEL = 1; // 计数等于CMPA时触发
EPwm1Regs.ETPS.bit.SOCAPRD = 1; // 每个事件都触发
}
这里有几个工程经验值得分享:
- 死区时间需根据MOS管开关特性调整,一般IGBT需要1-2μs,SiC MOSFET可缩减到200-500ns
- ADC采样时刻应设置在PWM周期中点,此时电流纹波最小
- 使用HRPWM模块时要注意MEP(微边沿定位)校准
2.3 ADC采样系统设计
电流采样采用双电阻方案(下桥臂采样),配置ADC如下:
c复制void InitAdc(void)
{
AdcRegs.ADCTRL1.bit.ACQ_PS = 0xF; // 采样窗口=16个SYSCLK周期
AdcRegs.ADCTRL3.bit.SMODE_SEL = 1; // 顺序采样模式
AdcRegs.ADCMAXCONV.bit.MAX_CONV = 1;// 转换2个通道
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 3; // 采样ADCINA3(相电流A)
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 7; // 采样ADCINB3(相电流B)
// 校准偏移(必须执行!)
AdcRegs.ADCOFFTRIM.bit.OFFSET_TRIM = GetAdcOffset(3);
}
注意:ADC采样结果需要做如下处理:
- 减去零点偏移(上电时测量)
- 乘以增益系数(实测值/理论值)
- 对双电阻方案需要进行电流重构:Ic = -Ia - Ib
3. FOC算法实现与优化
3.1 坐标变换实现
Clarke变换采用节省计算量的两相版本:
c复制void ClarkeTransform(float a, float b, float *alpha, float *beta)
{
*alpha = a;
*beta = (a + 2*b) * 0.57735026919f; // 1/sqrt(3)
}
Park变换使用预先计算的三角函数表:
c复制float sin_table[512]; // Q1.15格式
void InitSinTable(void)
{
for(int i=0; i<512; i++) {
sin_table[i] = sinf(2*PI*i/512.0f);
}
}
void ParkTransform(float alpha, float beta, float theta,
float *d, float *q)
{
int index = (int)(theta * 512/(2*PI)) & 0x1FF;
float sin_val = sin_table[index];
float cos_val = sin_table[(index+128)&0x1FF];
*d = alpha * cos_val + beta * sin_val;
*q = -alpha * sin_val + beta * cos_val;
}
实测查表法比实时计算快3倍以上,特别适合在中断服务程序中调用。
3.2 电流环PID实现
采用Q15定点数运算优化性能:
c复制#pragma CODE_SECTION(CurrentPID, "ramfuncs")
int16 CurrentPID(int16 err)
{
static int32 integral = 0;
int32 output;
// 比例项
output = (err * Kp) >> 8;
// 积分项(带抗饱和)
integral += err * Ki;
if(integral > 32767<<8) integral = 32767<<8;
else if(integral < -32768<<8) integral = -32768<<8;
output += (integral >> 8);
// 饱和输出
return __ssat(output, 16);
}
关键优化点:
- 使用
ramfuncs段避免Flash访问延迟 - 采用移位代替浮点运算
- 使用DSP内置的
__ssat饱和指令 - 积分项做防溢出处理
3.3 SVPWM生成算法
空间矢量调制是实现高效率的关键:
c复制void SVPWM_Gen(float alpha, float beta, float *ta, float *tb, float *tc)
{
// 扇区判断
int sector = 0;
if(beta >= 0) sector |= 1;
if(alpha*0.8660254f - beta*0.5f >= 0) sector |= 2;
if(-alpha*0.8660254f - beta*0.5f >= 0) sector |= 4;
// 计算占空比
switch(sector) {
case 1: // 扇区I
*ta = 0.5f * (1 + alpha + beta*0.57735026919f);
*tb = *ta - beta*1.15470053838f;
*tc = 1 - *ta;
break;
// 其他扇区类似...
}
// 过调制处理
float max_duty = __fmax(__fmax(*ta, *tb), *tc);
if(max_duty > 0.95f) { // 5%裕量
float scale = 0.95f / max_duty;
*ta *= scale; *tb *= scale; *tc *= scale;
}
}
4. 系统集成与调试
4.1 中断服务程序架构
整个FOC算法在PWM中断中完成:
c复制__interrupt void epwm1_isr(void)
{
// 1. 电流采样(关键时序!)
AdcReadCurrents(&Ia, &Ib);
// 2. 坐标变换
ClarkeTransform(Ia, Ib, &I_alpha, &I_beta);
ParkTransform(I_alpha, I_beta, theta, &Id, &Iq);
// 3. 电流环控制
Vd = CurrentPID(Id_ref - Id);
Vq = CurrentPID(Iq_ref - Iq);
// 4. 逆变换
InversePark(Vd, Vq, theta, &Valpha, &Vbeta);
// 5. PWM生成
SVPWM_Gen(Valpha, Vbeta, &Ta, &Tb, &Tc);
EPwm1Regs.CMPA = (uint16_t)(Ta * EPwm1Regs.TBPRD);
EPwm1Regs.CMPB = (uint16_t)(Tb * EPwm1Regs.TBPRD);
// 6. 更新角度(速度环在后台运行)
theta += speed * DT;
if(theta > 2*PI) theta -= 2*PI;
// 必须清除中断标志!
EPwm1Regs.ETCLR.bit.INT = 1;
}
4.2 调试技巧与常见问题
问题1:电机抖动不转
- 检查PWM输出是否正常(示波器观测)
- 确认ADC采样时刻是否正确(应在PWM周期中点)
- 验证电流采样极性是否正确(尝试交换Ia/Ib)
问题2:电流波形畸变
- 调整死区时间(过小会导致上下管直通)
- 检查PID参数是否合理(建议先调P再调I)
- 确认SVPWM算法是否做过调制处理
问题3:高速运行不稳定
- 增加速度环采样周期
- 检查角度估算是否累积误差(增加编码器校准)
- 降低电流环带宽(高速时电感效应明显)
经验:调试时先用低压电源(24V以下),逐步提高电压。准备备用MOS管/IPM模块,过压/过流保护电路必不可少。
5. 性能优化进阶
5.1 代码级优化技巧
- 关键函数RAM运行:
c复制#pragma CODE_SECTION(CurrentPID, "ramfuncs");
#pragma CODE_SECTION(ParkTransform, "ramfuncs");
- 使用DSP库函数:
c复制#include "DSP28x_Project.h"
#include "IQmathLib.h"
_iq15 Id_ref = _IQ15(1.0); // Q15格式
_iq15 Kp = _IQ15(0.5);
- 循环展开优化:
c复制// 查表法优化示例
float sin_val = sin_table[(theta>>7)&0x1FF];
float cos_val = sin_table[((theta>>7)+128)&0x1FF];
5.2 系统级优化方向
- 引入前馈控制:
c复制Vd = CurrentPID(Id_err) + speed * Lq * Iq_ref;
Vq = CurrentPID(Iq_err) + speed * (Ld * Id_ref + Ke);
- 参数自整定方案:
- 注入小信号扰动
- 分析频率响应
- 自动计算PID参数
- 无传感器扩展:
c复制// 滑模观测器实现
float E = (Valpha - Rs*I_alpha) - Ls*dI_alpha;
float Est_theta = atan2f(E_beta, E_alpha);
在完成基础FOC实现后,建议使用CCS的实时调试功能观察变量变化,逐步优化每个环节的性能。记住,电机控制是理论与实践紧密结合的领域,只有通过不断的调试和优化,才能真正掌握其精髓。