STM32F407ZET6+CubeMX学习笔记:定时器中断与PWM输出详解
STM32F407ZET6+CubeMX学习笔记:定时器中断与PWM输出详解
本文详细介绍了如何使用STM32F407ZET6微控制器和CubeMX工具进行定时器中断和PWM输出的配置。通过具体的参数设置和代码示例,文章深入浅出地讲解了定时器的基本原理、配置方法以及实际应用,适合有一定嵌入式系统基础的工程师和开发者阅读。
定时器基础
定时器内部计数器根据CLK进行计数。其CLK来源为主频通过分频倍频后的总线时钟再经过预分频器后分配给定时器。根据分配的CLK的频率,则可通过查看计数值进行时间计算。计数器的计数上限称为重装载值。
定时器的三种计数方式如上所示。定时器产生中断的时间点依次为:向上溢出,向下溢出,计数方向转变。
定时器配置
在CubeMX中,Timers下选项即为可配置的时钟。其中:
- TIM1, TIM8为高级定时器,功能最多
- TIM6, TIM7为基本定时器,功能最少
- 其余称为通用定时器
以通用定时器TIM2为例:
- Clock Source选为Internal Clock即可开启定时器
- 设置配置:
- Prescaler:对总线分频器进行分频得到CLK。查询参考手册可知TIM1,8,9,10,11挂载在APB2总线上,TIM2,3,4,5,6,7,12,13,14挂载在APB1总线上。再查看cubemx中配置的时钟树,即将使用的TIM2所挂载的APB1总线分频为84MHz。为了计数方便,分频给TIM2取84,即让TIM2得到1MHz的频率,每 1/1000000 s计数一次。在填入的过程中,需要填入83/84-1(从0开始计数),同时分频系数限制为一个无符号位16位的二进制数以内。
- Counter mode:计数器模式选为up向上计数
- Counter period:重装载值,此处取500ms触发一次中断,则重装载值应该为500000,实际应填入500000-1(从0开始计数),最大值为32位无符号位2进制数
完成定时器配置后进入NVIC选项卡将定时器对应中断启用并调整中断优先级后即可生成工程。
代码实现
在main函数while(1)前开启定时器,语句为:
HAL_TIM_Base_Start_IT(&htim2);
为了实现中断,同理需要在main函数前重写HAL库里面的中断回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
}
定时器产生中断后就会进入该函数,传入发生中断的定时器的句柄,执行内部语句,以反转LED电平为例。
定时器产生PWM输出
PWM脉冲宽度调制,以LED灯为例,通过调节占空比,使得LED快速闪烁,人眼虽然无法分辨出快速闪烁,但是由于LED实际点亮时间变短,所以会感觉灯光变弱,由此可实现LED灯亮度的调节。进一步的,随着时间周期性的不断调节占空比即可实现呼吸灯。
定时器中的通道在作为PWM输出的时候,可以连接一个IO口,该IO口上的输出电平即可根据当前定时器的计数值而发生变化。通过设置IO口的比较值,当计数值小于比较值时输出高电平,大于比较值时输出低电平,即可实现PWM。
PWM配置
首先查看各个定时器通道所能对应的IO口。以使用LED灯PF9为例:
通过查询原理图可知,其与TIM14的通道1对应。开启TIM14,通道1功能选择PWM Genration CH1。不同的TIM可能具有不同的通道数。TIM14同TIM2同样挂载在APB1上,故同理进行定时器的配置。分频取84,设置为1kHz频率即1ms的周期使得闪烁的足够快人眼无法分辨。
最后检查是否为所需要的IO口。
代码部分
同理,需要先开启计数器,由于这里不需要使用中断,使用的是PWM输出,并且需要开启通道1,故使用的是:
HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1);
接下来设置比较值:
__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,0);
第一个参数为对应定时器的句柄,第二个参数为对应通道,第三个参数即为设置的比较值。
通过for循环,先逐步提高比较值,再逐步降低比较值,同时注意每次设置完后给1ms的delay,不然比较值改变过于迅速无法看出效果:
for(int cnt = 0;cnt < 1000; cnt++)
{
__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,cnt);
HAL_Delay(1);
}
for(int cnt = 999;cnt >=0; cnt--)
{
__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,cnt);
HAL_Delay(1);
}
滴答定时器
当用cube生成工程后就会开启滴答定时器为HAL库服务,如HAL_Delay就是依赖于其。其产生中断的周期为1ms。具体中断服务函数可在it.c文件找到。内部的HAL_IncTick(); 实际上就是计数值的累加。uwTick是一个32位的存储变量,uwTickFreq为枚举值,具体为1。故uwTick相当于每1ms就+1,即单片机上电以来经过的毫秒数。想要应用这个值以获取时间往往通过__weak uint32_t HAL_GetTick(void);获取其当前值。
HAL_Delay的具体原理就是通过获取调用时候的uwTick作为比较值,直到实际uwTick与起始uwTick之间相差满足传入所需的delay毫秒数才出循环,实现延迟。由此引出HAL_Delay的一个常见使用错误:在某个中断函数里面调用HAL_Delay的同时,该中断本身比滴答定时器的抢断优先级高,滴答定时器就无法发生中断进行计数累加,则HAL_Delay中的HAL_GetTick()获取到的uwTick始终不变,HAL_Delay就无法出循环卡死。
定时器调试
定时器的句柄的类型定义:
TIM_TypeDef *Instance;
存储了所有的寄存器。内部存储的寄存器:PSC预分配系数,CNT当前计数值,ARR重装载值,CCR1-4通道1-4的比较值。调试中通过将这些值加入检测窗口就可以实时进行调试与修改。如通过实时改变CCR1的值即可改变亮度。