4、PWM输出实战:使用CubeMX配置定时器PWM输出,编写代码控制LED亮度或蜂鸣器
本章将聚焦于STM32定时器最核心的应用之一——PWM输出。我们将以STM32F103系列为例,手把手演示如何使用STM32CubeMX进行图形化配置,生成初始化代码,并通过编写简单的控制逻辑,实现LED呼吸灯效果和蜂鸣器音调控制。通过本章实战,你将彻底理解PWM的频率、占空比、分辨率等核心参数,并掌握通过修改捕获/比较寄存器(CCR)来动态调整输出的方法。
上图展示了STM32定时器PWM输出的完整信号链路。从内部时钟源开始,经过预分频器(PSC)分频、自动重装载寄存器(ARR)定义周期、捕获/比较寄存器(CCR)设定占空比,最终通过GPIO引脚输出。理解这条链路是掌握PWM应用的基础,下面我们将从核心原理开始深入剖析。
4.1 PWM输出核心原理与参数计算
PWM(Pulse Width Modulation,脉冲宽度调制)是一种利用数字方波信号模拟模拟电平的技术。其核心思想是:在固定频率下,通过改变高电平持续的时间(即脉冲宽度)来改变输出的平均电压。对于LED而言,平均电压的变化会改变其亮度;对于蜂鸣器而言,频率决定音调,占空比决定音量大小。
在STM32定时器中,PWM输出依赖于三个关键寄存器:
- PSC(Prescaler,预分频器):对内部时钟进行分频,决定计数器的计数频率。例如,72MHz时钟经过PSC=71分频后,计数频率为1MHz(即每微秒计数一次)。
- ARR(Auto-Reload Register,自动重装载寄存器):定义计数器的计数上限,决定PWM周期。计数从0递增到ARR,然后归零重新开始。
- CCR(Capture/Compare Register,捕获/比较寄存器):用于与计数器当前值进行比较。当计数器值小于CCR时,输出高电平;大于等于CCR时,输出低电平(在PWM模式1下)。
由此可以推导出两个核心公式:
PWM频率 = 定时器时钟 / [(PSC+1) × (ARR+1)]
占空比 = CCR / (ARR+1) × 100%
例如,若定时器时钟为72MHz,设置PSC=71,ARR=999,则PWM频率为72MHz / (72 × 1000) = 1kHz。若CCR=500,则占空比为500/1000 = 50%。
知识结构表:PWM关键参数与配置关系
| 参数 | 寄存器 | 作用 | 典型值示例 |
|---|---|---|---|
| 预分频 | PSC | 降低计数频率,适配低速外设 | 71(72MHz→1MHz) |
| 周期 | ARR | 决定PWM频率,计数上限 | 999(1kHz输出) |
| 占空比 | CCR | 决定高电平时间比例 | 500(50%占空比) |
| 分辨率 | 1/(ARR+1) | 占空比调节的最小步进 | 0.1%(ARR=999时) |
4.2 CubeMX图形化配置操作流程
使用STM32CubeMX可以大幅简化定时器PWM的初始化过程。以下是基于STM32F103C8T6(Blue Pill)的完整配置步骤:
- 创建工程:打开CubeMX,选择MCU型号STM32F103C8Tx,进入Pinout & Configuration界面。
- 配置时钟树:在System Core → RCC中,将HSE设为Crystal/Ceramic Resonator。切换到Clock Configuration标签页,将系统时钟配置为72MHz(HCLK=72MHz,APB1=36MHz,APB2=72MHz)。
- 选择定时器:在Timers → TIM1中,勾选Channel1作为PWM Generation CH1。注意TIM1挂在APB2总线上,时钟为72MHz。
- 设置PWM参数:在TIM1的Configuration中,设置Prescaler=71,Counter Period(即ARR)=999,Auto-reload preload设为Enable。Pulse(即CCR)设为500作为初始值。
- 配置GPIO:系统会自动将PA8复用为TIM1_CH1输出。可在GPIO设置中确认Pin Configuration,输出模式为Alternate Function Push-Pull,速度设为High。
- 生成代码:在Project Manager中设置工程名称和IDE(如MDK-ARM V5),点击Generate Code生成初始化代码。
生成代码后,CubeMX会自动在main.c中调用MX_TIM1_Init()函数完成定时器初始化,并在tim.c中生成具体的寄存器配置代码。但需要注意的是,CubeMX默认不会启动PWM输出,需要用户手动调用启动函数。
经验提醒:在CubeMX中,Pulse字段的数值范围是0到ARR。若需要动态调整占空比,务必在生成代码后保留对定时器句柄htim1的访问权限,后续通过HAL库函数修改CCR值。
4.3 实战案例:LED呼吸灯与蜂鸣器音调控制
本案例将实现两个功能:一是LED呼吸灯效果(亮度从暗到亮再变暗的循环),二是蜂鸣器发出不同音调。我们将使用TIM1_CH1驱动LED,TIM2_CH1驱动蜂鸣器,通过修改CCR值实现动态控制。
硬件连接:
- PA8(TIM1_CH1)→ 220Ω电阻 → LED正极,LED负极接GND
- PA0(TIM2_CH1)→ 蜂鸣器正极(有源蜂鸣器需串联三极管驱动),蜂鸣器负极接GND
软件实现步骤:
首先,在CubeMX中额外配置TIM2_CH1,设置PSC=71,ARR=999(与TIM1相同频率1kHz)。生成代码后,在main.c的main()函数中添加以下代码:
/* 启动PWM输出 */
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
/* 呼吸灯变量 */
uint16_t brightness = 0;
uint8_t direction = 1; // 1=变亮,0=变暗
while (1)
{
/* LED呼吸灯控制 */
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, brightness);
if (direction) {
brightness += 5;
if (brightness >= 1000) direction = 0;
} else {
brightness -= 5;
if (brightness <= 0) direction = 1;
}
/* 蜂鸣器音调控制:每500ms切换一次频率 */
static uint8_t tone_index = 0;
static uint32_t last_tick = 0;
uint16_t tones[] = {500, 400, 300, 200}; // 对应CCR值,频率依次升高
if (HAL_GetTick() - last_tick > 500) {
last_tick = HAL_GetTick();
tone_index = (tone_index + 1) % 4;
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, tones[tone_index]);
}
HAL_Delay(10); // 控制呼吸灯变化速度
}
在上述代码中,__HAL_TIM_SET_COMPARE()宏用于直接修改CCR寄存器值。呼吸灯通过线性增减brightness变量实现亮度渐变;蜂鸣器则通过切换不同的CCR值改变占空比,从而改变音量(注意:有源蜂鸣器的音调由内部振荡器决定,占空比只影响音量;若要改变音调需使用无源蜂鸣器并改变PWM频率)。
风险提示:直接驱动蜂鸣器时,若蜂鸣器工作电流较大(超过20mA),必须使用三极管或MOSFET进行功率放大,否则可能损坏STM32的GPIO引脚。此外,LED串联电阻不可省略,否则电流过大会烧毁LED。
4.4 常见误区与调优复盘
在实际调试中,初学者常遇到以下问题:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| LED不亮 | 未调用HAL_TIM_PWM_Start() | 检查是否启动PWM输出 |
| LED亮度不变 | CCR值未更新或更新频率过低 | 确认使用__HAL_TIM_SET_COMPARE且循环中有延时 |
| 蜂鸣器无声 | GPIO配置错误或驱动能力不足 | 检查复用功能配置,增加三极管驱动 |
| PWM频率不对 | PSC或ARR计算错误 | 重新计算:频率=72MHz/[(PSC+1)(ARR+1)] |
调优建议:对于呼吸灯效果,若感觉变化不够平滑,可减小brightness的步进值(如从5改为2)并增加HAL_Delay时间;若感觉变化太慢,则反向调整。对于蜂鸣器,若需要更丰富的音调,可增加tones数组的元素数量,并配合定时器中断实现更精确的切换时序。
复盘总结:本章实战的核心在于理解PWM的三个关键寄存器及其关系。CubeMX虽然简化了初始化过程,但开发者仍需掌握底层原理,才能在遇到问题时快速定位。建议读者在完成基础实验后,尝试修改PSC和ARR值,观察频率和分辨率的变化,加深理解。
案例拆解表:LED呼吸灯调试过程
| 调试阶段 | 初始现象 | 分析过程 | 关键操作 | 结果 |
|---|---|---|---|---|
| 首次上电 | LED常亮不闪烁 | 怀疑PWM未启动或CCR未更新 | 检查代码,发现遗漏HAL_TIM_PWM_Start() | 添加启动函数后LED开始呼吸 |
| 亮度跳变 | LED亮度呈阶梯状变化 | brightness步进过大,人眼可感知 | 将步进从10改为3,延时从5ms改为10ms | 过渡更平滑 |
| 最高亮度不足 | LED最亮时仍偏暗 | CCR最大值未达到ARR | 确认brightness上限设为1000(等于ARR+1) | 亮度达到100% |
通过本章的学习,你已经掌握了STM32定时器PWM输出的完整流程。从CubeMX配置到代码编写,从LED亮度控制到蜂鸣器驱动,这些技能将为你后续学习电机控制、电源管理等更复杂的PWM应用打下坚实基础。在实际项目中,PWM输出常用于电机调速、舵机控制、DAC模拟输出等场景,其核心思想都是通过调整占空比来控制输出能量。