硬件连接:

PB5 是STM32单片机的定时器3的通道2,TIM3 可以用定时器来产生 PWM 输出,因为刚好PB5连接着LED,PWM会控制LED的亮度,从而形成呼吸灯现象。
原理:
灯光的亮度与通过的平均电流成正比。PWM 通过快速切换灯光的 "亮"(高电平,有电流)和 "灭"(低电平,无电流)状态,利用人眼的 "视觉暂留效应",将高频脉冲信号的 "平均能量" 转化为感知到的亮度。
占空比与亮度的关系:
- 占空比 100%:高电平持续整个周期,灯光最亮;
- 占空比 0%:低电平持续整个周期,灯光熄灭;
- 占空比从 0% 逐渐增加到 100%:灯光从暗逐渐变亮;
- 占空比从 100% 逐渐降低到 0%:灯光从亮逐渐变暗。

STM32程序中,使用TIM_SetCompareX 不断改变比较值CCRx,达到不同的占空比效果,从而使灯有不同的亮度。
典型应用场景
- LED 调光:通过调整 CCR2 寄存器的值改变占空比,实现 LED 亮度的平滑调节。
- 电机控制:控制直流电机或步进电机的转速。
- 音频合成:生成特定频率和占空比的 PWM 信号,通过滤波后转换为模拟音频。
程序:
初始化
- 时钟使能:GPIO B的时钟,TIM3的时钟
- 重映射功能:使用定时器 3 的部分重映射功能,将 TIM3_CH2 通道映射到 GPIOB.5 引脚。
- GPIO 配置:将 GPIOB.5 引脚配置为复用推挽输出模式,用于输出 PWM 。
- 定时器配置:设置 TIM3 为 PWM 模式,输入【自动重装载值】和【预分频值】,配置为向上计数模式(从 0 递增到 arr)。
- 配置PWM模式:当计数器值小于 CCR2 时输出低电平,否则输出高电平
cpp
/*
arr:自动重装载值,决定 PWM 周期。
psc:预分频值,用于降低定时器时钟频率。
*/
void TIM3_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
调用时:
cpp
//不分频。PWM频率=72000000/900=80Khz
TIM3_PWM_Init(899,0);
所以ARR最大值是899。
不同亮度的灯
使用 TIM_SetCompare2 来设置定时器 2 通道的捕获 / 比较寄存器(CCR2)的值
我们限制CCR2的值在0~600中,不过于接近最大值899,因为当占空比过高时,LED 可能过亮,继续增加占空比对视觉效果提升有限,也有可能造成损坏。
cpp
while(1)
{
// 不同的亮度
TIM_SetCompare2(TIM3, 0);
delay_ms(100);
TIM_SetCompare2(TIM3, 100);
delay_ms(200);
TIM_SetCompare2(TIM3, 200);
delay_ms(300);
TIM_SetCompare2(TIM3, 300);
delay_ms(500);
}
再次回到上面的图:
可以理解为不同占空比,通过LED的平均电流不同,所以灯的亮度不同

呼吸灯程序
通过逐渐调整 PWM 信号占空比,实现 LED **"从暗到亮,再从亮到暗"**的循环变化,模拟呼吸效果。
- ledPwmValue的变化步长可以加速呼吸的快慢
- 做好范围控制,以免过亮
- direction 作为方向标志:1表示亮度增加,0表示亮度降低
cpp
void PWM_LED_Task(void *pvParameters)
{
u16 ledPwmValue=0; // PWM比较值(占空比控制量),范围0-600
u8 direction =1; // 方向标志:1表示亮度增加,0表示亮度降低
const int MAX_PWM_VALUE = 600;
while(1)
{
printf("PWM_LED_Task \r\n");
delay_ms(10);
if( direction )
{
// ledPwmValue++; // 亮度增加
// 加快
ledPwmValue +=3;
}
else
{
//ledPwmValue--; // 亮度降低
ledPwmValue -=3;
}
// 边界判断:达到最大亮度时切换方向
if( ledPwmValue > MAX_PWM_VALUE )
{
direction = 0;
}
// 边界判断:达到最小亮度时切换方向
if( ledPwmValue < 10 )
{
direction = 1;
}
// 设置PWM占空比
// TIM_SetCompare2 函数主要用于设置定时器 2 通道的捕获 / 比较寄存器(CCR2)的值
TIM_SetCompare2(TIM3, ledPwmValue);
}
}