教你手写定时器精准延时(看完这篇你就会手动写啦,保姆级讲解)---- 2020.2.27

关于定时器精准延时原理方面的文章

嵌入式stm32 复习(工作用)—定时器精准延时 原理知识 2020.2.27
添加链接描述

先上完整定时器精准延时部分代码!!!

void TIM2_Init(u16 arr,u16 psc)
{

	RCC->APB1ENR|=1<<0;

	TIM2->CR1|=1<<0;

	TIM2->PSC=psc-1;	

	TIM2->ARR=arr-1;

	TIM2->DIER|=1<<0;
	
	NVIC_SetPriorityGrouping(5);
	//SCB->AIRCR|=0x05<<8;
	
	NVIC_EnableIRQ(TIM2_IRQn);	
	
	NVIC_SetPriority(TIM2_IRQn,10);	
	
	
}
void TIM2_IRQHandler(void)
{

	if(TIM2->SR&0x01){	
		PBout(5)=~PBout(5);
		TIM2->SR&=~(1<<0);
	}

}

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

定时器初始化部分

RCC->APB1ENR|=1<<0;

//
在这里插入图片描述//通过这张stm32系统总线图可知,TIM2是在APB1总线下的。
//
在这里插入图片描述//通过这张图我们应该知道由于TIM2是在APB1总线下的,由图可知APB1的时钟大小是36MHz,且SysTick的最大时钟大小是72MHz,所以APB1的预分频系数是2,又因为APB1的预分频系数不为1,所以TIM2的时钟频率大小是72MHz。
//
在这里插入图片描述//同时注意这句话,所以我们在使能TIM2时应该通过使能APB1 外设时钟使能寄存器(RCC_APB1ENR)这个寄存器来成功开启TIM2。
//
在这里插入图片描述//又因为TIM2使能位是第0位,所以代码中左移0位即可,并且是或操作,具体这里为什么是或运算,我就不再赘述了。大家仔细想想就应该能明白。

TIM2->CR1|=1<<0;

//
在这里插入图片描述//注意这句话:真正的计数器使能信号CNT_EN是在CEN的一个时钟周期后被设置。

//举个例子,一条线段,用多少个点可以分割出来多少条线段。
如果有10个点,请问可以分割出来多少线段?
答案是:11个线段。
如果一根线段所需要花费时间为t,那么11个线段是11t,但是实际情况是10t。
所以我们这句话的作用就是将线段花费的11t中多余的t抹去,最终经过这个操作后变成10t。

//在stm32的芯片手册中,我们可以得知控制寄存器 1(TIMx_CR1)中的第0位,如图所示:
在这里插入图片描述//所以我们如果想要使能TIM2的计数器话,就应该将该位置1。由于该位是第0位,所以左移0位即可。并且是或操作。

TIM2->PSC=psc-1;	

//
在这里插入图片描述//这也是为什么我们在定时器初始化入口处设置无符号型16位的psc的原因了。
//
在这里插入图片描述//这里呢给了大家一张图,从这张图得知“当预分频器的参数从1变到2时”这句话,但是在底下的时序图中预分频控制寄存器却是从0到1,这是为什么呢?不要着急,接下来给大家一张图,大家就知道了。
//
在这里插入图片描述//在这张图当中,计数器的时钟频率内部当中已经给我们自动加1了,所以才会有上张图所说的现象,1变成了0,事实上最终结果是1。
//在上张图中我们可以知道,计数器寄存器计数是从0开始计数的。
//举个例子:
比如我们要进行100次计数,但是计算机是从0开始计数的
0~100,最终进行(100+1)次时钟周期,最终在arr赋值时,arr=100-1=99
所以100-1+1=100次时钟周期。
//这也是为什么在初始化的时候要减1的原因了

TIM2->ARR=arr-1;

//
在这里插入图片描述在这里插入图片描述//当ARPE寄存器=0时,ARR寄存器被写值时,会立即更新到计数器中

TIM2->DIER|=1<<0;

//在前一篇文章当中我们也提到了“在更新事件发生的时候,TIM2的更新中断事件指向MCU的哪一个部分是由NVIC机制来管理的。
//通过stm32的芯片数据手册可以得知
在这里插入图片描述在这里插入图片描述//由上图得知,允许更新中断的话为第0位,且置1则代表允许更新中断,所以代码中是TIM2->DIER左移0位,且置1。

NVIC_SetPriorityGrouping(5);

//在上一篇文章当中我们也提到过NVIC有68个可屏蔽的中断通道,且有16个可编程的优先等级,其中使用了4位中断优先级
//
在这里插入图片描述//由上图可以知道,TIM2的中断通道是第28个
//
在这里插入图片描述//在上图所示,位于第28中断通道的TIM2是通过ISER[0]来控制的。
//
在这里插入图片描述//通过上面分析我们可以知道TIM2是第28通道,同时是通过ISER[0]来控制的,上面这张图就是ISER[0]寄存器,那么我们只需要将ISER[0]第28位置1即可。

//同时中断优先级分为主优先级和子优先级。如果要比较两个中断的优先级的话,是先比较主优先级的大小,然后再比较子优先级的大小。
//同时为了简化代码并且提高效率,我们这里可以使用CMSIS提供的库函数。
在这里插入图片描述
//这里分组的函数就是NVIC_SetPriorityGrouping();
//
在这里插入图片描述//然后查看这个函数上面的说明得知:设置中断的优先级分组是通过SCB->AIRCR的8到10位来控制的。
//
在这里插入图片描述在这里插入图片描述//然后这里是8到10位设置优先级分组的地方,在前一篇文章中我们也知道NVIC有16个可编程的优先等级,且使用了4位中断优先级。
//
在这里插入图片描述//这里就是4位中断优先级分组0b101,通过计算器得知是5。所以代码中的函数入口是5。

SCB->AIRCR|=0x05<<8;

//通过上述分析得知这行代码等价于NVIC_SetPriorityGrouping(5);
//0x05变成二进制为101,又因为设置中断的优先级分组是通过SCB->AIRCR的8到10位来控制的,所以左移8位即可。

NVIC_EnableIRQ(TIM2_IRQn);	

//使能TIM2的全局中断,这个没什么好说的

NVIC_SetPriority(TIM2_IRQn,10);	

//由上述分析我们已经知道,我们已经将TIM2中断优先级分为主优先级两个、子优先级两个。那么我们这里将TIM2的中断优先级设置为10。
//
在这里插入图片描述至此,定时器初始化基本就算完成了

定时器中断函数部分

if(TIM2->SR&0x01){	
		PBout(5)=~PBout(5);
		TIM2->SR&=~(1<<0);
	}

//我们知道定时器触发一个更新事件之后,NVIC才会指向这个中断函数,但是我们需要判断一个条件来知道此时已经成功触发中断,相当于设置一个中断标志位。
在这里插入图片描述//我们由stm32芯片数据手册知道判断无中断事件产生是通过状态寄存器(TIMx_SR)这寄存器来控制的。
//当该0位为0时,无更新事件产生。
//当该0位为1时,有更新事件产生。
//**当产生更新事件时该位由硬件置’1’。它由软件清’0’。**所以我们才会在中断部分完成后,必须在软件将这个相当于中断标志位清零。

结束语

个人认为大家如果细心看完这篇文章,并且结合上一篇文章一起看(在文章的刚开始会将前几篇关于定时器部分的文章链接发出来),我相信大家会彻底掌握定时器精准延时了!!!如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!

以后我会继续推出关于嵌入式(stm32)的协议方面的讲解,下一讲会推出滴答定时器部分的文章!敬请期待!!!

**我先休息去了~~╭(╯^╰)╮

发布了27 篇原创文章 · 获赞 56 · 访问量 4481

猜你喜欢

转载自blog.csdn.net/qq_40544107/article/details/104540888