「STM32入门」TIM定时中断

定时器的简介

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断,在中断内可以执行中断事件
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择,主从触发模式,输入捕获,输出捕获,编码器接口等多种功能。

定时器的分类 

595e23c6de63480abcec11b954b6e2c7.png注:不同型号的STM32所拥有的定时器数量是不同的 

基本定时器工作流程

a578343482e74863a27faf55fd32b808.png

         从上往下看,内部时钟一般都是系统的主频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就是自动重装载寄存器的值

通用定时器的结构 

2554fce7ff994be7837e52cb116d4683.png

         与基本定时器最大的区别就在于,通用定时器的时钟源不一定必须是系统内部时钟,也可以是外部时钟。如左上角TIMx_ETR。这有什么用呢?根据引脚定义表我们发现,TIM1_ETR的引脚是PA0,那么我们可以在PA0脚输出一个自定义周期和占空比的方波,这样就可以控制定时器的自增频率了。

定时中断的基本结构 

0f098ef72f204857bdc7d3b8f03f2444.png

        我们发现这个框图里出现了两个之前没有介绍的东西,即最右边的中断控制和NVIC,那么他们是什么?首先,当计数器自增到自动重装载值后,中断信号会在状态寄存器里(图中未标出)置一个中断标志位,这个中断控制位会通过中断输出控制,到NVIC申请中断。那中断输出控制的作用是什么呢?因为这个定时器模块有很多地方要申请中断,那么中断输出控制就是用来控制这些中断执行或者不执行的。

NVIC基本结构 

7076939e65104311a026e580bcf29016.png

        NVIC的主要作用是统一分配中断优先级和管理中断。NVIC根据每个中断的优先级,分配中断的顺序,之后通过右边的输出口,告诉CPU应该处理哪个中断。可以这样理解,所有的中断都是病人,而CPU是医生,NVIC则是叫号系统,当一堆病人来了,NVIC需要根据他们的紧急程度,安排他们有次序的见医生。


NVIC优先级分组

5d69fce59255448dbfc2751e12e2ad40.png

        为了处理不同形式的优先级,NVIC可以对优先级进行分组,分为抢占优先级和相应优先级。那他们有什么区别呢?

        还是以病人看病为例。

        第一种是,上一个病人在看病,外面排队了很多的病人,当上一个病人看完后,紧急的病人即使是最后来的,也会最先进去看病,这种相当于插队的优先级,就叫响应优先级。

        第二种是,当一个病人很紧急,尽管此时有人在看病且没看完,他也可以直接进医生屋里,让上一个病人靠边站,自己先看病。等他看完了,上一个病人继续,上一个病人结束了,叫号系统再看看有没有新的病人到来。这种优先级就叫抢占优先级。

9b4dbf9a7b0a4e8d8318e9a0599b5282.png

        因此,当我们确定分组方式的时候,就要注意抢占优先级和响应优先级的取值范围,不能超过表里的取值范围。

定时器初始化代码示例 

#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);			//清除更新中断标志位
	}
}*/

猜你喜欢

转载自blog.csdn.net/m0_54689021/article/details/130201158