简介:基于STM32G474的四开关同步整流Buck-Boost数字电源开源项目,定位为面向电源设计与嵌入式开发者的完整工程参考,尤其适合毕业设计借鉴与产品预研。方案采用同步整流加Buck-Boost拓扑,支持TypeC接口PD诱骗输入和DC5.5接口输入,输入输出电压最高48V、电流最高10A,可覆盖笔记本电脑、车载电源等对输入输出压差较大或需多输入源切换的场合。工程共1655个文件,以820个C源码、483个头文件、119个汇编文件为主体,另有uvprojx/uvoptx工程配置、ICF链接脚本、SVD外设描述、bin固件及axf调试文件,对应原理图、PCB和外壳3D模型一并打包,压缩包12.36MB,目录结构清晰。已有377人学习下载。作者把整套毕业设计资料开放,从HAL库底层驱动到Buck-Boost控制逻辑、HRTIM定时器配置均有保留,配合调试文件可快速编译烧录,适合希望研究数字电源同步整流技术或基于G474做定制的读者深入参考。
1. 基于STM32的同步整流Buck-Boost数字电源 开源:为什么值得你复刻
基于STM32的同步整流Buck-Boost数字电源,拆开看是四件事:输入电压能升也能降,功率级用四颗MOS管搭成非反相Buck-Boost,同步整流替代二极管续流,再把环路补偿、保护逻辑、模式切换全部写进STM32固件,整份工程开源。相比传统模拟方案,调环路不用换电阻电容,改参数就是改代码,实验自由度完全不同;相比普通Buck-Boost,同步整流在低压大电流场景下效率能高出五到十个百分点。适合正在从单片机电赛转向电源方向的开发者,也适合要做电池充放电、光伏MPPT、实验室可调电源的嵌入式工程师。真正上手后会意识到,最难的不是PID写不出来,而是死区、采样点、模式切换这些摸不着的地方在背后埋雷。
2. 主功率拓扑与硬件选型:四开关Buck-Boost为什么被数字电源偏爱
2.1 非反相四开关Buck-Boost的三种工作状态与切换策略
这个拓扑在网上一搜“四开关 buck-boost”能看到大量资料,它的基本结构是四颗MOS管夹一颗电感:Q1和Q2组成半桥,Q3和Q4组成另一个半桥,电感挂在两个半桥中点之间,输出正极和输入正极同极性,所以叫非反相。之所以不选传统反相Buck-Boost,是因为反相拓扑输出是负压,且开关管应力高,用在数字电源上还得加隔离或电平移位,麻烦且不必要。四开关结构的优势是每个模式只有两颗管高频动作,另两颗管要么常开要么常关,开关损耗可控。
Buck模式的条件是VIN明显高于VOUT。此时Q1做主开关,Q2做同步整流管,Q4常开把输出地接到系统地,Q3常闭。能量从输入经Q1、电感、Q4送到输出,Q2在Q1关断期间续流。Boost模式相反,VIN明显低于VOUT,此时Q3做主开关,Q4做同步整流管,Q1常开,Q2常闭。能量从输入经Q1、电感、Q4到地储能,Q3开通时把电感能量举到输出。
真正让新手困惑的是VIN接近VOUT的情况。这时候Buck和Boost的占空比都趋于极限,环路增益异常,输出电压纹波变大。常见做法是划一个滞回区间:VIN小于0.9倍VOUT时进Boost,VIN大于1.1倍VOUT时进Buck,中间这20%区间让电路工作在真正的Buck-Boost模式,即四颗管全部参与高频开关,两组半桥互补导通,电感两端得到正向和反向交替的电压,输出等于输入乘以两个占空比的组合。还有一种更激进的做法叫直通模式:当VIN与VOUT误差极小,直接让Q1和Q3同时导通、Q2和Q4关断,输入经电感近乎直通到输出,效率最高,代价是没有调节能力,只适合旁路或者电压匹配场景。
| 工作模式 | 高频开关管 | 常开管 | 常闭管 | 适用条件 |
|---|---|---|---|---|
| Buck | Q1、Q2 | Q4 | Q3 | VIN > 1.1 × VOUT |
| Boost | Q3、Q4 | Q1 | Q2 | VIN < 0.9 × VOUT |
| Buck-Boost | Q1、Q2、Q3、Q4全动 | 无 | 无 | VIN ≈ VOUT |
| 直通 | 无 | Q1、Q3 | Q2、Q4 | VIN ≈ VOUT且无需调节 |
模式切换不能用单阈值,否则输入电压在交界处抖动时,电路会在Buck和Boost之间来回跳,输出电压跟着抖。这也是数字电源相对模拟电源最舒服的地方:滞回区间、切换延时、切换后的占空比初始值都能用代码精确控制。我自己做的时候还会给每个模式配独立的软启动斜坡,切换前先把目标占空比预置到接近当前值,避免电感电流突变。
2.2 同步整流与死区:效率、炸管、自举电容这三件事
同步整流的概念不复杂:用MOSFET的沟道替代体二极管续流。体二极管导通压降0.7V左右,在3A负载下就是2W以上损耗,换成10毫欧的MOSFET沟道,损耗不到0.1W。但代价是引入了死区问题——两个管子不能在同一个瞬间导通,否则输入直接短路,炸管就是一瞬间的事。
STM32的高级定时器TIM1、TIM8,以及STM32G4系列的高分辨率定时器HRTIM,都支持互补PWM输出和硬件死区插入。死区寄存器直接写在定时器配置里,不需要外部逻辑芯片。死区时间的计算要按定时器时钟来:F103的TIM1挂在APB2上,时钟72MHz,死区寄存器每个tick约13.8ns,想设100ns死区就得写7到8个tick。F334和G4的HRTIM时钟可以跑到144MHz甚至更高,死区分辨率精细得多,这也是为什么“数控buck-boost电路”开源工程里主控芯片常见的是STM32G4或F334。
死区太短,管子还没完全关断,对管已经开通,直通炸管;死区太长,死区时间内电感电流只能走体二极管,效率掉下来,管子发热。我一般先把死区设为150ns跑起来,然后用示波器看两个栅极驱动波形,确认交叉点没有重叠再往下压。驱动芯片用半桥驱动器,像IR2104这类自带自举二极管的型号最省事,但要注意自举电容的容量和摆放位置。自举电容必须在低边管导通时被充电,如果Buck-Boost模式下两组半桥都在高频切换,高边驱动的自举电容充电时间可能不够,表现为输出电压上不去、驱动波形畸变甚至驱动芯片过温。常见做法是把自举电容加大到1uF到4.7uF,叠层陶瓷电容尽量贴近驱动芯片的VB和VS引脚,还可以在VS引脚对地串一个小电阻限制负压尖峰。
同步整流还有一个隐含问题:逆流。电感电流在轻载或空载时会反向,电流从输出流回输入。如果电路不允许能量回馈,就得加入反向电流检测,在电流过零前关断同步整流管,让系统进入断续模式。这个逻辑放在比较器或者MCU里都可以,但MCU响应太慢,更可靠的做法是用比较器输出直接触发定时器刹车,让PWM在几个时钟周期内关断。
2.3 电流采样与硬件保护:三路反馈加一条保命刹车
数字电源至少要采三路模拟量:输入电压VIN、输出电压VOUT、电感电流IL。VIN用于模式判定,VOUT是闭环调节目标,IL用于过流保护,如果后续要加电流内环,IL就是内环反馈。采样方案上,输出电压用电阻分压后进ADC,输入电压同样分压,注意两个分压电阻的阻值要尽量大,减少静态功耗。电感电流的采法有几种,采样电阻加运算放大器最直接,但采样电阻上的功耗会吃掉同步整流省下的效率;电感DCR采样不增加损耗,但需要RC网络匹配电感的时间常数,且DCR随温度漂移,精度一般只够做保护,不够做高精度电流环。
数字电源最容易翻车的不是控制环,而是过流保护这关。MCU固件里做判断,哪怕中断优先级再高,从电流突变到PWM关断也要微秒级时间,而MOSFET在短路时几个微秒就可能过热损坏。所以必须用硬件比较器:采样电阻上的电压直接进比较器,比较器输出接到STM32的BKIN引脚,BKIN触发后由定时器硬件刹车逻辑直接封锁PWM输出,这个路径不经过CPU,延迟只有比较器和门电路的几百纳秒。BKIN要配置成锁存模式,触发后PWM保持关闭,直到软件确认故障解除后主动解锁。
电感选型方面,先用纹波电流公式估算:ΔI = VIN × D / (fsw × L)。设计目标是把纹波电流控制在满载电流的20%到30%。举个例子,输入12V、输出5V、开关频率100kHz、占空比约0.42,想控制3A负载的纹波在0.75A左右,L需要大约67uH,取标准值68uH。功率MOSFET的耐压按输入最高电压留30%余量,额定电流按峰值电流留两倍余量。栅极驱动电阻先用10欧姆,栅极波形振铃明显再往上加,振铃太凶会导致EMI超标和管子发热。
3. 数字控制环路与固件实现:从ADC采样到PID整定的完整链路
3.1 用STM32高级定时器产出互补PWM:中心对齐模式与死区寄存器
数字电源的PWM输出用中心对齐模式,也就是三角波载波,而不是边沿对齐。原因是中心对齐模式下,PWM波形的上升沿和下降沿对称,电感电流纹波更小,EMI更均衡。更重要的是,ADC采样点可以放在三角波的顶点,这个时刻正好对应电感电流的平均值,采出来的电流和电压不受开关纹波影响。配置TIM1输出互补PWM的基本代码如下:
// 以HAL库风格示意,实际工程中需按芯片型号调整时钟和引脚 TIM_HandleTypeDef htim1; TIM_OC_InitTypeDef sConfigOC; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig; htim1.Instance = TIM1; htim1.Init.Prescaler = 0; // 72MHz / (0+1) = 72MHz htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中心对齐模式1 htim1.Init.Period = 720 - 1; // 开关频率 = 72MHz / 720 / 2 = 50kHz htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim1); sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 360; // 初始占空比50% sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1); sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime = 7; // 死区约 7 x 13.8ns = 97ns sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW; sBreakDeadTimeConfig.BreakFilter = 0; sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); // 启动互补输出这段配置里有几个关键参数值得展开。Prescaler设为0让定时器直接吃72MHz时钟;Period设为719,加1后是720个计数周期,中心对齐模式下开关频率等于时钟除以Period的两倍,所以50kHz。Pulse是占空比比较值,360对应50%占空比,运行时不能直接改这个寄存器的值,要用__HAL_TIM_SET_COMPARE更新,PID输出就是写给这个函数的。DeadTime写7,是死区时间tick数,不同系列单片机的死区寄存器位宽不同,F103的DTG是8位,G4的HRTIM更复杂,但逻辑一致。BreakState使能后,BKIN引脚一旦被拉低,PWM输出立即进入安全状态,这把硬件刹车和软件控制分开了,是数字电源固件里保命的一道防线。
有一点必须留意:高级定时器输出PWM前还有个主输出使能位MOE,要调用HAL_TIM_PWM_Start才能置位。如果调试时发现引脚没有任何波形,先查MOE是否被清零,它会被BKIN刹车事件自动清掉。这是新手排查“为什么没有输出”的第一站。
3.2 ADC注入组同步采样与数字滤波:避开开关边沿噪声
ADC采样的时机决定了控制环的性能上限。如果PWM是中心对齐,电感电流纹波在三角波顶点处正好是平均值,同时在开关切换边沿处噪声最大。让ADC在顶点附近采样,等于从源头把开关噪声滤掉一大半,剩下的靠数字滤波就够了。实现方式是让定时器的更新事件或比较事件触发ADC注入组,注入组支持一次触发连续转换多个通道,正好把VIN、VOUT、IL三路一次采完。
// 以ADC注入组触发为例,示意配置流程 ADC_HandleTypeDef hadc1; ADC_InjectionConfTypeDef sConfigInjected; hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO; // TIM1触发注入组 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; HAL_ADC_Init(&hadc1); sConfigInjected.InjectedChannel = ADC_CHANNEL_5; // 输出反馈电压 sConfigInjected.InjectedRank = 1; sConfigInjected.InjectedNbrOfConversion = 3; // 三个注入通道 sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_71CYCLES_5; sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO; sConfigInjected.AutoInjectedConv = ENABLE; sConfigInjected.InjectedDiscontinuousConvMode = DISABLE; sConfigInjected.InjectedOffset = 0; HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected);注入组的好处是不需要频繁手工切换ADC通道。很多人用ADC连续扫描模式去“stm32 adc切换通道”,软件里反复改通道选择寄存器,采样时刻完全不可控,采出来的电压带着开关纹波又叠加了通道切换的毛刺,PID输出跟着抖。注入组配合定时器触发,数据是硬同步的,每次PWM周期自动更新一次采样值,CPU只需要在转换完成中断里读结果,代码清爽得多。
采样得到的是寄存器的原始值,用之前要换算成实际电压:VOUT = ADC值 × 参考电压 / 4095,再乘分压比。这里参考电压不要直接用理论上的3.3V,最好用万用表实测一下VDDA,或者对分压电阻做一次两点校准,否则输出电压会有两三个百分比的稳态误差。数据出来了之后再加一道均值滤波,比如连续采8次去掉最大最小再求平均,但均值窗口不能太长——对50kHz控制环来说,几次采样的延迟还能接受,几十次采样就把相位裕度吃没了。
3.3 数字PID与抗积分饱和:模式切换时的三套系数
控制环路的实现是数字电源的灵魂。差分方程用位置式PID,输出直接对应PWM比较值。公式是u[k] = Kp×e[k] + Ki×Σe + Kd×(e[k]-e[k-1])。理论不复杂,坑全在工程细节上:积分项饱和、输出限幅、模式切换时的参数跳变。
typedef struct { float Kp; // 比例系数 float Ki; // 积分系数 float Kd; // 微分系数 float target; // 目标输出电压 float integral; // 积分累计值 float prev_err; // 上一次误差 float out_min; // 输出下限,对应最小占空比 float out_max; // 输出上限,对应最大占空比 } PID_TypeDef; float PID_Update(PID_TypeDef *pid, float feedback) { float err = pid->target - feedback; // 积分分离:误差过大时停止积分,防止饱和 if (err > -5.0f && err < 5.0f) { pid->integral += err; // 积分钳位,限幅在输出范围的30%以内 if (pid->integral > pid->out_max * 0.3f) pid->integral = pid->out_max * 0.3f; if (pid->integral < -pid->out_max * 0.3f) pid->integral = -pid->out_max * 0.3f; } else { pid->integral = 0.0f; } float out = pid->Kp * err + pid->Ki * pid->integral + pid->Kd * (err - pid->prev_err); pid->prev_err = err; // 输出限幅,这也是PWM比较值的边界 if (out > pid->out_max) out = pid->out_max; if (out < pid->out_min) out = pid->out_min; return out; }参数整定的起点用临界比例度法:先只保留Kp,Ki和Kd设0,把Kp从0逐步加大,直到输出电压开始等幅振荡,记下此时的Kp和振荡周期Tu。然后按经验公式给参数:Kp取临界值的0.45倍,Ki取1.2倍的Kp除以Tu,Kd先不给,跑起来后再微调。对Buck-Boost数字电源来说,Buck模式和Boost模式的功率级特性不同,最好给三个模式各独立一组PID参数,模式切换时更换系数并把积分清零。
| 模式 | Kp | Ki | Kd | 说明 |
|---|---|---|---|---|
| Buck | 0.8 | 0.02 | 0.2 | 增益较低,Kp可稍大 |
| Boost | 0.6 | 0.01 | 0.3 | 右半平面零点限制带宽 |
| Buck-Boost | 0.7 | 0.015 | 0.25 | 折中,输出纹波较大 |
这个表只是起点,不是万能公式。Boost模式存在右半平面零点,占空比突然增大时输出电压会先跌一下再上升,单纯加大Kp会振荡,解决靠压低环路带宽,所以Boost模式的Kp通常比Buck小。整定过程中用串口把PID输出值实时打印出来看曲线,比用示波器猜有效得多。
3.4 软启动状态机与故障锁定:数字电源的“后悔药”
上电瞬间如果直接给满占空比,输出电容充电电流会巨大,输出电压过冲,轻则触发过压保护,重则炸整流管。软启动就是在固件里把目标电压从0逐步斜坡上升到设定值,本质上是数字电源的后悔药:知道上电会过冲,就让控制环循序渐进去追目标。状态机通常划分成关机、软启动、稳态运行、故障四个状态。
typedef enum { PWR_OFF, // 关机或待机 SOFT_START, // 软启动斜坡 RUN, // 稳态闭环 FAULT // 故障锁定 } PowerState_TypeDef; volatile PowerState_TypeDef pwr_state = PWR_OFF; float target_voltage = 0.0f; void Power_StateMachine(void) { switch (pwr_state) { case PWR_OFF: __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); if (enable_flag == 1) { pwr_state = SOFT_START; target_voltage = 0.0f; } break; case SOFT_START: // 每进一次中断目标电压爬升0.1V,中断频率10kHz则每秒爬升1000V target_voltage += 0.1f; pid.target = target_voltage; if (target_voltage >= vout_setpoint) { pid.target = vout_setpoint; pwr_state = RUN; } break; case RUN: // 正常闭环,PID持续运行 pid.target = vout_setpoint; break; case FAULT: // 故障锁定,必须按键或串口指令解锁 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); if (fault_unlock_flag == 1) { fault_unlock_flag = 0; pwr_state = PWR_OFF; } break; } }软启动斜坡速率要斟酌。对于输出电容100uF、负载1A的电源,每秒几百伏的爬升速度是安全的;如果输出电容更大,斜坡要放缓,否则等效充电电流还是会过大。FAULT状态的处理比大多数人想的重要:故障锁定后不能自己恢复,否则电路在反复短路的边缘来回试探,MOSFET很快就扛不住。解锁方式可以用按键,也可以串口发命令。我习惯加一个故障灯,不同闪法代表过压、过流、过温,这样调试时不用连电脑也能猜个大概。
4. 同步整流Buck-Boost踩坑排查:从炸管到环路振荡的5个典型问题
4.1 上电瞬间炸管:死区时间、自举电容与供电时序
现象:第一次上电,输入电流瞬间拉高,伴随一声脆响,MOSFET裂开,驱动芯片发热甚至鼓包。
原因:炸管基本逃不出三个方向。一是死区时间设得太短甚至为0,上下管直通,输入直接短路;二是自举电容没充电,高边驱动输出欠压,MOSFET没有完全导通,工作在线性区,热耗瞬间拉满;三是供电时序问题,MCU还没完成初始化就跑主程序,PWM输出状态不确定。
解决:上电调试时先把主功率输入断开,只给控制电路供电,用示波器看PWM和互补PWM的死区波形,确认交叉点没有重叠后再接主功率。自举电容的容量按开关频率核算,至少保证一个开关周期内高边驱动压降不超过0.5V。供电时序上,让主功率输入受控,可以用一个继电器或高边开关,等MCU初始化完成、PWM正常输出后再接通功率输入。
4.2 空载输出电压低频振荡:轻载增益与PID裕度不足
现象:接上电子负载,输出电压稳定;空载时输出电压在设定值附近以几赫兹到几十赫兹的频率来回摆动,LED灯都能看出闪烁。
原因:空载时电感电流断续,控制对象从连续模式的二阶系统变成离散的一阶系统,增益和相位特性全变了。PID参数是按连续模式调的,在断续模式下相位裕度不足,系统在低频处振荡。
解决:最简单粗暴的做法是空载时切到突发模式,输出电压低于阈值就开启PWM,高于阈值就关闭PWM,让电源工作在打嗝状态,损耗小且不振荡。另一种做法是让电路强制工作在连续模式,轻载时拉一个假负载,或者让同步整流管继续导通保持电感电流连续——这个操作会牺牲一点轻载效率。我一般选择突发模式,阈值设置要留滞回,比如设定值减1%开PWM,恢复设定值关PWM,否则会在临界点高频开关,输出纹波反而大。
4.3 输出纹波里有开关频率毛刺:采样点与PCB地环路
现象:示波器上一档测输出电压,能看到设定值之上叠加着明显的开关频率尖峰,纹波超过规格书要求。
原因:两个层面。一是ADC采样时刻没有对准载波顶点,采到了开关边沿附近的振铃;二是PCB布局上功率地和信号地没有分开,功率回路的电流流过ADC参考地,采样值被污染。
解决:先用软件修,确认ADC触发确实来自TIM1的TRGO,采样时刻在中心对齐模式的顶点事件上,同时把采样通道的采样时间拉长,比如71.5个ADC时钟周期。再查硬件,功率地的开尔文连接必须单独走线回到输入电容负极,ADC分压电阻的参考地也要单独引线,不做单点连接的话,功率电流会在地线上制造几百毫伏的差模噪声,这个噪声直接进ADC参考地,采出来的电压天生不准,属于玄学级问题但根源非常物理。
4.4 Buck-Boost交界处电压跌落:模式切换造成的控制断档
现象:输入电压缓慢变化,扫过输出设定值附近时,输出电压出现几百毫伏的下坠,严重时触发欠压保护。
原因:模式切换那一下,占空比和PID参数同时跳变,而电感电流是连续量,不可能瞬间跳变,控制量突变必然导致输出短暂失控。Buck-Boost模式下四管全动,死区损耗和开关损耗大,如果切换又频繁,效率还会掉下来。
解决:把模式切换做成带滞回的决策,判定基于输入电压与输出电压的比值而不是绝对值。切换时先把PWM锁定在安全占空比,等电感电流稳定再恢复PID输出,这个过程大概需要几十个开关周期。更稳妥的做法是在VIN约等于VOUT的区间直接进直通模式,四管中两管常开,没有斩波动作,输出电压接近输入电压,配合后级线性稳压做精细调整,整个切换过程没有任何占空比跳变。
4.5 把开源工程烧到自研板表现不同:时钟配置、ADC校准与引脚复用
现象:按开源项目原样的原理图画板打样,固件烧进去后输出不准,或完全没有输出,但程序能跑、指示灯正常。
原因:开源工程往往绑定特定主控型号和外部晶振,你可能换了型号或直接用内部RC时钟,PWM频率和ADC采样时序全偏了。ADC没有做偏移校准和增益校准,采样值系统性偏差。引脚复用冲突,同一个引脚既接了按键又接了PWM输出使能,初始化顺序不同导致外设配置被覆盖。
解决:拿到任何开源工程先查三件事。一是时钟树,确认初始化后SYSCLK、APB1、APB2、ADC时钟分别是多少,能否匹配工程里预期的PWM频率;二是核对启动文件里的堆栈大小,电源固件中断多,栈溢出会让程序跑飞;三是对照原理图逐个核对引脚复用功能,尤其注意ADC输入引脚不能同时被GPIO配置为输出模式。ADC校准用各系列自带的校准函数,比如F4系列要调用HAL_ADCEx_Calibration_Start,校准完再开始转换,否则绝对精度差不少。
5. 验证方法这条路到头:效率曲线、阶跃响应与上位机调参
验证数字电源,最核心的是三条:效率、动态响应、热表现。效率测试用可编程电子负载拉恒定电流,功率分析仪或两台万用表同时量输入输出,从10%负载一直测到110%负载,画出一条效率曲线。同步整流做得好不好,看半载到满载这一段效率是不是能保持在90%以上。如果效率在某个负载点突然下降,检查是否进入了断续模式,或者自举电容充电不足导致高边驱动损耗增加。
动态响应测试比效率测试更容易暴露环路问题。电子负载设置成在额定电流的50%到100%之间周期切换,示波器接输出端看电压波形。好的环路恢复时间在几百微秒以内,过冲不超过2%;如果波形发软、恢复时间长,说明带宽太低;如果出现衰减振荡,相位裕度不足。没有环路分析仪时可以这样估相位裕度:把Ki临时设为0,只用Kp,逐步增加直到输出出现等幅振荡,这时的Kp是临界值,用这个值反推当前相位裕度。这个操作不需要额外仪器,但试验时要盯紧输出,振荡幅度大了要立刻恢复参数。
想让调试效率再高一步,就得上位机调参。串口用USB转TTL接到板子,自定义一个简单协议:写PID参数、读实时电压电流、切换工作模式。上位机用PyQt或者网页串口工具都行,核心是把PID整定从“改代码烧录”变成“发命令看曲线”,配合实时波形显示,整定时间能缩短一个数量级。整定完成后把参数写入Flash,掉电不丢,后续量产直接抄参数。
我自己的习惯是先把死区、刹车、过压保护全部调好,再碰PID,顺序反了会得到血泪教训。做数字电源和写业务代码不一样,它不是黑匣子,每个异常都有物理层面的原因。做负载阶跃测试时别用手摸散热片,也别近距离看MOSFET——用红外热像仪远远地看,哪个管子先热就是问题所在。希望帮到你,祝一次点亮。
本文还有配套的精品资源,点击获取