如何在定时器中断中实现PWM波形切换?

目录

[1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景)](#1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景))

[2.方法二:硬件PWM + 更新中断(推荐,精度高)](#2.方法二:硬件PWM + 更新中断(推荐,精度高))

3.两种方案对比


在中断中实现 PWM 波形切换,核心思路是利用定时器的中断功能,在精确的时间点通过软件来控制 GPIO 引脚的电平高低,从而"手动"合成出 PWM 波形。

主要有两种实现方式,它们的复杂度和精度有所不同。

1. 方法一:软件模拟 PWM(适用于无硬件PWM或低频场景)

这种方法完全依靠中断和软件来翻转 IO 口。你需要配置一个定时器,让它以一个固定的、较短的时间间隔(我们称之为"节拍")产生中断。

核心逻辑:

  1. 定义参数: 设定 PWM 的总周期(total_time)和高电平持续时间(high_time),单位都是"节拍"数。
  2. 设置中断: 配置定时器,使其每隔一个"节拍"就触发一次中断。
  3. 在中断中判断: 在中断服务函数(ISR)里,使用一个计数器(pwm_counter)来记录当前处于周期的哪个位置。
    • 如果 pwm_counter 小于 high_time,则将 GPIO 设置为高电平
    • 否则,将 GPIO 设置为低电平
  4. 周期重置: 每次中断后 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 的参数(如占空比或频率)。

核心逻辑:

  1. 配置硬件PWM: 首先,配置定时器工作在 PWM 模式。设置好自动重装载值(ARR,决定频率)和初始的捕获/比较值(CCR,决定初始占空比)。启动 PWM 后,硬件会自动生成波形,无需 CPU 干预
  2. 使能更新中断: 开启定时器的"更新中断"(Update Interrupt)。这个中断会在定时器计数器溢出(即一个 PWM 周期结束)时触发。
  3. 在中断中更新参数: 在中断服务函数中,你不再操作 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 波形。

相关推荐
asjodnobfy2 小时前
生产过程中的电容损坏分析
嵌入式硬件·硬件工程
be to FPGAer2 小时前
设计约束命令和SDC命令
单片机·嵌入式硬件
Flamingˢ3 小时前
ZYNQ+OV5640+VDMA+HDMI视频链路搭建实录:从摄像头采集到实时显示
arm开发·嵌入式硬件·fpga开发·vim·音视频
Topplyz3 小时前
PCB开尔文走线
嵌入式硬件·pcb·layout
C^h3 小时前
RT thread使用u8g2点亮oled显示屏
linux·单片机·嵌入式硬件·嵌入式
senijusene3 小时前
IMX6ULL ADC 驱动开发解析:
驱动开发·嵌入式硬件
UTP协同自动化测试3 小时前
智能家居中控屏测试:触摸屏操作 + I2C 读取传感器 + UART 与子设备通信 + GPIO 控制
功能测试·单片机·嵌入式硬件·测试工具·智能家居
【云轩】4 小时前
【拆解系列 一 】拆解手持式泡泡机
嵌入式硬件
Hello World . .4 小时前
ARM裸机学习9——ADC模块详解与应用实践
arm开发·嵌入式硬件