STM32定时器不止于定时:手把手教你用PWM控制LED呼吸灯(HAL库版)

1. 从定时器到PWM:理解呼吸灯的核心原理

第一次看到LED呼吸灯效果时,那种柔和渐变的明暗变化总让人感觉特别"高级"。其实背后的原理并不复杂,关键就在于PWM(脉冲宽度调制)技术。对于STM32开发者来说,定时器模块正是实现PWM的绝佳工具。

PWM本质上是通过快速开关来控制平均功率。想象一下用STM32的GPIO直接驱动LED:如果以100Hz的频率让LED亮灭各半秒,我们看到的是LED闪烁;但当频率提高到1kHz以上,人眼就会感知为亮度减半。这就是PWM调光的基础——通过改变高电平时间(占空比)来模拟不同亮度。

在STM32的定时器中,有三个关键寄存器协同工作:

  • ARR(Auto-Reload Register):决定PWM周期
  • CCR(Capture/Compare Register):决定脉冲宽度
  • CNT(Counter):实时计数值

当CNT<CCR时输出高电平,CNT≥CCR时输出低电平,CNT达到ARR后归零重新计数。通过动态调整CCR值,我们就能实现占空比的连续变化,这正是呼吸灯效果的精髓所在。

2. CubeMX配置:从零搭建PWM工程

2.1 硬件准备与工程创建

在开始前,请确保准备好:

  • 任意一款STM32开发板(如STM32F103C8T6)
  • 一个LED及适当限流电阻(通常220Ω)
  • STM32CubeIDE开发环境

创建新工程时,选择对应型号后,我们需要重点关注定时器的配置。以常见的TIM3为例:

  1. 在Pinout视图中找到TIM3
  2. 激活某个通道(如Channel1)的PWM模式
  3. 自动分配对应GPIO(如PA6)

2.2 定时器参数详解

在Configuration标签页中,定时器的参数配置尤为关键:

参数 作用 推荐值(呼吸灯)
Prescaler 时钟预分频系数 79
Counter Mode 计数模式 Up
Period (ARR) 自动重装载值 999
Pulse (CCR) 初始占空比 0
CH Polarity 输出极性 High

计算实际PWM频率的公式为:

PWM频率 = 定时器时钟 / (Prescaler + 1) / (ARR + 1)

以72MHz系统时钟为例,上述配置得到的PWM频率为:

72,000,000 / 80 / 1000 = 900Hz

这个频率远超人眼可辨范围,是LED调光的理想选择。

3. 代码实现:让LED"呼吸"起来

3.1 PWM初始化与启动

生成代码后,在main函数中添加PWM启动代码:

/* 启动PWM通道1 */
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

/* 初始占空比设置为0% */
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0);

3.2 呼吸效果算法实现

呼吸灯的核心在于CCR值的动态变化。我们可以在主循环中实现渐变效果:

uint16_t pwmVal = 0;
int8_t dir = 1;  // 1表示增加,-1表示减少

while (1) {
    HAL_Delay(10);  // 控制变化速度
    
    /* 更新CCR值 */
    pwmVal += dir;
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwmVal);
    
    /* 边界检测 */
    if(pwmVal >= 1000) dir = -1;
    else if(pwmVal <= 0) dir = 1;
}

这段代码会产生线性变化的呼吸效果。如果想实现更自然的视觉效果,可以改用正弦函数计算pwmVal:

float radian = 0;
while (1) {
    HAL_Delay(10);
    radian += 0.01;
    if(radian > 6.28) radian = 0;
    
    pwmVal = (sin(radian) + 1) * 500;  // 映射到0-1000范围
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwmVal);
}

4. 进阶技巧与实战优化

4.1 使用中断实现精确控制

直接使用HAL_Delay会阻塞CPU,更好的做法是利用定时器中断:

// 在main中启动基本定时器
HAL_TIM_Base_Start_IT(&htim2);

// 中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if(htim == &htim2) {
        static uint16_t pwmVal = 0;
        static int8_t dir = 1;
        
        pwmVal += dir;
        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwmVal*pwmVal/1000); // 二次方曲线更自然
        
        if(pwmVal >= 1000) dir = -1;
        else if(pwmVal <= 0) dir = 1;
    }
}

4.2 多通道协同控制

通过配置多个PWM通道,可以实现RGB LED的彩色呼吸效果:

// 设置三个通道的CCR值
void setRGB(uint16_t r, uint16_t g, uint16_t b) {
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, r);
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, g);
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, b);
}

// 在循环中调用
setRGB(pwmVal, (pwmVal+333)%1000, (pwmVal+666)%1000);

4.3 常见问题排查

当PWM输出不正常时,建议检查:

  1. GPIO是否配置为复用推挽输出
  2. 定时器时钟是否使能
  3. PWM通道极性设置是否正确
  4. ARR和CCR值是否在合理范围内

调试时可以先用示波器观察PWM波形,确认硬件正常工作后再连接LED。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐