🎛️ Timer & PWM 模块 —— 给你的开发板装上“节奏大师”!
✅ 适用对象:嵌入式初学者、电机/LED 控制开发者
💡 核心目标:理解 PWM 原理 + 掌握硬件 vs 软件实现 + 熟练使用 CubeMX 配置 + 调用 HAL 库控制 PWM
🧠 特色:用“心跳节拍器”比喻 PWM,用“乐队指挥”解释定时器角色,小白秒懂!
🎵 一、什么是 PWM?
PWM(Pulse Width Modulation,脉宽调制)是一种通过调节高电平“占空时间”来控制平均能量输出的技术。
🌟生活比喻:
- LED 亮度:快速开关 LED(比如每秒 1000 次),人眼看到的是“平均亮度”——开得久就亮,开得短就暗。
- 电机转速:给电机“间歇供电”,占空比越大,转得越快。
- 音响音量:用 PWM 模拟音频信号(需滤波后播放)。
🔑核心参数:
- 频率(Frequency):每秒开关多少次(Hz)
- 占空比(Duty Cycle):高电平时间 / 总周期(如 50% = 一半时间开)
⚙️ 二、两种实现方式:硬件 PWM vs 软件 PWM
| 对比项 | 硬件 PWM | 软件 PWM |
|---|---|---|
| 实现方式 | 用 STM32 定时器自动输出 | 用代码控制 GPIO 开关 |
| 频率控制 | 精确(由寄存器决定) | 依赖延时/中断,易抖动 |
| 占空比控制 | 改 CCR 寄存器(一键调整) | 手动计算高低电平切换时刻 |
| CPU 占用 | ⭐ 极低(硬件自动干) | ⚠️ 高(CPU 得盯着) |
| 资源占用 | 占用定时器 + 特定引脚 | 不占定时器,任意 GPIO 可用 |
| 精度 & 稳定性 | ⭐⭐⭐⭐⭐(无抖动) | ⭐⭐(受任务调度影响) |
| 灵活性 | 受限于定时器通道 | ⭐⭐⭐⭐⭐(哪都能用) |
| 适用场景 | 电机、高精度 DAC、音频 | 简单 LED 调光、玩具级项目 |
✅ 硬件 PWM —— “专业鼓手”
📌 实现步骤
- 选择定时器:如 TIM2、TIM3(通用定时器)
- 配置时基:
- Prescaler(预分频器)+ARR(自动重载值)→ 决定 PWM 频率
- 设置占空比:
- 修改CCR(捕获/比较寄存器)→ 控制高电平时间
- 启用输出:
- GPIO 设为Alternate Function(复用功能)
- 使能 PWM 通道
✅ 优点
- 高精度、零 CPU 开销、超稳定
- 支持死区控制(高级定时器)、同步等高级功能
❌ 缺点
- 引脚固定(如 TIM2_CH1 → PA0)
- 定时器数量有限(资源紧张时需规划)
✅ 软件 PWM —— “兼职打拍子”
📌 实现方式(不推荐用于高精度场景!)
// 主循环模拟(仅作演示) while (1) { HAL_GPIO_WritePin(LED_GPIO, LED_PIN, GPIO_PIN_SET); // 开 delay_us(duty * period_us); // 高电平时间 HAL_GPIO_WritePin(LED_GPIO, LED_PIN, GPIO_PIN_RESET); // 关 delay_us((1 - duty) * period_us); // 低电平时间 }或使用定时器中断动态切换状态。
✅ 优点
- 任意 GPIO 都能输出 PWM
- 无需占用硬件定时器
❌ 缺点
- CPU 忙着“打拍子”,没法干别的
- 多任务下极易失真、抖动大
💡建议:除非引脚不够或只是临时调试,优先用硬件 PWM!
🕰️ 三、STM32 定时器全家桶详解
STM32 的定时器不是“一个”,而是一个功能丰富的工具箱!
| 类型 | 代表型号 | 功能特点 | 典型应用 |
|---|---|---|---|
| 基本定时器 | TIM6, TIM7 | 仅定时 + 中断 | 软件延时、心跳包 |
| 通用定时器 | TIM2~TIM5 | ✅ PWM ✅ 输入捕获 ✅ 输出比较 ✅ 编码器接口 | 电机控制、测频、LED 调光 |
| 高级定时器 | TIM1, TIM8 | ✅ 三相 PWM ✅ 死区时间 ✅ 刹车功能 | 无刷电机、逆变器 |
| 看门狗定时器 | IWDG, WWDG | 程序跑飞自动复位 | 系统安全守护 |
| 低功耗定时器 | LPTIM1~2 | Stop 模式下仍工作 | 电池设备唤醒 |
🎯重点推荐:
- LED/舵机→ 通用定时器(TIM3)
- 直流/步进电机→ 通用定时器
- 无刷电机(BLDC)→ 高级定时器(TIM1)
🔧 四、CubeMX 配置详解(13 项关键参数)
1.Prescaler(预分频器)
- 作用:将输入时钟分频,降低计数频率
- 公式:
- 示例:72MHz ÷ (7199 + 1) =10kHz
2.Counter Period (ARR)
- 作用:设定最大计数值,决定 PWM 周期
- 公式:
- 示例:ARR=999 → 周期 = 1000 / 10kHz =100μs(10kHz)
3.Counter Mode(计数模式)
| 模式 | 特点 | 应用 |
|---|---|---|
| Up(向上) | 0 → ARR,上升沿固定 | 通用 PWM |
| Down(向下) | ARR → 0,下降沿固定 | 特殊控制 |
| Center-aligned(中心对齐) | 0→ARR→0,对称波形 | 电机控制(减少噪声) |
4.Output Compare Mode(OC 模式)
- PWM Mode 1:CNT < CCR → 高电平(常用)
- PWM Mode 2:CNT < CCR → 低电平
5.Pulse(即 CCR 值)
- 作用:设定高电平持续时间
- 占空比公式:
- 示例:ARR=999,Pulse=500 →50% 占空比
6.Polarity(极性)
- High:默认高电平有效
- Low:反相输出(用于 NPN 驱动等)
7.Clock Division(时钟分频)
- DIV1 / DIV2 / DIV4 → 进一步降低时钟(用于输入捕获抗噪)
8.Repetition Counter(RCR)
- 设置每 N 个周期才触发一次更新事件(高级定时器特有)
9.Auto Reload Preload(ARR 预加载)
- Enable:更新在周期结束时生效 →避免波形突变(推荐开启!)
10–13.高级功能(高级定时器专属)
- Dead Time(死区时间):防止 H 桥上下管同时导通
- Break(刹车):故障时立即关闭 PWM
- Complementary Output(互补输出):生成一对反相 PWM
- Output Idle State:停机时引脚电平(安全设计)
📐 五、PWM 频率 & 占空比设置步骤(实战示例)
目标:生成1kHz、50% 占空比的 PWM 信号(系统时钟 72MHz)
步骤 1:确定时钟源
- APB1 时钟 = 72MHz → 定时器时钟 = 72MHz
步骤 2:计算 Prescaler 和 ARR
- 目标频率 = 1kHz → 周期 = 1ms
- 选择 Prescaler = 7199 → 计数频率 = 72MHz / 7200 =10kHz
- ARR = (10kHz / 1kHz) - 1 =999
步骤 3:计算 CCR(占空比 50%)
- CCR = (ARR + 1) × 50% = 1000 × 0.5 =500
步骤 4:CubeMX 配置
- Timer:TIM2
- Channel:Channel 1 (PA0)
- Prescaler:7199
- Counter Period:999
- Pulse:500
💻 六、底层代码与 HAL 库 API
1. 启动/停止 PWM
// 启动 PWM(Channel 1) HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 停止 PWM HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);2. 动态调整占空比(推荐方式)
// 方法 1:直接写 CCR 寄存器(高效) __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, new_ccr_value); // 方法 2:封装函数(更直观) void pwm_set_duty(TIM_HandleTypeDef *htim, uint32_t channel, float duty_percent) { uint32_t arr = htim->Instance->ARR; uint32_t ccr = (uint32_t)((arr + 1) * (duty_percent / 100.0f)); __HAL_TIM_SET_COMPARE(htim, channel, ccr); } // 使用示例 pwm_set_duty(&htim2, TIM_CHANNEL_1, 75.0f); // 75% 占空比⚠️注意:不要直接修改
htim->Instance->CCR1,应使用__HAL_TIM_SET_COMPARE()宏!
3. 带中断的 PWM(用于周期回调)
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1); // 启动 + 使能更新中断 // 在 stm32xxx_it.c 中处理 void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(&htim2); } // 用户回调(在 main.c 中定义) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { // 每个 PWM 周期执行一次 } }🆚 七、STM32 vs 8051 定时器大对比
| 功能 | STM32 定时器 | 8051 定时器 |
|---|---|---|
| 数量 | 4~17 个(视型号) | 仅 2 个(T0/T1) |
| PWM | ✅ 硬件支持 | ❌ 只能软件模拟 |
| 输入捕获 | ✅ 精确测频/脉宽 | ❌ 无 |
| 编码器接口 | ✅ | ❌ |
| 死区控制 | ✅(高级定时器) | ❌ |
| 看门狗 | ✅ 独立+窗口 | ❌(需外接) |
| 低功耗定时 | ✅ LPTIM | ❌ |
| 配置复杂度 | 较高(但 CubeMX 化简) | 简单(但功能弱) |
💥结论:
8051 是“算盘”,STM32 是“智能手表”——
一个只能计数,一个能精准控节奏、测速度、保安全!
🧠 本章口诀(背下来!)
🎛️PWM 就是调节奏,占空比定能量多!
🥁硬件 PWM 是鼓手,稳准狠还不累CPU!
👐软件 PWM 是兼职,灵活但容易掉链子!
⏱️定时器分五兄弟,各司其职真给力!
🔌引脚复用要记牢,CubeMX 一键配好!
📈ARR 决周期,CCR 定占空,预分频降频率!
🛑死区刹车保安全,高级定时器不简单!
📺 推荐学习资源
- 【十行代码,就能让你理解看门狗!】
⼗⾏代码,就能让你理解看⻔狗!经常⻅但不知道看⻔狗是什么,看了这个视频你就知 道了!
这份笔记完整覆盖了PWM的所有内容,包括:
- 硬件/软件 PWM 的原理、实现、优劣对比
- STM32 五类定时器的功能详解
- CubeMX 13 项配置参数详解
- PWM 频率/占空比计算公式与设置步骤
- HAL 库 API 使用(Start/Stop/SetCompare)
- 与 8051 定时器的全面对比
- 生动比喻 + 实用建议 + 避坑指南
现在,你的开发板不仅能“说话”、有“慧眼”、会“表情”、带“生物钟”,还拥有了精准的节奏控制能力,无论是点亮呼吸灯、驱动小马达,还是玩转无刷电机,都游刃有余!✨