当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]无OS跑同步Buck,核心不是“写个PID函数”,而是把控制环钉在PWM周期里:每个开关周期采一次输出、算一次增量、更新一次占空比,全程不动态分配、不调库、不进延时。裸机数字电源的稳定,靠的是时序确定性和输出限幅,不是算法多复杂。


无OS跑同步Buck,核心不是“写个PID函数”,而是把控制环钉在PWM周期里:每个开关周期采一次输出、算一次增量、更新一次占空比,全程不动态分配、不调库、不进延时。裸机数字电源的稳定,靠的是时序确定性和输出限幅,不是算法多复杂。

一、硬件前提:互补PWM必须带死区

同步Buck高边和低边MOS是互补导通,两管同时导通=母线直接短路炸管。高级定时器(TIM1/HRTIM)配互补通道+死区插入:

#define PWM_PERIOD  1000      // 100kHz,计数周期

#define DEAD_TIME   20        // 死区计数,按驱动延迟实测

#define MAX_DUTY    920       // 92%,留余量

#define MIN_DUTY    40        // 4%,保证干净开通

死区时间要用示波器量HS关→LS开、LS关→HS开的间隔,别只看寄存器配置。驱动IC有传播延迟,软件死区必须覆盖它。

二、控制环结构:增量式PI

电压单环先用PI,D项在电源里容易放大采样噪声,初期设0。

typedef struct {

   float kp, ki;

   float integ;

   float err_prev;

   float out;            // 当前占空比(计数单位)

   float out_min, out_max;

} pi_t;


pi_t volt_pi = {

   .kp=0.15f, .ki=0.02f,

   .integ=0, .err_prev=0,

   .out=200, .out_min=MIN_DUTY, .out_max=MAX_DUTY

};

增量式写法,输出是占空比增量,天然抗突变:

float pi_update(pi_t*p, float ref, float fb, float dt){

   float err = ref - fb;

   /* 积分累加,先抗饱和 */

   p->integ += p->ki * err * dt;

   if(p->integ >  p->out_max) p->integ =  p->out_max;

   if(p->integ <  p->out_min) p->integ =  p->out_min;


   float pterm = p->kp * (err - p->err_prev);

   float iterm = p->ki * err * dt;

   p->err_prev = err;


   p->out += pterm + iterm;          // 增量叠加

   if(p->out > p->out_max) p->out = p->out_max;

   if(p->out < p->out_min) p->out = p->out_min;

   return p->out;

}

关键点:积分项在累加时就限幅,输出触顶不再继续累加同向误差,避免“过冲后半天回不来”。

三、ADC采样时机

别在main里随便采。用PWM更新事件触发ADC,采样点落在低边导通中期(输出纹波较稳的位置):

volatile uint16_t adc_raw = 0;

volatile uint8_t  adc_ready = 0;


void ADC_IRQHandler(void){

   adc_raw = ADC_READ_VOUT();

   adc_ready = 1;

   ADC_CLEAR_EOC();

}

无OS时,主循环轮询adc_ready,或者把控制逻辑直接放进PWM更新中断(周期10μs量级,代码必须短)。

四、主控制节拍(裸机轮询版)

void power_loop(void){

   static float filt = 0;

   if(!adc_ready) return;

   adc_ready = 0;


   /* 一阶滤波,抑制采样毛刺 */

   float vout = adc_raw * ADC_V_SCALE;

   filt += 0.3f*(vout - filt);


   /* 保护优先于闭环 */

   if(filt > VOUT_OVP)      { pwm_force_off(); fault_set(FAULT_OVP); return; }

   if(adc_current() > I_OCP){ pwm_force_off(); fault_set(FAULT_OCP); return; }


   /* 软启动:参考电压斜坡 */

   static float vref_ramp = 0;

   if(vref_ramp < V_REF_TARGET)

       vref_ramp += SOFTSTART_STEP;


   float duty = pi_update(&volt_pi, vref_ramp, filt, CTRL_DT);

   pwm_set_compare((uint16_t)duty);

}

软启动必须爬坡给参考值,不能直接把12V设进去——否则上电瞬间占空比打满,电感电流冲击很大。

五、DCM下同步管处理

轻载时电感电流会到零,低边MOS继续导通反而把能量倒灌回去。工程做法:

检测电感电流过零

过零后关低边管,切异步整流(体二极管续流)

或直接在轻载区关闭同步管驱动

if(inductor_current() <= 0 && load_light())

   ls_mos_disable();

else

   ls_mos_enable();

这一步不做,轻载效率会明显变差,还可能振荡。

六、写CCR的时机

占空比更新必须在PWM周期边界、更新事件后写,避免中途改比较值导致半高半低脉冲。高级定时器用Preload+Update事件装载:

pwm_set_compare(duty);   // 实际写CCRx,等下一个UEV生效

别在HS导通期间改CCR,否则可能出现一个异常窄脉冲。

七、落坑清单

互补PWM没配死区→上下管直通炸管

位置式PID不抗饱和→过冲后慢恢复

采样点在开关边沿→读到尖峰

上电直接给满参考→冲击电流

动态malloc/printf进中断→时序崩

过压保护只告警不关PWM→输出冲高

轻载不关同步管→倒灌振荡

CCR中途更新→窄脉冲打管

积分项不限位→空载切负载超调

没留最小/最大占空比→占空比跑到0或100%

八、落地顺序

先配互补PWM+死区,示波器量上下管波形

开ADC触发采样,固定采样窗口

写增量PI,积分限幅+输出限幅

主循环轮询adc_ready,算占空比

加软启动斜坡和过压/过流硬保护

轻载切异步整流

先空载后带载,逐步调Kp/Ki

阶跃负载看超调,再微调微分

无OS数字电源的本质是“时序即控制”:控制环不是被调度出来的,是被PWM周期逼出来的。PID只是其中一小段,真正决定稳不稳的是——死区防直通、采样窗口固定、积分抗饱和、占空比硬限幅、保护优先于闭环。这几条守住,同步Buck裸机跑起来比带RTOS还稳;守不住,算法再漂亮也是先炸管再谈精度。

接下来你想优先完善哪一部分:参数整定、双环控制还是保护恢复?

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭