TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。
STM32 有16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz基数时钟下可以实现最大的59.65s的定时。不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。
三种定时器高级向下兼容,高级定时器包含通用定时器的所有功能,通用定时器包含基本定时器的所有功能。
以STM32F103C8T6为例,其定时器资源为:TIM1、TIM2、TIM3、TIM4,也就是一个高级定时器和三个通用定时器,没有基本定时器。
定时器电路分析
基本定时器
基本定时器中有三个最重要的寄存器,分别是预分频器、计数器、自动重装寄存器,他们构成了最基本的计数计时器电路,所以这一块电路叫做时基单元,预分频器之前,连接的就是基准计数时钟的输入,最终到了控制器,控制器的输入端直接与内部时钟相连,内部时钟的来源是RRC_TIMxCLK,这里的频率值一般都是系统的主频72MHz。
预分频器如果为0,就是不分频,或者1分频,此时输出频率=输入频率=72MHz;
如果预分频器写1,就是2分频,此时输出频率=输入频率/2=36MHz;
如果预分频器写2,就是3分频,此时输出频率=输入频率/3=24,以此类推
即,实际分频系数=预分频器的值+1
图中预分频器为16位的,所以最大值可以写65535,也就是65536分频
计数器也为16为,每计数一次自增一次,直到65535清零,计数的同时不断与自动重装寄存器进行比较他们的值相等时,会产生一个更新中断和更新事件,CPU相应更新中断,就完成了定时中断的任务了。
通用定时器
通用定时器最核心的还是时基单元,每部分功能和流程也可时基单元是一样的,但高级定时器和通用定时器就不止向上自增这一种计数模式,还支持向下计数模式和中央对齐模式。向下计数模式是从重装值开始向下自减,减到0之后,回到重装值同时申请中断然后继续下一轮,依次循环 。中央对齐模式就是从0开始向上自增到重装值时再向下自减,减到0,再申请中断。不过我们比较常用的还是向上计数的模式
定时器中断
在OLED上显示了一个数字Num,并且每秒自动加1,这就是用了定时中断的功能,定时器使用内部时钟定了一个1秒的时间,每隔1秒申请下中断,然后在中断函数里执行Num++,最后在OLED显示Num。
接线图
示例代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num; //定义在定时器中断里自增的变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时中断初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
while (1)
{
OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量
}
}
/**
* 函 数:TIM2中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
定时器外部时钟
使用外部时钟来驱动定时器,在定时器指定的外部引脚上,输入一个方波信号来提供定时器计数的时钟。(该实验用对射红外传感器模拟外部时钟,用挡光片,依次遮挡、移开、遮挡、移开,提供一个方波对应OLED显示出计数器的值,每遮挡、移开一次,计数器加1,计数器计到9后自动清0,同时申请中断,同时Num++)
使用定时器的外部时钟,可以提供一个更加精准的时钟来计时,或者也可以把外部时钟当作一个计数器,用来统计引脚上的电平翻转的次数
接线图
示例代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num; //定义在定时器中断里自增的变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时中断初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
OLED_ShowString(2, 1, "CNT:"); //2行1列显示字符串CNT:
while (1)
{
OLED_ShowNum(1, 5, Num, 5); //不断刷新显示Num变量
OLED_ShowNum(2, 5, Timer_GetCounter(), 5); //不断刷新显示CNT的值
}
}
/**
* 函 数:TIM2中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}