1. 项目背景与需求分析
在开关电源设计中,BUCK降压电路是常见拓扑结构。为了优化电源性能,工程师们发现当驱动MOSFET的两路PWM信号相位相差180°时,可以有效降低输出电流纹波。这个现象背后的原理是:两路交错工作的PWM能够实现电流的相互补偿,从而平滑输出波形。
使用STM32F407单片机生成这样的PWM信号时,我们需要满足三个核心要求:
- 两路PWM频率必须严格同步(本例为48kHz)
- 相位差需要精确控制在180°
- 占空比需要能够实时调整(用于电压调节)
传统方案可能考虑使用单个定时器的不同通道,但这种方法存在局限性:STM32的同一定时器各通道PWM相位是固定的,无法自由调整。因此,我们需要采用更灵活的解决方案——使用两个独立定时器协同工作。
2. 硬件设计与定时器选型
2.1 定时器资源配置
本方案选用TIM8和TIM3两个高级定时器,主要基于以下考虑:
-
TIM8特性:
- 16位自动重装载寄存器
- 支持互补输出的PWM生成
- 最高时钟频率168MHz(APB2总线)
- 死区时间可编程
-
TIM3特性:
- 同样为16位定时器
- 最高时钟频率84MHz(APB1总线)
- 支持从模式触发
硬件连接上:
- TIM8_CH1 → PC6
- TIM3_CH2 → PC7
选择这两个引脚是因为它们:
- 都支持复用为定时器输出
- 位于同一端口,布线方便
- 电气特性一致(均为100MHz高速IO)
2.2 时钟树配置要点
STM32F407的时钟系统较为复杂,需要特别注意:
c复制// 系统时钟配置示例(使用HSE 8MHz晶振)
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 1. 配置PLL为168MHz
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 2. 配置时钟树
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // TIM3时钟42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // TIM8时钟84MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
关键细节:APB1总线上的定时器时钟实际是APB1时钟的2倍(当APB1 prescaler≠1时),因此TIM3实际工作频率为84MHz。
3. 软件实现详解
3.1 TIM8主定时器配置
TIM8作为主定时器,配置要点如下:
c复制void timer8_init(u32 arr, u16 psc) {
// GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8);
// 时基配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV4;
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
// PWM输出配置
TIM_OCInitTypeDef TIM_OCInitStructure = {0};
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM8, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable);
// 刹车和死区时间配置
TIM_BDTRInitTypeDef TIM_BDTRInitStructure = {0};
TIM_BDTRInitStructure.TIM_DeadTime = 0x50; // 死区时间
TIM_BDTRConfig(TIM8, &TIM_BDTRInitStructure);
TIM_ARRPreloadConfig(TIM8, ENABLE);
TIM_CtrlPWMOutputs(TIM8, ENABLE);
}
关键参数计算:
- PWM频率 = Timer时钟 / ((ARR + 1) * (PSC + 1))
- 本例中:84MHz / (1750 * 1) ≈ 48kHz
- 死区时间 = 0x50 * T_dts,其中T_dts = 1/(84MHz/4) ≈ 47.6ns
3.2 TIM3从定时器配置
TIM3配置与TIM8类似,但需要注意几点差异:
c复制void TIM3_Init(u32 arr, u16 psc) {
// GPIO初始化(PC7)
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_TIM3);
// 时基配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// PWM通道2配置
TIM_OCInitTypeDef TIM_OCInitStructure = {0};
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
}
特别注意:TIM3的时钟源是APB1(84MHz),而TIM8是APB2(168MHz)。虽然TIM3的时钟频率较低,但对于48kHz PWM已经足够。
4. 相位控制实现方案
4.1 硬件同步方案(推荐)
更精确的方案是使用定时器主从模式:
- 配置TIM8为主定时器,TIM3为从定时器
- 通过ITR3内部连接触发TIM3
- 设置TIM3为触发从模式
c复制// TIM8主模式配置
TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);
// TIM3从模式配置
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Trigger);
TIM_SelectInputTrigger(TIM3, TIM_TS_ITR3);
这种方案相位差更稳定,不受中断延迟影响。
4.2 软件延时方案
如原始代码所示,通过精确延时控制相位:
c复制TIM_Cmd(TIM8, ENABLE);
delay_us(10.4); // 180°相位差对应48kHz的延时
TIM_Cmd(TIM3, ENABLE);
计算原理:
- 48kHz周期T=20.83μs
- 180°相位差对应延时=T/2=10.42μs
实测技巧:使用DWT周期计数器实现纳秒级延时:
c复制#define DWT_CYCCNT *(volatile uint32_t *)0xE0001004
void delay_ns(uint32_t ns) {
uint32_t start = DWT_CYCCNT;
uint32_t cycles = (ns * (SystemCoreClock/1000000)) / 1000;
while((DWT_CYCCNT - start) < cycles);
}
5. 实测结果与性能优化
5.1 示波器测量数据
实测参数:
- 频率:48.01kHz(误差<0.1%)
- 相位差:179.8°(误差<0.2%)
- 上升时间:<15ns(100MHz GPIO配置)
5.2 死区时间优化
在BUCK电路中,死区时间设置至关重要:
- 过小会导致上下管直通
- 过大会增加导通损耗
推荐计算公式:
code复制死区时间 ≥ 功率管开通延迟 - 关断延迟 + 安全裕量
对于典型MOSFET(如IRF540N):
- 开通延迟:30ns
- 关断延迟:60ns
- 建议死区:100-150ns
对应寄存器值:
code复制DeadTime = (所需时间 * Timer时钟) / T_dts
例如:100ns @84MHz → 0x54
5.3 动态调整占空比
安全变更占空比的流程:
- 禁用PWM输出(TIMx->BDTR |= TIM_BDTR_MOE)
- 修改CCR值
- 等待下一次更新事件
- 重新使能输出
c复制void set_pwm_duty(TIM_TypeDef* TIMx, uint32_t channel, uint32_t duty) {
TIMx->BDTR &= ~TIM_BDTR_MOE; // 禁用输出
switch(channel) {
case 1: TIMx->CCR1 = duty; break;
case 2: TIMx->CCR2 = duty; break;
// 其他通道...
}
while(!(TIMx->SR & TIM_SR_UIF)); // 等待更新
TIMx->SR = ~TIM_SR_UIF;
TIMx->BDTR |= TIM_BDTR_MOE; // 重新使能
}
6. 常见问题排查
6.1 PWM无输出检查清单
- 确认GPIO已正确配置为复用功能
- 检查定时器时钟是否使能
- 验证BDTR寄存器的MOE位是否置1
- 测量引脚是否有其他硬件冲突
6.2 相位偏差过大处理
- 检查两个定时器的ARR值是否一致
- 确认预分频器设置正确
- 改用硬件同步方案(主从模式)
- 检查中断是否干扰了延时精度
6.3 高频应用建议
当PWM频率>100kHz时:
- 使用DMA自动更新CCR值
- 降低GPIO速度以减少振铃
- 考虑使用TIM1/TIM8的互补输出功能
我在实际项目中发现,当驱动大功率MOSFET时,在PWM输出端串联22Ω电阻可以有效抑制振铃现象。同时,在栅极添加10kΩ下拉电阻能防止意外导通。