一、前言
在嵌入式开发里,定时器 PWM 绝对是日常使用频率最高的外设功能之一。小到开发板上的 LED 呼吸效果,大到直流电机调速、舵机角度控制、简易 DCDC 调压,凡是需要连续可调模拟量控制的场景,基本都会用到 PWM。
很多刚接触 STM32 的朋友会形成一个误区:认为 PWM 能够直接输出连续变化的模拟电压。实际并不是这样。PWM 从头到尾都只是高低电平交替的数字方波,不存在中间电压。我们看到 LED 明暗变化、电机转速快慢,本质是依靠负载特性、人眼视觉暂留或者机械惯性,对高频方波做 “平均等效” 得到的效果。
本文从 PWM 底层原理、关键参数、定时器硬件运行逻辑入手,结合呼吸灯、直流电机调速两个最经典工程案例,讲清楚实际开发该如何配置、如何避坑,最后附上可直接编译运行的 STM32 示例代码。
二、PWM 核心基础原理
2.1 什么是 PWM
PWM 全称脉冲宽度调制(Pulse Width Modulation)。核心规则:信号周期保持不变,只调整一个周期内高电平持续的时间(脉宽)。
普通 GPIO 只有全开、全关两种状态,只能控制设备启动或者停止。而 PWM 依靠高速反复通断,利用平均效应,实现无级调节,这也是它在控制领域被广泛使用的根本原因。
2.2 PWM 关键工程参数
所有单片机定时器配置 PWM,都围绕下面几个参数展开,弄懂之后不管换 STM32 哪个型号,配置思路不会变。
- 周期:一次完整高电平 + 低电平的总时长。PWM 标准设计思路:周期固定,只修改脉宽。
- 频率:周期的倒数,代表每秒产生多少组脉冲波形。频率选择没有统一标准,需要结合负载特性决定。
- 脉宽:单个周期中高电平持续时间,是我们程序中动态调整的对象。
- 占空比:高电平时长 / 完整周期时长,通常使用百分比表示。
2.3 等效平均电压
负载最终响应的是一个周期内的平均电压,公式:
举个例子,3.3V 单片机系统:
- 占空比 0%,平均电压 0V,LED 熄灭、电机停转;
- 占空比 50%,平均电压 1.65V;
- 占空比 100%,持续高电平,满功率输出。
依靠连续修改占空比,就能等效实现 0~3.3V 电压连续调节。
三、STM32 定时器生成 PWM 硬件机制
3.1 定时器计数逻辑
STM32 硬件 PWM 依托通用定时器 / 高级定时器实现。定时器从 0 开始向上不断计数,不断和捕获比较寄存器 CCR 做对比:
- CNT(计数值) < CCR:输出指定电平
- CNT(计数值) ≥ CCR:电平翻转
- CNT 计数达到 ARR 自动重装载值,计数器清零,重新开始新一轮计数,循环产生稳定方波。
3.2 两个核心寄存器分工
- ARR(自动重装载寄存器):决定 PWM 周期、输出频率,工程上配置完成后一般固定不变;
- CCR(捕获比较寄存器):决定脉宽、占空比,程序运行时动态修改 CCR 数值,就能实时调节输出效果。
工程经验:固定 ARR 调整 CCR,不要运行过程中频繁修改 ARR,波形容易紊乱。
3.3 PWM 模式 1 与模式 2 区别(向上计数)
- PWM 模式 1:CNT < CCR 时输出有效电平(高电平),常用在呼吸灯、电机调速;
- PWM 模式 2:CNT < CCR 时输出无效电平(低电平),适合需要反向逻辑的场景。
日常开发绝大多数场景直接选用 PWM 模式 1 即可。
四、实战场景一:呼吸灯原理与实现逻辑
4.1 呼吸灯实现本质
单纯调用 HAL_Delay 切换 IO 电平,得到的只是闪烁,不是呼吸效果。 呼吸灯平滑明暗渐变,核心思路:持续缓慢修改 PWM 对应的 CCR 数值,连续改变占空比,LED 平均电压平滑变化,肉眼看到明暗渐变。
执行流程:
- CCR 数值从小到大增加 → 占空比上升 → LED 由暗逐渐变亮;
- CCR 到达最大值后,数值逐步减小 → 占空比下降 → LED 由亮慢慢变暗;
- 循环往复,形成持续呼吸效果。
4.2 实际配置要点
LED 属于纯电阻负载,没有惯性,频率太低会肉眼看到明显频闪。 推荐 PWM 频率区间:1kHz ~ 5kHz。
同时调整 CCR 的步长不能太大,步长过大会出现亮度阶梯跳动;步长过小,明暗变化速度太慢。
4.3 硬件注意事项
单片机 IO 口驱动电流有限,如果是大功率 LED,不能直接用 IO 驱动,需要增加三极管或者 MOS 管扩流。普通开发板板载小 LED 可以直接驱动。
五、实战场景二:直流电机调速原理与实现逻辑
5.1 调速底层逻辑
直流电机转速和两端平均电压正相关,和 LED 调光原理相通。 电机属于机械惯性负载,转子惯性相当于天然的低通滤波器,对方波有平滑作用,对波形的敏感程度比 LED 低很多。
5.2 PWM 频率选型(重点)
电机频率不能直接照搬 LED 参数,频率直接影响运行噪音:
- 200Hz ~ 1kHz:低速扭矩充足,但运行时会产生刺耳啸叫;
- 8kHz ~ 20kHz:超出人耳听觉范围,运行安静,是小型直流电机首选区间。
注意:频率也不要无限制拉高,过高的频率会增大定时器中断、MOS 管开关损耗。
5.3 开发常见问题
- 低速抖动无法启动:占空比太低,平均电压不足以克服静摩擦力,代码中需要设置最小启动占空比;
- 启停冲击过大:不要直接从 0% 跳转到 100% 占空比,需要做软启动,缓慢增加占空比;
- 单片机 IO 不能直接驱动电机:电机启动电流大,必须搭配 DRV8833、L298N 这类驱动芯片。
六、呼吸灯 VS 直流电机调速对比汇总
表格
| 对比项 | LED 呼吸灯 | 直流电机调速 |
|---|---|---|
| 推荐 PWM 频率 | 1kHz~5kHz | 8kHz~20kHz |
| 负载类型 | 纯电阻负载,无惯性 | 机械惯性负载,自带滤波效果 |
| 控制逻辑 | 循环增减占空比,往复变化 | 稳态固定占空比,按需变速 |
| 设计目标 | 调节等效电压控制亮度 | 调节等效电压控制转速 |
| 驱动需求 | 小功率 LED 可直驱 | 必须使用专用电机驱动芯片 |
七、通用工程避坑总结
- 优先使用定时器硬件 PWM。软件延时模拟 PWM 极度占用 CPU,一旦系统中存在中断,波形很容易失真,只适合临时验证;
- 区分有效电平,硬件电路加上下拉电阻时,注意 PWM 极性配置;
- 感性负载(电机)需要增加续流二极管,防止反向电动势击穿芯片;
- 占空比不要频繁突变,大功率负载容易产生冲击电流;
- 调试阶段优先使用示波器观测 PWM 波形,确认频率、占空比和预期一致,再接入负载。
八、STM32 HAL 库 呼吸灯完整示例代码
平台说明:STM32F103C8T6,TIM1_CH1 (PA8) 输出 PWM,开发板 LED 外接 PA8; 配置:定时器 72MHz 时钟,预分频 PSC=71,ARR=999,PWM 频率 1kHz; 编译环境:STM32CubeMX 生成工程,HAL 库。
运行
#include "main.h" #include "tim.h" // CCR最大值,对应ARR数值 #define PWM_MAX_CCR 999 // 亮度变化步长 #define STEP 5 // 延时参数,控制呼吸快慢 #define BREATH_DELAY 10 // 全局变量 uint16_t ccr_val = 0; uint8_t dir = 1; // 1递增,0递减 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); // 启动TIM1 CH1 PWM输出 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); while (1) { if(dir == 1) { ccr_val += STEP; if(ccr_val >= PWM_MAX_CCR) { dir = 0; } } else { ccr_val -= STEP; if(ccr_val == 0) { dir = 1; } } // 更新比较值,实时改变占空比 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, ccr_val); HAL_Delay(BREATH_DELAY); } }关键配置补充(CubeMX 参数):
- TIM1 时钟:72MHz
- Prescaler (PSC):71 → 定时器计数时钟 = 72M/(71+1)=1MHz
- Counter Period(ARR):999
- PWM 频率 = 1M/(999+1) = 1000Hz
- PWM 模式:PWM Generation CH1 → PWM 模式 1
代码扩展改造思路
如果想要移植到电机调速:
- 调整 PSC、ARR 参数,把 PWM 频率提升至 10kHz 左右;
- 删除来回增减的 dir 逻辑,外部直接固定 / 修改 ccr_val 即可稳定控制转速;
- 额外增加 GPIO 控制电机正反转。
九、PWM的输出与作用
- 可以直接通过芯片内部模块输出PWM信号,前提是这个I/O口要有集成的pwm控制器,只需要通过对应的寄存器即可,这种自带有PWM输出的功能模块在程序设计更简便,同时数据更精确。
- 但是如果IC内部没有PWM功能模块,或者要求不是很高的话可以利用I/O口设置一些参数来输出PWM信号,因为PWM 信号其实就是一高一低的一系列电平组合在一起。具体方法是给I/O加一个定时器,对于你要求输出的 PWM信号频率与你的定时器一致,用定时器中断来计数,但是这种方法一般不采用,除非对于精度、频率等要求不是很高可以这样实现。
十、全文总结
PWM 的本质就是固定周期、可变脉宽的数字方波,依靠占空比调整得到等效平均电压。呼吸灯和电机调速底层原理完全一致,只是频率选择、负载特性、控制逻辑有明显区别。
实际项目开发不要停留在直接复制代码的层面,搞懂 ARR、CCR、频率、占空比之间的换算关系,遇到灯光、电机、电源类 PWM 需求,就能灵活调整参数解决频闪、噪音、低速无力等各类现场问题。