用DSP28335做三相逆变电源开发,本质上是在电力电子和嵌入式编程的交叉领域跳舞。这个项目最刺激的地方在于,你得同时处理纳秒级的PWM时序控制和毫秒级的电压闭环调节——就像一边用绣花针缝扣子,一边还要保持平衡木稳定。
我最近完成的这个数字电源项目,主拓扑采用典型的三相全桥逆变结构,控制部分基于TI的DSP28335。这个片子虽然有些年头了,但在工业界依然老当益壮,特别是它的EPWM模块和12位ADC,做三相控制刚刚好。不过要注意,28335的ADC基准电压只有3V,前端必须做好信号调理。
三相逆变桥的硬件设计有三大死亡陷阱:
关于母线电容,新手最容易栽在ESR参数上。我建议用多个低ESR的薄膜电容并联(比如EPCOS的B3277系列),容量按每千瓦100-200μF配置。曾经有个项目为了省钱用了普通电解电容,结果在负载突变时ESR发热导致电容失效,IGBT直接放烟花。
栅极驱动建议先用现成的驱动板练手,比如英飞凌的2ED020I12-F或TI的UCC21520。自己画板子时务必注意:
电压采样电路需要特别注意共模电压问题。对于380V系统,我推荐采用线性光耦HCNR201做隔离采样,配合OPA2188搭建差分放大电路。关键参数计算如下:
code复制采样电阻分压比 = 3V(ADC量程) / (380V * 1.414) ≈ 1/180
取R1=180kΩ, R2=1kΩ, 实际分压比=1/181
电流采样建议用LEM的霍尔传感器,比如LAH-50P。它的响应速度快(<1μs),而且自带电气隔离。注意传感器输出要加RC滤波(典型值100Ω+100nF),但截止频率不能设太低,否则会影响动态响应。
这个项目的软件架构采用典型的双环控制:
时序调度用28335的EPWM1中断触发,载波频率设为10kHz。中断服务程序(ISR)的执行时间必须控制在20μs以内,否则会错过下一个PWM周期。我的实测数据显示,完整的双环计算大约需要15μs(主频150MHz时)。
重要提示:一定要在CCS中开启编译器优化(-O2级别),否则ISR会超时。但优化后某些调试变量可能被优化掉,这时可以用volatile关键字保护关键变量。
三相电压采样必须严格同步,否则会导致控制环路失衡。28335的ADC配置有几个关键点:
c复制void Init_ADC(void) {
AdcRegs.ADCTRL1.bit.ACQ_PS = 0xF; // 采样窗口=16个SYSCLK
AdcRegs.ADCTRL3.bit.ADCCLKPS = 3; // HSPCLK四分频
AdcRegs.ADCMAXCONV.bit.MAX_CONV = 2; // 转换3个通道
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0; // A相电压
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 1; // B相电压
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 2; // C相电压
// 用EPWM1的CTR=PRD事件触发采样
AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // 使能SEQ1中断
}
采样时序要满足:
code复制采样保持时间 > 信号建立时间 + PCB走线延迟
对于1米以内的走线,建议保持时间≥500ns。有个调试技巧:用PWM触发ADC后,在ISR里读取ADCRESULT寄存器,观察数值是否稳定。
数字电源最核心的就是PID算法。这里采用增量式实现,主要优势是:
c复制typedef struct {
_iq Kp, Ki, Kd;
_iq integral;
_iq last_error;
_iq max_out, min_out;
} PID_Struct;
_iq PID_Calc(PID_Struct *pid, _iq error) {
_iq p_term = _IQmpy(pid->Kp, error);
_iq i_term = _IQmpy(pid->Ki, pid->integral);
_iq d_term = _IQmpy(pid->Kd, error - pid->last_error);
pid->integral += error;
pid->last_error = error;
_iq output = p_term + i_term + d_term;
// 输出限幅
if(output > pid->max_out) output = pid->max_out;
else if(output < pid->min_out) output = pid->min_out;
return output;
}
Q格式处理是关键,28335是定点DSP,建议用Q15格式(_iq类型)。比如当Kp=0.5时,实际赋值应该是:
c复制pid.Kp = _IQ(0.5); // Q15格式下等于16384
三相PWM需要6个通道,配置时要注意载波同步:
c复制void Init_EPWM(void) {
// 时基配置
EPwm1Regs.TBPRD = 1500; // 10kHz PWM (SYSCLK=150MHz)
EPwm1Regs.TBPHS.half.TBPHS = 0;
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
EPwm1Regs.TBCTL.bit.PHSEN = TBPHSEN;
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
// 动作限定配置
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
// 死区配置
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm1Regs.DBFED = _DB_TIME(100ns); // 上升沿延迟
EPwm1Regs.DBRED = _DB_TIME(100ns); // 下降沿延迟
// 同步配置
EPwm1Regs.TBCTL.bit.SWFSYNC = 1; // 强制同步
}
死区时间必须大于IGBT的关断延迟时间。以英飞凌的IKW40N120T2为例:
code复制开通延迟(td(on)) = 110ns
关断延迟(td(off)) = 420ns
推荐死区时间 = td(off) - td(on) + 余量 = 420-110+50=360ns
但在实际调试中发现,驱动电路本身也有延迟,所以最终设了500ns死区。
血泪教训:上电前一定要用万用表检查所有电源对地阻抗!我有次因为5V和地短路,烧了3片DSP。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 输出电压震荡 | PID参数不当 | 先调Kp,再加Ki |
| PWM波形畸变 | 死区时间不足 | 增加DBFED/DBRED |
| ADC采样跳变 | 地线干扰 | 加π型滤波 |
| IGBT过热 | 驱动电阻过大 | 减小栅极电阻 |
c复制#pragma DATA_SECTION(AdcResult, "AdcResultSec")
volatile Uint16 AdcResult[3];
然后在CCS中添加图形显示,采样率设为10kHz。
c复制if(PID_output > 0.9) __asm(" ESTOP0"); // 当输出饱和时触发断点
c复制CpuTimer0.InterruptCount = 0; // 在ISR开头清零
// ... ISR代码 ...
// 退出时查看CpuTimer0.InterruptCount的值
c复制#define FILTER_DEPTH 8
Uint16 filter_buf[FILTER_DEPTH];
Uint16 moving_avg(Uint16 new_val) {
static Uint16 index = 0;
filter_buf[index] = new_val;
index = (index + 1) % FILTER_DEPTH;
Uint32 sum = 0;
for(int i=0; i<FILTER_DEPTH; i++) {
sum += filter_buf[i];
}
return (Uint16)(sum / FILTER_DEPTH);
}
c复制void EPWM_ISR(void) {
if(AdcResult[0] > OVER_VOLTAGE_THRESHOLD) {
EPwm1Regs.TZCTL.bit.TZA = TZ_FORCE_HI;
EPwm1Regs.TZCTL.bit.TZB = TZ_FORCE_HI;
SystemStatus = FAULT_MODE;
}
}
三相逆变电源开发就像在刀尖上跳舞,每一个细节都可能成为事故的导火索。我最深刻的体会是:硬件是骨架,软件是灵魂,而调试则是让两者和谐共舞的指挥家。记得在第一个成功并网的瞬间,示波器上那完美的正弦波让我觉得所有炸管的惊吓都值了。