上篇文章用通用定时器实现了LED灯的闪烁,这次用通用定时器来产生PWM波,使LED呈现呼吸灯的效果。
一、PWM相关知识
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制。
为了使STM32的TIMx生成PWM输出,除了基本的寄存器设置外,还需要使用三个主要寄存器:捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)和捕获/比较寄存器(TIMx_CCR1~4)。下面是这些寄存器的简要介绍:
-
捕获/比较模式寄存器(TIMx_CCMR1/2):
- TIMx_CCMR1控制CH1和CH2,TIMx_CCMR2控制CH3和CH4。
- 模式设置位OCxM决定PWM模式,设置为110或111。
-
捕获/比较使能寄存器(TIMx_CCER):
- 控制各输入输出通道的开关。我们使用CC2E位来使能PWM输出。
-
捕获/比较寄存器(TIMx_CCR1~4):
- 这些寄存器的值与CNT的值比较,通过修改这些寄存器的值来控制PWM输出的脉宽。本实验使用TIM3的CH2,所以修改TIM3_CCR2实现PWM输出控制。
由于LED0连接在PB5引脚上,而TIM3_CH2默认连接在PA7上,所以要通过重映射功能将TIM3_CH2映射到PB5。STM32的重映射功能由AFIO_MAPR寄存器控制,需要设置TIM3_REMAP[1:0]为10,实现部分重映射。
二、代码相关
我们使用通用定时器TIM3模块来生成PWM信号。首先要对TIM3进行初始化配置,包括设置时钟分频、计数模式、自动重载值和预分频器等参数。然后初始化GPIO引脚,将其配置为复用功能输出,以输出PWM信号控制LED灯。
1.TIM3定时器初始化
通过配置定时器的时钟分频、计数模式、自动重装载值和预分频器等参数,对TIM3进行初始化设置。这些参数的配置决定了PWM信号的频率和占空比。
void TIM3_Int_Init(u16 arr, u16 psc) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; // 设置自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化TIM3
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 使能TIM3更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 先占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 从优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能IRQ通道
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); // 使能TIM3
}
2.GPIO引脚配置
配置GPIO引脚,将PB5引脚设置为复用功能输出,用于输出TIM3的CH2通道的PWM信号。这一步将TIM3的PWM信号与LED的控制引脚连接,实现对LED的控制。
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); // 使能TIM3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // 使能GPIO和AFIO时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); // TIM3部分重映射
// 配置PB5为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // TIM3_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; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化TIM3
// 初始化TIM3 CH2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // PWM模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); // 初始化TIM3 CH2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能预装载寄存器
TIM_Cmd(TIM3, ENABLE); // 使能TIM3
}
3.生成PWM信号
在主循环中,通过逐步增加或减少PWM值,实现LED的呼吸灯效果。通过条件判断改变PWM值的递增或递减方向,使LED的亮度呈现出渐变效果。
while (1) {
delay_ms(10); // 延时10ms
if (isIncreasing) {
led0pwmval++; // 增加PWM值
} else {
led0pwmval--; // 减少PWM值
}
if (led0pwmval > 300) {
isIncreasing = 0; // 达到最大值时,改变方向
}
if (led0pwmval == 0) {
isIncreasing = 1; // 达到最小值时,改变方向
}
TIM_SetCompare2(TIM3, led0pwmval); // 设置TIM3 CH2的比较值,控制PWM占空比
}
看看效果:
呼吸灯