1. PWM技术基础与STM32应用场景
PWM(脉冲宽度调制)是嵌入式开发中最常用的模拟信号控制技术之一。在STM32项目中,从电机调速到LED调光,几乎每个需要模拟量控制的场景都会用到PWM。我曾在工业伺服控制器项目中使用STM32F4系列芯片同时生成16路PWM信号,控制精度达到0.1%的占空比分辨率。
PWM的本质是通过调节数字信号的占空比来等效模拟电压输出。例如在LED调光应用中,虽然GPIO只能输出0V或3.3V,但通过快速切换高低电平(比如100Hz频率下10ms周期内高电平持续3ms),人眼会感知到相当于30%亮度的效果。STM32的定时器模块为此提供了硬件级支持,无需CPU持续干预。
关键理解:占空比(Duty Cycle) = 高电平时间 / 周期时间 × 100%。例如3ms高电平 + 7ms低电平的波形,占空比为30%。
2. STM32定时器系统深度解析
2.1 定时器类型与PWM能力
STM32家族包含基本定时器(TIM6/7)、通用定时器(TIM2-5)和高级定时器(TIM1/8),它们的PWM生成能力差异显著:
| 定时器类型 | 通道数 | 互补输出 | 死区控制 | 典型应用场景 |
|---|---|---|---|---|
| 基本定时器 | 无 | 不支持 | 不支持 | 时基生成 |
| 通用定时器 | 4 | 可选 | 不支持 | 电机控制 |
| 高级定时器 | 6 | 支持 | 支持 | 电源逆变 |
以常见的TIM3为例,作为通用定时器:
- 16位自动重装载寄存器(ARR)决定PWM周期
- 16位捕获比较寄存器(CCR)决定脉冲宽度
- 支持边沿对齐和中心对齐模式
2.2 时钟树配置实战
PWM频率的计算公式为:
code复制PWM频率 = 定时器时钟源 / (PSC + 1) / (ARR + 1)
假设使用72MHz系统时钟,欲生成1kHz PWM:
- 预分频器(PSC)设为71 → 72MHz/(71+1)=1MHz
- 自动重装载值(ARR)设为999 → 1MHz/(999+1)=1kHz
此时占空比分辨率=1/(999+1)=0.1%
调试技巧:使用STM32CubeMX的Clock Configuration界面可直观验证时钟路径,避免手动计算错误。
3. PWM输出配置全流程
3.1 硬件环境搭建
以STM32F103C8T6驱动LED为例:
- LED阳极接PA6(TIM3_CH1)
- 阴极通过220Ω电阻接地
- 示波器探头接PA6测量波形
3.2 CubeMX配置步骤
- 在Pinout视图启用TIM3
- Channel1选择"PWM Generation CH1"
- Parameter Settings中:
- Prescaler = 71
- Counter Mode = Up
- Period = 999
- Pulse = 初始占空比值
- 生成代码时勾选"Generate peripheral initialization as a pair of .c/.h files"
3.3 关键代码实现
c复制// 启动PWM
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
// 动态调整占空比
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 300); // 30%占空比
实测中我发现,直接修改CCR寄存器可能导致当前周期波形异常,更稳妥的方式是:
c复制TIM3->CCR1 = 300; // 更新占空比
while(!(TIM3->SR & TIM_SR_CC1IF)); // 等待更新完成
4. 高级PWM应用技巧
4.1 互补输出与死区控制
在电机驱动等H桥电路中,需要互补的PWM信号且必须插入死区时间防止上下管直通。以TIM1为例:
c复制// 死区时间计算
// DeadTime = (DTG[7:0] + 1) * Tdtg
// 其中Tdtg取决于时钟分频
TIM1->BDTR |= TIM_BDTR_DTG_0 | TIM_BDTR_DTG_3; // 设置9个时钟周期的死区
TIM1->BDTR |= TIM_BDTR_MOE; // 主输出使能
4.2 带刹车的PWM控制
当检测到过流时,需要立即关闭PWM输出。通过配置刹车输入:
c复制TIM1->BDTR |= TIM_BDTR_BKE; // 刹车使能
TIM1->BDTR |= TIM_BDTR_BKP; // 刹车极性(高电平触发)
TIM1->BDTR |= TIM_BDTR_AOE; // 自动输出使能
5. 常见问题排查指南
5.1 PWM无输出检查清单
- 时钟未启用
- 检查__HAL_RCC_TIMx_CLK_ENABLE()是否调用
- GPIO配置错误
- 确认引脚已配置为复用功能
- 使用CubeMX检查Alternate Function设置
- 定时器未启动
- 遗漏HAL_TIM_PWM_Start()调用
- 占空比为0或100%
- 检查CCR值是否在0-ARR范围内
5.2 波形畸变解决方案
现象:PWM上升沿/下降沿出现振铃
- 增加GPIO速度设置:GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH
- 缩短导线长度,在输出端并联100pF电容
- 改用推挽输出模式:GPIO_InitStruct.Mode = GPIO_MODE_AF_PP
现象:占空比突然变化时出现毛刺
- 使用TIM_OCMode_PWM2模式(CCR大于比较值时输出有效)
- 在中断中更新CCR值,而非主循环
6. 实际项目经验分享
在开发3D打印机加热控制时,需要同时控制多个加热棒的PWM输出。通过以下优化实现了0.5°C的温度稳定性:
- 使用TIM1和TIM8的同步功能
c复制TIM1->CR2 |= TIM_CR2_MMS_1; // TIM1作主
TIM8->SMCR |= TIM_SMCR_SMS_2; // TIM8为从模式
- 采用中心对齐模式减少电流突变
c复制TIM1->CR1 |= TIM_CR1_CMS_1; // 中心对齐模式1
- PWM频率选择最佳点
- 对于加热棒负载,实测1-3kHz范围内纹波最小
- 过高频率导致MOS管开关损耗增加
- 过低频率引起可见光闪烁(若用于LED)
调试中发现,当多个PWM通道同步运行时,偶尔会出现相位偏移。解决方法是在每次温度调整时:
c复制TIM1->EGR = TIM_EGR_UG; // 产生更新事件
TIM8->EGR = TIM_EGR_UG;
