目录
[1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景)](#1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景))
[2.方法二:硬件PWM + 更新中断(推荐,精度高)](#2.方法二:硬件PWM + 更新中断(推荐,精度高))
在中断中实现 PWM 波形切换,核心思路是利用定时器的中断功能,在精确的时间点通过软件来控制 GPIO 引脚的电平高低,从而"手动"合成出 PWM 波形。
主要有两种实现方式,它们的复杂度和精度有所不同。
1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景)
这种方法完全依靠中断和软件来翻转 IO 口。你需要配置一个定时器,让它以一个固定的、较短的时间间隔(我们称之为"节拍")产生中断。
核心逻辑:
- 定义参数: 设定 PWM 的总周期(
total_time)和高电平持续时间(high_time),单位都是"节拍"数。- 设置中断: 配置定时器,使其每隔一个"节拍"就触发一次中断。
- 在中断中判断: 在中断服务函数(ISR)里,使用一个计数器(
pwm_counter)来记录当前处于周期的哪个位置。
- 如果
pwm_counter小于high_time,则将 GPIO 设置为高电平。- 否则,将 GPIO 设置为低电平。
- 周期重置: 每次中断后
pwm_counter加 1。当pwm_counter达到total_time时,将其重置为 0,开始下一个 PWM 周期。
代码示例 (以 51 单片机风格为例):
sbit PWM_PIN = P1^0; // 定义PWM输出引脚
unsigned int pwm_counter = 0;
unsigned int high_time = 500; // 高电平持续500个节拍
unsigned int total_time = 1000; // 总周期1000个节拍
// 定时器中断服务程序,假设每10us中断一次(一个节拍)
void Timer_ISR(void) interrupt 1
{
// 1. 根据计数器判断输出高电平还是低电平
if(pwm_counter < high_time)
{
PWM_PIN = 1; // 输出高电平
}
else
{
PWM_PIN = 0; // 输出低电平
}
// 2. 计数器递增
pwm_counter++;
// 3. 检查是否完成一个完整周期
if(pwm_counter >= total_time)
{
pwm_counter = 0; // 周期结束,重置计数器
}
}
通过修改 high_time 的值,就可以在不改变频率(total_time 不变)的情况下,动态调整 PWM 的占空比。
2.方法二:硬件PWM + 更新中断(推荐,精度高)
这是更优的方案。它利用 MCU 内置的硬件 PWM 模块来生成稳定的波形,而中断仅用于在特定时刻(如每个周期结束时)更新 PWM 的参数(如占空比或频率)。
核心逻辑:
- 配置硬件PWM: 首先,配置定时器工作在 PWM 模式。设置好自动重装载值(
ARR,决定频率)和初始的捕获/比较值(CCR,决定初始占空比)。启动 PWM 后,硬件会自动生成波形,无需 CPU 干预。- 使能更新中断: 开启定时器的"更新中断"(Update Interrupt)。这个中断会在定时器计数器溢出(即一个 PWM 周期结束)时触发。
- 在中断中更新参数: 在中断服务函数中,你不再操作 GPIO,而是修改定时器的
CCR寄存器(或ARR寄存器来变频)。
- 修改
CCR:可以平滑地改变下一个周期的占空比。- 修改
ARR:可以改变 PWM 的频率。
代码示例 (以 STM32 HAL库为例):
TIM_HandleTypeDef htim3; // 假设使用 TIM3
// 1. 初始化并启动硬件PWM (在主函数中调用)
void PWM_Init() {
// ... (此处省略定时器和PWM通道的详细初始化代码)
// 假设已配置好:频率1kHz, 初始占空比50% (ARR=999, CCR=500)
// 使能更新中断
__HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);
// 启动PWM
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}
// 2. 更新中断服务函数
void TIM3_IRQHandler(void)
{
// 检查是否是更新中断
if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE) != RESET)
{
// 清除中断标志
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
// --- 在此处实现你的波形切换逻辑 ---
// 例如:动态改变占空比,实现呼吸灯效果
static uint16_t new_duty = 500;
new_duty = (new_duty + 10) % 1000; // 占空比在0-100%之间变化
// 安全地更新CCR值,改变下一个周期的占空比
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, new_duty);
}
}
3.两种方案对比
| 特性 | 软件模拟 PWM | 硬件PWM + 更新中断 |
|---|---|---|
| CPU占用率 | 高,每个节拍都需进入中断 | 极低,仅周期更新时进入中断 |
| 波形精度 | 较低,受中断延迟和抖动影响 | 非常高,由硬件定时器保证 |
| 实现复杂度 | 简单,逻辑直观 | 稍复杂,需理解硬件PWM配置 |
| 适用场景 | 无硬件PWM模块、低频控制 | 电机控制、精密电源等绝大多数应用 |
除非你的 MCU 没有硬件 PWM 功能,或者对频率要求极低(如几赫兹),否则强烈推荐使用方法二。它能以最小的 CPU 开销获得最稳定、最精确的 PWM 波形。