1. 项目概述:基于STM32F103的数控Buck电源设计
去年在调试一个嵌入式项目时,发现市面上的可调电源要么价格昂贵,要么调节精度不够。于是萌生了自己设计一款数控Buck电源的想法。经过三个月的反复迭代,最终用STM32F103实现了效率高达96%的同步整流Buck电源,通过USART指令就能实时调整输出电压(5-24V可调),满载5A时纹波控制在30mV以内。
这个设计的核心价值在于:
- 完全开源:原理图、PCB、固件全部公开
- 超高性价比:BOM成本控制在50元以内
- 工业级稳定性:采用增量式PI算法,电压调整时间<50ms
- 灵活扩展:支持USART、ADC按键、PWM等多种调压方式
2. 硬件设计详解
2.1 功率级设计
主电路采用同步整流架构,相比传统二极管方案效率提升显著:
plaintext复制[传统方案]
输入24V→输出12V@5A时效率约88%
损耗分析:
- 整流二极管正向压降0.7V → 导通损耗3.5W
- 开关损耗约1.2W
总损耗约4.7W
[同步整流方案]
相同工况下效率96%
损耗分析:
- MOS管Rds(on)=44mΩ → 导通损耗0.968W
- 开关损耗约0.8W
总损耗仅1.768W
关键器件选型:
- 功率MOS:NRF540N (Vds=40V, Id=33A, Rds(on)=44mΩ)
- 驱动芯片:IR2104 (典型传播延迟520ns)
- 输出电感:47μH一体成型电感(饱和电流>8A)
2.2 驱动电路设计
自举电路是半桥驱动的核心,我的优化方案:
c复制// 死区时间计算(系统时钟72MHz)
DeadTime = (t_rise + t_fall) * 72 - 1
= (60ns + 50ns) * 72 - 1
≈ 63
实际配置:
c复制TIM_BDTRInitTypeDef bdtr;
bdtr.TIM_DeadTime = 0x3F; // 63个时钟周期≈0.875us
TIM_BDTRConfig(TIM1, &bdtr);
2.3 采样电路设计
电流采样采用三线制锰铜电阻(0.01Ω)配合LM385搭建差分放大电路:
plaintext复制放大倍数计算:
Gain = Rf/Rin = 100k/1k = 100倍
12bit ADC分辨率:
最小电流分辨率 = 3.3V/(4096*100*0.01Ω) ≈ 8mA
电压采样采用电阻分压+RC滤波:
plaintext复制分压比 = R2/(R1+R2) = 10k/(10k+30k) = 0.25
24V满量程对应ADC值 = 24*0.25/3.3*4096 ≈ 7445
3. 软件实现关键点
3.1 PWM生成配置
TIM1通道1/2配置为互补PWM输出:
c复制void PWM_Init(uint16_t arr, uint16_t psc) {
// 时基配置
TIM_TimeBaseInitTypeDef tim;
tim.TIM_Period = arr - 1; // 自动重装载值
tim.TIM_Prescaler = psc - 1; // 预分频
tim.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &tim);
// PWM模式配置
TIM_OCInitTypeDef oc;
TIM_OCStructInit(&oc);
oc.TIM_OCMode = TIM_OCMode_PWM1;
oc.TIM_OutputState = ENABLE;
oc.TIM_Pulse = arr/2; // 初始占空比50%
TIM_OC1Init(TIM1, &oc);
TIM_OC2Init(TIM1, &oc);
// 刹车和死区配置
TIM_BDTRInitTypeDef bdtr;
bdtr.TIM_DeadTime = 0x3F;
TIM_BDTRConfig(TIM1, &bdtr);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
3.2 增量式PI算法实现
相比位置式PI,增量式算法具有以下优势:
- 无积分饱和问题
- 手动/自动切换无扰动
- 更易实现输出限幅
代码实现:
c复制typedef struct {
float Kp;
float Ki;
float Err[2]; // 保存当前和前一次误差
} PI_Controller;
float PI_Calculate(PI_Controller *pi, float target, float actual) {
float err = target - actual;
float delta = pi->Kp*(err - pi->Err[0]) + pi->Ki*err;
// 更新误差记录
pi->Err[0] = pi->Err[1];
pi->Err[1] = err;
return delta;
}
参数整定步骤:
- 先将Ki设为0,逐渐增大Kp直到系统出现等幅振荡
- 记录临界增益Kc和振荡周期Tc
- 根据Ziegler-Nichols公式:
- Kp = 0.45*Kc
- Ki = 0.54*Kc/Tc
3.3 ADC采样同步策略
为避免PWM干扰ADC采样,采用以下同步机制:
c复制// 在PWM周期中点触发ADC采样
void TIM1_UP_IRQHandler(void) {
if(TIM_GetITStatus(TIM1, TIM_IT_Update)) {
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
}
ADC配置要点:
c复制void ADC_Config(void) {
ADC_InitTypeDef adc;
ADC_StructInit(&adc);
adc.ADC_ContinuousConvMode = DISABLE;
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc.ADC_ScanConvMode = DISABLE;
ADC_Init(ADC1, &adc);
// 配置采样通道和时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);
// 校准ADC
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
4. 实测性能与优化技巧
4.1 效率测试数据
| 输出条件 | 效率 | 关键损耗点 |
|---|---|---|
| 24V→12V@1A | 97.2% | 驱动损耗占比60% |
| 24V→5V@3A | 95.8% | 电感DCR损耗占比45% |
| 24V→18V@5A | 96.1% | MOS导通损耗占比70% |
提升效率的关键措施:
- 选用低Qg的MOS管(NRF540N的Qg仅23nC)
- 优化死区时间(实测0.8-1us最佳)
- 采用低DCR电感(实测一体成型电感优于传统绕线电感)
4.2 动态响应测试
负载阶跃响应(0-5A突变):
plaintext复制电压跌落:<0.05V
恢复时间:<2ms
超调量:<1%
优化手段:
- 在PI输出端增加前馈补偿
- 采用变参数PI(小误差时增大Ki)
- 输出电容采用低ESR的固态电容
4.3 常见问题排查
问题1:MOS管发热严重
- 检查死区时间是否足够(示波器观察上下管Vgs波形)
- 测量驱动电压是否达到10V以上
- 确认MOS管栅极电阻取值合理(通常10-22Ω)
问题2:输出电压振荡
- 降低PI参数(特别是Ki)
- 检查ADC采样是否与PWM同步
- 确认反馈回路走线远离功率线路
问题3:轻载时效率骤降
- 启用脉冲跳跃模式(Burst Mode)
- 增加最小占空比限制(如5%)
- 优化同步整流管关断时序
5. 进阶功能扩展
5.1 恒流模式实现
在现有硬件基础上增加恒流功能:
c复制void Control_Loop(void) {
static uint8_t mode = VOLTAGE_MODE;
if(mode == VOLTAGE_MODE) {
// 常规电压环控制
duty = PI_Calculate(&voltage_pi, V_target, V_actual);
} else {
// 恒流模式
duty = PI_Calculate(¤t_pi, I_target, I_actual);
}
PWM_SetDuty(duty);
}
5.2 上位机通信协议
自定义简易通信协议:
plaintext复制帧格式:$[CMD][PARAM]#
示例:
- 设置电压12.5V:$VSET12.5#
- 读取电流值:$GICUR#
- 进入恒流模式:$MODE1#
5.3 参数存储方案
使用片内Flash模拟EEPROM:
c复制#define PARA_ADDR 0x0801F000 // 最后一页Flash
void Save_Parameters(void) {
FLASH_Unlock();
FLASH_ErasePage(PARA_ADDR);
uint32_t *p = (uint32_t*)¶meters;
for(int i=0; i<sizeof(parameters)/4; i++) {
FLASH_ProgramWord(PARA_ADDR + i*4, p[i]);
}
FLASH_Lock();
}
这个项目最让我惊喜的是STM32F103的性能潜力——通过合理设计,这颗"老将"依然能实现媲美专业电源控制器的性能。整个开发过程中,最大的收获是对开关电源闭环控制的深入理解,特别是如何平衡动态响应和稳定性这对矛盾体。
