通过配置STM32F407定时器来实现呼吸灯。
PWM输出就是通过定时器通道对外输出脉宽(即占空比)可调的方波信号,信号频率由定时器自动重装寄存器 ARR 的值决定,占空比由定时器比较寄存器 CCR 的值决定。
PWM模式分为两种,PWM1和 PWM2,具体区别如下表:
模式 |
计数器CNT计算公式 |
说明 |
PWM1 |
递增 |
CNT<CCR,通道CH为有效,否则为无效 |
递减 |
CNT>CCR,通道CH为无效,否则为有效 |
|
PWM2 |
递增 |
CNT<CCR,通道CH为无效,否则为有效 |
递减 |
CNT>CCR,通道CH为有效,否则为无效 |
下面我们以PWM1模式来讲解,以计数器CNT计数的方向不同还分为边沿对齐模式和中心对齐模式。
1、PWM边沿对齐模式
在递增模式下,计数器从0计数到自动重载值(TIMx_ARR寄存器的内容),然后重新从0开始计数并生成计数器上溢事件,PWM1模式的边沿对齐波形如下所示(ARR=8):
在边沿对齐模式下,计数器CNT只工作在一种模式下,递增或者递减模式。这里我们以CNT工作在递增模式为例,在图中,ARR=8,CCR=4,CNT从0开始计数,当CNT<CCR的值时,OCxREF为有效的高电平,于此同时,比较中断寄存器CCxIF置位。当CCR<=CNT<=ARR时,OCxREF为无效的低电平。然后CNT又从0开始计数并生成计数器上溢事件,以此循环往复。
2、PWM中心对齐模式
在中心对齐模式下,计数器 CNT 是工作在递增/递减模式下。开始的时候,计数器 CNT 从 0 开始计数到自动重载值减 1(ARR-1),生成计数器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事件。之后从 0 开始重新计数,PWM1模式的中心对齐波形如下:
在图中,ARR=8,CCR=4,第一阶段计数器CNT工作在递增模式下,从0开始计数,当CNT<CCR的值时,OCxREF为有效的高电平,当CCR<=CNT<<ARR时,OCxREF为无效的低电平。第二阶段计数器CNT工作在递减模式,从ARR的值开始递减,当CNT>CCR时,OCxREF为无效的低电平,当CCR>=CNT>=1时,OCxREF为有效的高电平。
中心对齐模式又分为中心对齐模式 1/2/3 三种,具体由寄存器 CR1位CMS[1:0]配置。具体的区别就是比较中断标志位 CCxIF 在何时置 1:中心对齐模式 1 在 CNT 递减计数的时候置 1,中心对齐模式 2 在 CNT 递增计数时置 1,中心对齐模式 3 在 CNT 递增和递减计数时都置1。
呼吸灯,就是指灯光设备的亮度随着时间由暗到亮逐渐增强,再由亮到暗逐渐衰弱,很有节奏感地一起一伏,就像是在呼吸一样,因而被广泛应用于手机、电脑等电子设备的指示灯中,冰冷的电子设备应用呼吸灯后,顿时增添了几分温暖。
要使用STM32控制LED灯实现呼吸灯效果,可以通过输出脉冲的占空比来实现。如下图:
图中列出了周期相同而占空比分别为 100%、80%、50 和 20%的脉冲波形,假如利用这样的脉冲控制 LED 灯,即可控制 LED 灯亮灭时间长度的比例。若提高脉冲的频率,LED 灯将会高频率进行开关切换,由于视觉暂留效应,人眼看不到 LED 灯的开关导致的闪烁现象,而是感觉到使用不同占空比的脉冲控制 LED 灯时的亮度差别。即单个控制周期内,LED 灯亮的平均时间越长,亮度就越高,反之越暗。
要想使用STM32F4的定时器产生PWM输出,需要使用下面三个寄存器(寄存器详细描述请参考STM32F407参考手册):
- 捕获/比较模式寄存器(TIMx_CCMR1/2)
Bits 6:4:此部分为模式设置位,可以配置成7种模式,我们使用的是PWM模式,所以这3位必须设置位110/111。
Bits 1:0:此部分为通道方向选择位,用于设置通道的方向(输入/输出)默认设置为0,就是设置通道作为输出使用。
- 捕获/比较使能寄存器(TIMx_CCER)
- 捕获比较寄存器(TIMx_CCR1~4)
在输出模式下,该寄存器的值与CNT的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制PWM的输出脉宽了。
呼吸灯程序如下:
TIM_HandleTypeDef TIM_Handle; // 定时器初始化结构体变量
TIM_OC_InitTypeDef TIM_OC_Handle; // 定时器输出初始化结构体变量
// 定时器输出PWM初始化
void TIM_PWM_Init(void)
{
// 定时器初始化
TIM_Handle.Channel = HAL_TIM_ACTIVE_CHANNEL_1; // 通道1
TIM_Handle.Instance = TIM14; // 选择定时器14
TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟1分频
TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
TIM_Handle.Init.Period = 500; // 自动重装载值
TIM_Handle.Init.Prescaler = 168; // 预分频系数
HAL_TIM_PWM_Init(&TIM_Handle); // 初始化定时器
// 定时器输出PWM初始化
TIM_OC_Handle.OCMode = TIM_OCMODE_PWM1; // 模式选择PWM1
TIM_OC_Handle.OCPolarity = TIM_OCPOLARITY_LOW; // 输出比较极性为低
TIM_OC_Handle.Pulse = 250; // 设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
HAL_TIM_PWM_ConfigChannel(&TIM_Handle, &TIM_OC_Handle, TIM_CHANNEL_1); // 配置PWM输出
HAL_TIM_PWM_Start(&TIM_Handle, TIM_CHANNEL_1); // 开始PWM输出
}
// 定时器使用引脚初始化
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Handle; // GPIO初始化结构变量
if(htim->Instance == TIM14)
{
__HAL_RCC_GPIOF_CLK_ENABLE(); // 使能GPIO
__HAL_RCC_TIM14_CLK_ENABLE(); // 使能定时器
GPIO_Handle.Pin = GPIO_PIN_9; // PF9
GPIO_Handle.Mode = GPIO_MODE_AF_PP; // 复用 推挽模式
GPIO_Handle.Pull = GPIO_PULLUP; // 上拉电阻
GPIO_Handle.Speed = GPIO_SPEED_HIGH; // 高速
GPIO_Handle.Alternate = GPIO_AF9_TIM14; // 将GPIO复用为定时器14
HAL_GPIO_Init(GPIOF, &GPIO_Handle); // 初始化GPIO
}
}
// 设置定时器输出占空比
void TIM_SetPluse(uint32_t pluse)
{
TIM14->CCR1 = pluse; // 设置占空比
}
main.c程序如下:
uint16_t pulse; // 占空比
uint8_t dir = 1; // 占空比增长的方向
int main(void)
{
CLOCLK_Init(); // 初始化系统时钟
TIM_PWM_Init(); // 定时器输出PWM初始化
while(1)
{
if(dir) pulse++; // dir==1 占空比递增
else pulse--; // dir==0 占空比递减
if(pulse > 300) dir = 0; // 占空比到达300后,开始递减
if(pulse == 0) dir = 1; // 占空比到达0后,开始递增
TIM_SetPluse(pulse); // 设置定时器占空比
HAL_Delay(10); // 延时10ms
}
}
实验现象:
将程序下载到开发板中,我们可以看到LED1灯由暗到亮,然后又从亮变到暗。
参考文章链接:
https://www.cnblogs.com/firege/p/5805894.html
参考资料:正点原子 STM32F4开发指南-库函数版本_V1.1.pdf