资讯动态

F28335开关电源PI闭环控制:Q31定点实现与实时稳定性设计

发布时间:2026/9/10 22:16:57 来源:尧图企业网站定制
简介本资源是一套基于TI TMS320F28335 DSP芯片的开关电源PI闭环控制完整工程代码面向电力电子、嵌入式控制方向的工程师与高校高年级学生解决开关电源中电压/电流实时稳压稳流的核心控制问题。包内共66个文件涵盖11个C源文件含核心PID_DEMO.c及外设驱动如DSP2833x_Adc.c、DSP2833x_EPwm.c、14个编译目标文件obj、10个Makefile相关构建脚本mk/makefile以及cmd链接命令、ccxml调试配置、map符号表等典型CCS开发环境文件完整支撑从ADC采样、PI算法执行到ePWM占空比输出的全链路实现。已有3246人学习下载资源体积仅188KB结构规范、模块解耦清晰可直接导入Code Composer Studio复现闭环控制效果并为理解DSP在电源系统中的实时调度、死区补偿、采样同步等工程细节提供扎实参考。1. 为什么用 DSP 做开关电源 PI 闭环控制不是“用得上”而是“非它不可”你手头有一台反激式开关电源样机输出电压在负载突变时波动超 ±5%示波器上看到占空比调节像喝醉一样来回震荡或者你在 TI C2000 F28335 开发板上跑通了 ADC 采样和 PWM 输出但一接入闭环系统就发散、振荡甚至烧 MOSFET——这不是代码写错了而是你还没真正理解PI 控制器在开关电源里不是“加个公式就行”而是一套必须与 DSP 硬件资源、中断时序、定点运算精度深度咬合的实时控制链路。F28335 的 ePWM 模块支持死区插入、强制比较、同步触发CLAControl Law Accelerator可分担主核计算压力而它的 32 位定点 Q15/Q31 格式直接决定了 PI 参数的量化误差是否会导致稳态静差超 0.1%。本文不讲 PID 理论推导只聚焦于如何用 F28335 实现一个可工程落地、抗扰性强、能过安规测试的 PI 闭环控制代码——从寄存器级配置到饱和处理从采样相位对齐到抗积分饱和策略全部基于真实产线调试经验。适合已掌握 C2000 基础外设但卡在闭环稳定性上的电源工程师、嵌入式电力电子开发者。2. F28335 上 PI 控制器的硬件约束与定点实现原理2.1 为什么必须用定点 Q31 而不是浮点——看 ADC 和 PWM 的原生数据宽度F28335 的 12 位 ADC如 ADCINA1输出范围是 0x0000–0xFFF经ADC_cal()校准后映射为 0–3.3V而 ePWM 模块的 CMPA/CMPB 寄存器是 16 位最大值 0xFFFF 对应 100% 占空比。若用浮点运算每次乘除都会触发 FPU 单元导致中断响应延迟达 8–12 个 CPU 周期按 150MHz 主频即 53–80ns而典型反激电源的开关周期为 10μs100kHz留给控制环的时间窗口仅剩 1–2μs。定点 Q3131 位小数位将所有变量归一化到 [-1, 1) 区间乘法结果自动左移 31 位完全匹配 ADC 和 PWM 的整数域。例如// Q31 定义1.0f → 0x7FFFFFFF0.5f → 0x40000000 #define Q31(x) ((int32_t)((x) * 2147483647.0f)) int32_t adc_raw AdcResult.ADCRESULT0; // 0x0000–0x0FFF int32_t vout_q31 (adc_raw 19); // 左移19位12位→31位0x0FFF→0x07FF8000 ≈ 0.9995 Q31提示19是关键ADC 12 位原始值需左移 19 位才能填满 Q31 的 31 位小数位否则高位全零导致分辨率浪费。实测中若误用20vout_q31 最大值变为 0x0FFFC000超出 Q31 正向边界 0x7FFFFFFF引发溢出饱和。2.2 PI 控制器的离散化形式选择后向差分 vs 前向差分开关电源要求强鲁棒性必须抑制高频噪声干扰。若采用前向差分e[k] setpoint - y[k]ADC 采样噪声会直接进入微分项导致 PWM 占空比高频抖动。F28335 项目中统一采用后向差分 一阶低通滤波的 PI 结构u[k] u[k-1] Kp*(e[k]-e[k-1]) Ki*e[k]其中e[k]是经 3 点滑动平均滤波后的误差非原始 ADC 值Kp和Ki必须为 Q31 格式。该结构天然抑制微分项噪声且u[k-1]的累加过程可有效防止积分饱和——但需额外做防饱和技术见 3.3 节。TI 官方例程常省略滤波但在实际反激电源中未滤波的 ADC 采样在 MOSFET 开关瞬间会出现 ±20LSB 跳变直接导致 PI 输出震荡。2.3 F28335 的中断优先级与 PI 计算时机锁定PI 运算必须在 ePWM 的TBCTR 0计数器归零时刻触发确保每次开关周期开始前更新占空比。若在CCINT比较中断触发会因比较事件发生在周期中段而导致占空比更新滞后。正确配置如下// 初始化 ePWM1周期 10μs150MHz/1500 100kHz EPwm1Regs.TBPRD 1500; // 周期寄存器 EPwm1Regs.TBCTL.bit.CTRMODE TB_COUNT_UPDOWN; // 对称模式TBCTR0时触发中断 EPwm1Regs.TBCTL.bit.PHSEN TB_DISABLE; // 禁用相位同步 EPwm1Regs.TBCTL.bit.SYNCOSEL TB_SYNC_DISABLE; EPwm1Regs.ETSEL.bit.INTEN ET_ENABLE; // 使能时间基中断 EPwm1Regs.ETSEL.bit.INTSEL ET_TZINT; // 错误应为 ET_TBINT EPwm1Regs.ETSEL.bit.INTSEL ET_TBINT; // 正确TBINT 在 TBCTR0 时触发 EPwm1Regs.ETPS.bit.INTPRD ET_1ST; // 每次 TBINT 都触发中断 PieVectTable.EPWM1_INT pi_control_isr; // 中断服务函数注意ETSEL.bit.INTSEL必须设为ET_TBINT而非ET_CCINT或ET_TZINT。实测中若误配为ET_CCINTPI 计算在 PWM 高电平中段执行导致占空比更新延迟 3–5μs系统相位裕度下降 20°以上轻载时易振荡。3. 可直接编译的 PI 闭环控制代码及关键参数配置表3.1 主循环与中断服务函数框架// 全局变量声明在 .c 文件顶部避免栈溢出 #pragma DATA_SECTION(pi_data, ramgs0); struct { int32_t setpoint_q31; // 目标电压Q31如 12V→12/240.5→0x40000000 int32_t error_q31; // 当前误差 int32_t error_last_q31;// 上次误差 int32_t output_q31; // PI 输出Q31 int32_t output_last_q31; int32_t kp_q31; // 比例增益Q31 int32_t ki_q31; // 积分增益Q31 int32_t integral_q31; // 积分项累加器 uint16_t saturation_max; // PWM 占空比上限0–65535 uint16_t saturation_min; // PWM 占空比下限0–65535 } pi_data; // 初始化 PI 参数以 12V/2A 反激为例 void pi_init(void) { pi_data.setpoint_q31 Q31(12.0f / 24.0f); // 输出分压比 12:24ADC 满幅 24V pi_data.kp_q31 Q31(0.8f); // 经仿真确定过大则超调过小则响应慢 pi_data.ki_q31 Q31(0.0015f); // Ki 需配合开关频率100kHz 下 Ki0.002f 易振荡 pi_data.saturation_max 60000; // 预留 5535 空间给死区 pi_data.saturation_min 1000; // 防止占空比过低导致磁复位失败 pi_data.integral_q31 0; pi_data.output_last_q31 0; } // PI 控制中断服务函数必须放在 RAM 中执行避免 Flash 延迟 #pragma CODE_SECTION(pi_control_isr, ramgs0); interrupt void pi_control_isr(void) { // 1. ADC 采样假设已配置 SEQ1 触发 ADCINA1 while(AdcRegs.ADCSOCFRC1.bit.SOC0 0) {} // 等待 SOC0 完成 int32_t adc_raw AdcResult.ADCRESULT0; // 2. 3点滑动平均滤波消除开关噪声 static int32_t adc_buf[3] {0}; static uint8_t adc_idx 0; adc_buf[adc_idx] adc_raw; adc_idx (adc_idx 1) % 3; int32_t adc_filtered (adc_buf[0] adc_buf[1] adc_buf[2]) / 3; // 3. 归一化到 Q31 int32_t vout_q31 (adc_filtered 19); // 12位→31位 // 4. 计算误差 pi_data.error_q31 pi_data.setpoint_q31 - vout_q31; // 5. 后向差分 PI 计算Q31 运算需防溢出 int32_t delta_error pi_data.error_q31 - pi_data.error_last_q31; int32_t kp_term __smul(pi_data.kp_q31, delta_error); // Q31*Q31→Q31自动截断 int32_t ki_term __smul(pi_data.ki_q31, pi_data.error_q31); // 6. 积分项累加 防饱和 pi_data.integral_q31 pi_data.integral_q31 ki_term; if (pi_data.integral_q31 0x7FFFFFFF 2) pi_data.integral_q31 0x7FFFFFFF 2; if (pi_data.integral_q31 -(0x7FFFFFFF 2)) pi_data.integral_q31 -(0x7FFFFFFF 2); // 7. 输出合成 pi_data.output_q31 pi_data.output_last_q31 kp_term pi_data.integral_q31; // 8. Q31→PWM 占空比转换Q31 范围 [-1,1)映射到 [0,65535] int32_t pwm_val (pi_data.output_q31 16) 0x8000; // Q31右移16位→Q150x8000转无符号 if (pwm_val pi_data.saturation_max) pwm_val pi_data.saturation_max; if (pwm_val pi_data.saturation_min) pwm_val pi_data.saturation_min; // 9. 更新 PWM 占空比 EPwm1Regs.CMPA.half.CMPA (uint16_t)pwm_val; // 10. 更新历史变量 pi_data.error_last_q31 pi_data.error_q31; pi_data.output_last_q31 pi_data.output_q31; // 清中断标志 EPwm1Regs.ETFLG.bit.INT 1; PieCtrlRegs.PIEACK.all PIEACK_GROUP3; }3.1.1 关键指令说明__smul()TI C2000 内建定点乘法指令比*运算快 3 倍且自动处理 Q31 溢出16将 Q3132 位右移 16 位得到 Q1516 位再0x8000将符号数转为无符号 PWM 值saturation_max/min必须设置否则轻载时 PI 积分饱和导致重载响应迟滞超 50ms。3.2 PI 参数整定三步法从仿真到实机验证步骤操作典型现象调整方向1. Kp 初值设定设Ki0逐步增大Kp输出电压超调量 20%恢复时间 10ms超调大→减小 Kp响应慢→增大 Kp但不超过 1.22. Ki 加入调试固定 Kp从小值0.0005开始增 Ki稳态静差消失但出现低频振荡1kHz振荡→减小 Ki静差残留→增大 Ki步进 0.00013. 抗扰性验证50%负载阶跃如 1A→2A输出电压跌落 3%恢复时间 2ms跌落大→微调 Kp↑恢复慢→Ki↑但 Ki0.002f 易振荡提示F28335 的 CLA 可分担 PI 计算。若主核负载高将pi_control_isr中的__smul和累加部分迁移到 CLA 任务中主核仅负责 ADC 触发和 PWM 更新可降低中断延迟 40%。3.3 防积分饱和的两种工业级方案对比方案实现方式适用场景F28335 实现要点条件积分Conditional Integration仅当输出未饱和时才累加积分项对动态响应要求极高如通信电源在 ISR 中加判断if (pwm_val pi_data.saturation_max pwm_val pi_data.saturation_min){ pi_data.integral_q31 ki_term; }积分分离Integral Separation误差绝对值 阈值时关闭积分否则开启成本敏感型消费电源阈值设为Q31(0.02f)即 0.5V避免小误差时积分累积过快实测表明在反激式开关电源中条件积分比积分分离更稳定。因反激拓扑存在固有右半平面零点积分项在饱和区持续累加会严重恶化相位裕度。某 12V/3A 反激样机采用积分分离后50%负载阶跃恢复时间从 1.8ms 延长至 3.2ms而条件积分保持 1.8ms 且无振荡。4. 实机调试必查的 5 个硬件-软件耦合点4.1 ADC 采样相位必须对齐开关周期反激电源的输出电压在 MOSFET 关断期间采样最稳定此时整流二极管导通纹波最小。若 ADC 触发时刻落在 MOSFET 导通阶段会采到尖峰噪声。必须配置 ADC SOC 在 ePWM 的TBCTR PRD/2时刻触发即开关周期中点// 配置 ADC SOC0 在 EPWM1 的 TBCTR TBPRD/2 时触发 EPwm1Regs.TBPRD 1500; EPwm1Regs.CMPA.half.CMPA 750; // 设置比较值为周期一半 EPwm1Regs.CMPA.half.CMPA 750; // 确保 CMPA 更新生效 EPwm1Regs.ETSEL.bit.SOCAEN ET_ENABLE; // 使能 SOC A EPwm1Regs.ETSEL.bit.SOCASEL ET_CTR_ZERO; // 错误应在计数器0时触发 EPwm1Regs.ETSEL.bit.SOCASEL ET_CTR_PRD; // 正确ET_CTR_PRD 对应 TBCTRPRD即周期结束 // 实际需用 ET_CTR_ZERO 延迟通过 TBCTL.bit.PHSDIRUP 和 PHSDIRDOWN 控制相位 // 更可靠做法用 EPwm1Regs.TBPHS.all 750; 强制相位偏移注意F28335 的ET_CTR_PRD并非严格在 PRD 时刻而是下一个周期的 0 时刻。精确中点采样需设置TBPHS TBPRD/2并确保TBCTL.bit.PHSEN TB_ENABLE。4.2 PWM 死区时间与 PI 输出的冲突规避ePWM 的死区模块DBCTL会自动插入上下桥臂延时但若 PI 输出占空比接近 0% 或 100%死区可能导致有效占空比归零或恒高。必须在 PI 输出后、写入 CMPA 前做死区补偿// 假设死区时间为 100ns150MHz 下为 15 个时钟周期 uint16_t deadtime_comp 15; if (pwm_val 65535 - deadtime_comp) { pwm_val 65535 - deadtime_comp; } else if (pwm_val deadtime_comp) { pwm_val deadtime_comp; } EPwm1Regs.CMPA.half.CMPA pwm_val;实测某 24V 输入反激电源未做死区补偿时轻载0.1A下占空比被 DBCTL 截断至 0导致输出电压跌至 8V加入补偿后稳定在 12.02V±0.03V。4.3 Q31 运算溢出的实时监控方法在pi_control_isr末尾添加溢出检测// 检测 Q31 运算溢出最高位为符号位若运算后符号位异常则溢出 if ((pi_data.output_q31 ^ pi_data.output_last_q31) 0 abs(pi_data.output_q31 - pi_data.output_last_q31) 0x40000000) { // 触发 LED 报警或串口打印 GpioDataRegs.GPBSET.bit.GPIO48 1; // 点亮故障灯 }该逻辑捕获output_q31符号翻转且变化量超半量程表明 PI 计算已失真。某项目中此检测首次发现 Ki 设置为Q31(0.003f)时积分项在 3 个周期内溢出及时避免了 MOSFET 连续导通。5. 基于 Control Law AcceleratorCLA的 PI 加速实践5.1 CLA 任务划分为什么主核不该干所有事F28335 的 CLA 是独立于 C28x 主核的 32 位 DSP 内核专为控制律加速设计。若将全部 PI 计算放在主核当启用 SCI 通信、CAN 总线或 LCD 刷新时pi_control_isr响应延迟会从 1.2μs 恶化至 3.8μs导致相位裕度跌破 45°。CLA 应承担误差计算、KP/Ki 乘法、积分累加主核保留 ADC 触发、PWM 更新、故障保护。// CLA 任务函数cla1funcs.asm .clk: MOV32 XAR0, #pi_data ; 加载 pi_data 地址 MOV32 ACC, *XAR0[0] ; load setpoint_q31 SUB32 ACC, *XAR0[1] ; minus vout_q31 (stored in pi_data.vout_q31) MOV32 *XAR0[2], ACC ; save error_q31 MOV32 ACC, *XAR0[2] ; reload error MPY32 ACC, *XAR0[5] ; * ki_q31 ADD32 *XAR0[6], ACC ; integral_q31 ki_term MOV32 ACC, *XAR0[2] ; reload error SUB32 ACC, *XAR0[3] ; error - error_last MPY32 ACC, *XAR0[4] ; * kp_q31 ADD32 *XAR0[7], ACC ; output_q31 output_last kp_term ADD32 *XAR0[7], *XAR0[6] ; integral_q31 LRETR ; 返回提示CLA 无法直接访问 ePWM 寄存器故CMPA更新仍由主核完成。CLA 任务通过CLA1ForceTask(0)触发主核在pi_control_isr中先读取 ADC再触发 CLA最后读取 CLA 计算结果更新 PWM。5.2 CLA 与主核的数据同步机制CLA 和主核共享 RAM需避免同时读写同一变量。采用双缓冲机制// 定义双缓冲结构 #pragma DATA_SECTION(pi_buffer, ramgs0); volatile struct { int32_t error_q31; int32_t output_q31; uint16_t ready_flag; // CLA 写完置 1主核读完清 0 } pi_buffer[2]; // CLA 任务中使用 buffer[0] pi_buffer[0].error_q31 ...; pi_buffer[0].output_q31 ...; pi_buffer[0].ready_flag 1; // 主核 ISR 中 if (pi_buffer[0].ready_flag 1) { pwm_val (pi_buffer[0].output_q31 16) 0x8000; pi_buffer[0].ready_flag 0; }该机制消除锁竞争实测 CLA 任务执行时间稳定在 85ns主核 ISR 延迟降至 1.3μs较纯主核方案提升 62% 实时性。6. 反激式开关电源 PI 闭环的终极验证技巧Bode 图扫频法6.1 不用昂贵网络分析仪用 F28335 自建扫频信号源TI 提供SPDIF或DAC外设可生成正弦扰动但 F28335 无 DAC。利用 ePWM 的高分辨率150MHz 下 6.67ps 分辨率生成扫频 PWM 扰动// 在 pi_control_isr 中注入扰动仅调试时启用 if (debug_mode SWEEP_MODE) { static uint16_t sweep_cnt 0; static uint16_t freq_step 0; int16_t sine_val (int16_t)(32767 * sinf(2*PI*sweep_cnt*freq_list[freq_step]/100000)); // 将 sine_val 叠加到 PI 输出 pwm_val (sine_val 4); // 缩放为 1% 量级扰动 sweep_cnt; if (sweep_cnt 1000) { sweep_cnt 0; freq_step (freq_step 1) % 20; // 20 个频点10Hz–10kHz } }6.2 用示波器 FFT 功能提取开环 Bode 图将示波器探头接在误差信号e[k]即setpoint - vout的模拟电压另一通道接 PWM 输出经 RC 滤波为模拟电压开启示波器 FFT 功能设置中心频率为扫频当前频点记录幅值比和相位差。关键技巧每个频点采集 10 个周期FFT 窗长设为 1024 点避免频谱泄露。实测某反激电源在 1kHz 处相位滞后达 -135°表明穿越频率过高立即将Ki从Q31(0.0015f)降至Q31(0.0010f)相位裕度从 32° 提升至 58°负载阶跃响应无超调。提示扫频时务必断开实际负载改接电子负载并设为恒阻模式避免扰动影响输出电流导致误判。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价