教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25

先上完整滴答定时器部分代码!!!

EXTI.c部分

#include "ext.h"

#define SYSTICK_E	9

/**
  * @brief  用于SysTick初始化
  * @param  None
  * @retval None
  */
void TICK_Init(void)
{
	//1.设置SysTick的时钟频率
	SysTick->CTRL&=~(1<<2);	//选择当前时钟为AHB/8,在72MHz情况下,SysTick就是9MHz
}

/**
  * @brief  ms级别延迟,最大值在达到1864的情况下,会出现时间不精确
  * @param  u32 count:ms级别延迟时间数
  * @retval None
  */
void sleep_ms(u32 count)
{
	if(count<=0)return;
	
	u8 n=count/1000;
	u16 last=count%1000;
	if(n>=1){
		//循环n次1000ms
		u8 i=0;
		for(i=0;i<n;i++){
			sleep_us(1000*1000);
		}
	}
	
	if(last!=0){
		//在等待最后剩余的时间
		sleep_us(last*1000);
	}
}

/**
  * @brief  us级别延迟
  * @param  u32 count:us级别延迟时间数
  * @retval None
  */
void sleep_us(u32 count)
{
	if(count<=0)return;
	
	//1.先设置LOAD寄存器
	SysTick->LOAD=SYSTICK_E*count;
	//2.清零
	SysTick->VAL=0;
	//3.使能(开启SysTick)
	SysTick->CTRL=0x01;
	while(!(SysTick->CTRL&0x10000));
	//关闭SysTick
	SysTick->CTRL&=~(1<<0);
}

EXTI.h部分

void TICK_Init(void);		//用于SysTick初始化
void sleep_ms(u32 count);	//ms级别延迟
void sleep_us(u32 count);	//us级别延迟

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

滴答定时器初始化部分

//1.设置SysTick的时钟频率
SysTick->CTRL&=~(1<<2);	//选择当前时钟为AHB/8,在72MHz情况下,SysTick就是9MHz

//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第1张图片//由上图可以得知滴答定时器为24位,自动装载值也是24位,并且计数器计数的方式是从当前给定的load装载值一直减少到0。
在这里插入图片描述//有上一篇文章我们得知,HCLK为72MHz,那么systick就是9MHz。
//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第2张图片//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第3张图片//由上图我们可以得知,设置CLKSOURCE为0,则代表当前时钟频率是9MHz。
//所以左移两位,并且取反。

滴答定时器延时us部分

if(count<=0)return;

//这里就是为了防止输入该函数的参数是0,那么如果不加这个操作的话,这个函数毫无意义,最终返回。

//1.先设置LOAD寄存器
SysTick->LOAD=SYSTICK_E*count;

//在上main我们已经得知,该时钟频率为9MHz,那么就相当于为1/9us,也就是每滴答一次就是1/9us,但是我们想要延时1us,是以1us为基准单位的,那么这里就需要我们人为扩大9倍。

//2.清零
SysTick->VAL=0;

//这里就是在赋值的时候先清零,以防止之前寄存器里面有值。

//3.使能(开启SysTick)
SysTick->CTRL=0x01;
while(!(SysTick->CTRL&0x10000));
//关闭SysTick
SysTick->CTRL&=~(1<<0);

//教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第4张图片//当第0位为0时,不使能滴答定时器,反之当第0位为1时,则使能滴答定时器。并且再次清零。
//在这里插入图片描述//同时由上图得知,当装载值减少到0时,则该位返回1。
//那么我们就需要通过判断该位是否为1来得知延时是否完成。
//由于COUNTFLAG为第17位。
//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第5张图片
//最后关闭使能,取反即可。

滴答定时器延时ms部分

if(count<=0)return;

//同样的道理,不再赘述。

u8 n=count/1000;
u16 last=count%1000;
if(n>=1){
	//循环n次1000ms
	u8 i=0;
	for(i=0;i<n;i++){
		sleep_us(1000*1000);
	}
}

//正常来说该装载值寄存器是24位的,那么通过计算器可知为16777216。这时是以秒为单位的那么折算成ms,则需要除以1000。然后再除以9,得到如下所示。

教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第6张图片//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第7张图片//相当于最终算出来ms函数的输入参数最大是1864,不能超过1864。那么我们如果输入的参数大于1864呢?那我们该怎么办?
//所以我们为了解决这个问题,这时需要知道此时输入的参数里面到底有多少个1000,也就是有多少个1s。
//
教你手写滴答定时器(看完这篇你就会手动写啦,保姆级讲解)---- 2020.3.25_第8张图片//如上图所示,我们通过除运算,得知有多少个1000ms,通过%运算,得知还剩多少ms。
//如果n>=1代表肯定比1000多,那么就需要先循环延时n次1000ms。

if(last!=0){
	//在等待最后剩余的时间
	sleep_us(last*1000);
}

//如果最终执行完n次1000ms之后还有剩余一部分时间(ms)(小于1000ms),那么就在执行last ms即可。这就完美解决问题了!!!滴答定时器如此简单!!!

结束语

如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!

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

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

你可能感兴趣的:(嵌入式(stm32))