定时器的本质就是一个加1计数器。它随着计数器的输入脉冲进行自加1,当计数器加到各位全为1时,或者加到一个事先预设好的数值时,再输入一个脉冲就会使计数器回零,且计数器的溢出使响应的中断标志位置1,向CPU发出中断请求。
定时器通常用于事先某些需要延迟操作的任务,例如几秒后LED亮。另外还可以用于PWM输出、输入捕获等。本书在此列出几种常见的用法,读者可以自己举一反三。
(1)定时功能。实现精确的定时功能,也可以实现delay函数精确延时。
(2)PWM输出。在定时器中断处理函数中控制GPIO引脚的输出电平,从而实现输出一个PWM波形。适用于电机控制、电流控制等。
(3)输入捕获。对于某些作为输入的引脚,可以使用定时器中断精确地获取波形的变化时间间隔。适用于一些传感器、红外遥控器波形检测等。
STM32拥有14个定时器,分为高级控制定时器(TIM1和TIM8)、32位通用定时器(TIM2和TIM5)、16位通用定时器(TIM3、TIM4、TIM9~TIM14)、基本定时器(TIM6、TIM7)。
这些定时器都有如下几个重要的寄存器。
(1)计数器当前值寄存器(TIMx_CNT),存放计数器的当前值。
(2)递增、递减、递增/递减自动重载计数器(TIMx_ARR),当计数等于自动重载计数器的数值时,再输入一个脉冲就会使计数器回零,并向STM32发出中断请求。
(3)预分频寄存器(TIMx_PSC),对定时器的输入脉冲做预分频。
定时器中断时间的计算公式(4-1):
其中, T out 是定时器中断时间,单位是s。 T clk 是定时器的输入脉冲频率,也叫作定时器时钟源。在STM32F407中,TIM1~TIM14的时钟源如下。
(1)高级定时器TIM1、TIM8及通用定时器TIM9、TIM10、TIM11的时钟来源是APB2总线。
(2)通用定时器TIM2~TIM5,通用定时器TIM12~TIM14及基本定时器TIM6、TIM7的时钟来源是APB1总线。
因为系统初始化SystemInit函数里初始化APB1总线时钟为4分频即42MHz,APB2总线时钟为2分频即84MHz,所以可以得到如下数据:
(1)TIM1、TIM8~TIM11的时钟为APB2时钟的两倍即168MHz。
(2)TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍即84MHz。
有了以上资料,计算定时器的中断时间就比较简单了。例如设置TIM2的TIMx_ARR等于8399,设置TIMx_ARR等于4999,而TIM2的时钟是84MHz。代入公式(4-1)得到:
T out =((4999+1)×(8399+1))/84000000=0.5s。
所以TIM2每隔0.5s中断一次。
打开Chapter4\03_timer\mdk\TIMER.uvprojx工程文件,如图4.30所示。
图4.30 TIMER.uvprojx工程
打开main.c文件,代码如下:
打开timer.c文件,主要是定时器的初始化和定时器中断处理函数部分,代码如下:
TIM2中断处理函数在timer.c文件的43行处,代码如下:
整体代码就是这3部分,实现了LED0每隔0.5s亮灭的功能。读者可以编译并下载到开发板进行测试。
SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号:15)。它可以节省MCU资源,不需要浪费一个定时器,只要不清除SysTick使能位,就不会停止,即使在睡眠模式下也能工作。捆绑在NVIC中断优先级管理,能产生SysTick异常(中断),可设置中断优先级。
通常我们都是用SysTick定时来做精确的延时功能。例如Chapter4\03_timer\Main\main.c文件中的第10行所调用的delay_init(),事实上就是初始化SysTick定时器。打开Chapter4\03_timer\Common\common.c文件,代码如下:
①代码中的us为μs由于代码中无法录入μ故用u代替。
SysTick定时器的中断处理函数是SysTick_Handler(),我们暂时不需要在中断里面处理事情,故而函数内容为空即可。
通常这几个delay函数是通用的模板,读者在需要使用精确delay功能时,可以直接使用本书的模板。
本节讲解了定时器的原理和STM32定时器的计算公式,同时介绍了TIM2和SysTick定时器的用法。希望读者通过本节能自己尝试把其他定时器的操作代码写出来并实现对应的功能。定时器在后面的开发过程中属于最常见的基础知识,希望读者能举一反三。