定时器的简介
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断,在中断内可以执行中断事件
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择,主从触发模式,输入捕获,输出捕获,编码器接口等多种功能。
定时器的分类
注:不同型号的STM32所拥有的定时器数量是不同的
基本定时器工作流程
从上往下看,内部时钟一般都是系统的主频72MHz。PSC指的是预分频系数,比如我希望计数的频率就是72MHz,那么可以给PSC=0,意为一分频或者叫不分频。当希望以36MHz计数时,那么就给PSC=1,二分频,以此类推。
CNT就是计数器,根据之前的分频系数,每个一段周期,CNT计数器接收到一段高电平脉冲,此时CNT+1,自动重装载寄存器就是目标值,比如该值为80,那么当CNT计数到80后,就会出发中断(UI更新中断/U更新事件),之后CNT值需要手动清0。
计数器的计数频率:CK_CNT = CK_PSC / (PSC + 1)
计数器的溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
ARR就是自动重装载寄存器的值
通用定时器的结构
与基本定时器最大的区别就在于,通用定时器的时钟源不一定必须是系统内部时钟,也可以是外部时钟。如左上角TIMx_ETR。这有什么用呢?根据引脚定义表我们发现,TIM1_ETR的引脚是PA0,那么我们可以在PA0脚输出一个自定义周期和占空比的方波,这样就可以控制定时器的自增频率了。
定时中断的基本结构
我们发现这个框图里出现了两个之前没有介绍的东西,即最右边的中断控制和NVIC,那么他们是什么?首先,当计数器自增到自动重装载值后,中断信号会在状态寄存器里(图中未标出)置一个中断标志位,这个中断控制位会通过中断输出控制,到NVIC申请中断。那中断输出控制的作用是什么呢?因为这个定时器模块有很多地方要申请中断,那么中断输出控制就是用来控制这些中断执行或者不执行的。
NVIC基本结构
NVIC的主要作用是统一分配中断优先级和管理中断。NVIC根据每个中断的优先级,分配中断的顺序,之后通过右边的输出口,告诉CPU应该处理哪个中断。可以这样理解,所有的中断都是病人,而CPU是医生,NVIC则是叫号系统,当一堆病人来了,NVIC需要根据他们的紧急程度,安排他们有次序的见医生。
NVIC优先级分组
为了处理不同形式的优先级,NVIC可以对优先级进行分组,分为抢占优先级和相应优先级。那他们有什么区别呢?
还是以病人看病为例。
第一种是,上一个病人在看病,外面排队了很多的病人,当上一个病人看完后,紧急的病人即使是最后来的,也会最先进去看病,这种相当于插队的优先级,就叫响应优先级。
第二种是,当一个病人很紧急,尽管此时有人在看病且没看完,他也可以直接进医生屋里,让上一个病人靠边站,自己先看病。等他看完了,上一个病人继续,上一个病人结束了,叫号系统再看看有没有新的病人到来。这种优先级就叫抢占优先级。
因此,当我们确定分组方式的时候,就要注意抢占优先级和响应优先级的取值范围,不能超过表里的取值范围。
定时器初始化代码示例
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //启用TIM3时钟
TIM_InternalClockConfig(TIM3); //设置TIM3使用内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体,配置定时器
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置1分频(不分频)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //设置最大计数值,达到最大值触发更新事件,因为从0开始计数,所以计数10次是10-1,每10微秒触发一次
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //设置时钟预分频,72-1就是每 时钟频率(72Mhz)/72=1000000 个时钟周期计数器加1,每1微秒+1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器(高级定时器才有,所以设置0)
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //初始化TIM3定时器
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除更新中断标志位
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //开启更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体,配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //指定中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE); //开启定时器
}
/*
void TIM3_IRQHandler(void) //更新中断函数
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除更新中断标志位
}
}*/