如何在定时器中断中实现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 波形。

相关推荐
进击的小头1 小时前
20_第20篇:嵌入式外设驱动开发基础:寄存器级开发与库函数开发对比实战
arm开发·驱动开发·单片机
guygg883 小时前
基于STM32的智能小区管理系统设计
stm32·单片机·嵌入式硬件
Deitymoon3 小时前
STM32——震动传感器控制led
stm32·单片机·嵌入式硬件
bubiyoushang8884 小时前
51单片机MPU6050 DMP驱动实现
单片机·嵌入式硬件·51单片机
BT-BOX4 小时前
STM32的温湿度防盗安防报警器仿真_LCD1602显示
stm32·安防·烟雾·防盗·lcd1602显示·dht11温湿度·火焰
Deitymoon4 小时前
STM32——继电器
stm32·单片机·嵌入式硬件
hfdz_00424 小时前
无人机无刷电机(BLDC)无感六步换相与过零点检测
嵌入式硬件·无人机·硬件设计
恶魔泡泡糖5 小时前
stm32F103C8T6标准库外部中断的概念
stm32·单片机·嵌入式硬件
VBsemi-专注于MOSFET研发定制5 小时前
高端LED封装自动化产线功率MOSFET选型方案——精密、高效与可靠驱动系统设计指南
运维·单片机·自动化
LCG元7 小时前
STM32项目实战:基于STM32F103的智能台灯控制
stm32·单片机·嵌入式硬件