基于STM32+SysTick系统滴答定时器精准延时(附源码)

前言

       本次我们认识一下基于Cortex M3架构的系统滴答定时器(systick),以及如何使用系统滴答定时器实现精准的 ms 和 μs 延时,大部分是自己收集和整理,如有侵权请联系我删除。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

1.认识系统滴答定时器

delay 延时的编程思想:
        CM3 内核的处理器,内部 包含了一个 SysTick 定时器, SysTick 是一个 24 位的倒计数定时器,当计数到 0 时,将从 RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状 态寄存器中的使能位清除,就永不停息。

SysTick定时器被捆绑在NVIC中,是一个简单的定时器,对于CM3、CM4内核芯片,都有Systick定时器。Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。

Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。

系统滴答定时器有4个寄存器:

CTRLSysTick控制及状态寄存器(地址:0xE000_E010)
LOADSysTick重装载数值寄存器(地址:0xE000_E014)
VALSysTick当前数值寄存器(地址:0xE000_E018)
CALIBSysTick校准数值寄存器(地址:0xE000_E01C)
SysTick控制及状态寄存器(地址:0xE000_E010)
SysTick重装载数值寄存器(地址:0xE000_E014)

 SysTick当前数值寄存器(地址:0xE000_E018)

 SysTick校准数值寄存器(地址:0xE000_E01C)- - - 不常用

2.系统滴答定时器的使用流程

 1)时钟源的选择:

        在使用系统滴答定时器的时候,我们首先需要对时钟源进行选择,通过对SysTick控制及状态寄存器(地址:0xE000_E010)第【2】位来控制选择时钟源。

当寄存器的CLKSOURCE这个位:

为1的时候选择内部时钟源,时钟频率为72M不分频。

为0的时候选择外部时钟源,时钟频率为72M/8=9M。

选择外部时钟和内部时钟的区别?

        外部时钟相对来说精准度更高,稳定性更好,内部时钟使用的时RC振荡器,稳定性较差,相信了解过时钟树方面的知识应该能清楚。

2)系统滴答定时器的计时说明

计数时间的计算,通常我们计时时间为1S = 72M = 72 000 000。

如果我们选择了外部时钟源,经过8分频之后时钟频率为 9M,相当于1 S = 9 M;
所以 1s = 9 000 000  1ms = 9000     1μs = 9;计数9次为1微秒。

因为系统滴答定时器为24位的定时器,正常来说8位的范围在 0 -255之间
所以 24 位的 范围为  256*256*256 = 16 777 215
16 777 215 / 9 = 1864135μs = 1864.135ms = 1.864s
所以这个系统滴答定时器的最大延时为 1.864s


3)系统滴答定时器中断优先级设置

通过系统异常优先级寄存器来设置系统滴答定时器的优先级
在设置优先级之前得确定优先级分组,几位是抢占式优先级,几位是子优先级。

这个时候有人就有疑问了,为什么要开中断,开不开中断的差别在哪里?

1. 其实就是排队和插队的区别

2. 如果不进行中断配置,那么在函数的执行过程中,就是只能排队执行,就是轮到你才能执行。

3. 如果对定时器进行了中断配置,那么优先级高的中断会打断掉延时,但是如果没有其他中断,那么它的优先级是最高的,可以插队执行。

3.程序执行代码过程(附源码)

1)初始化延时函数,配置选择时钟源为外部时钟源,预分频为8

这里的 fac_us=SystemCoreClock/8 000 000;                //为系统时钟的1/8  
表示fac_us = 9;  计数9次为1微秒

#include "delay.h"

static u8  fac_us=0;	//us延时倍乘数			   
static u16 fac_ms=0;	//ms延时倍乘数,在ucos下,代表每个节拍的ms数

			   
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{

	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	fac_us=SystemCoreClock/8000000;				//为系统时钟的1/8  

	fac_ms=(u16)fac_us*1000;					//非OS下,代表每个ms需要的systick时钟数   

}	

2)不进中断的延时函数源码

宏定义的代码:

μs延时函数代码:

//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL  |= SysTick_CTRL_ENABLE_Msk ;	//开始倒数 1<<0
	//SysTick->CTRL|= 0x01 ;	
	
	//do  while 判断就是systick使能(bit0)位为1且(bit16)位为1的时候等待结束
	
	//(temp&0x01)说明已经开始开始使能CTRL寄存器并且开始计数了,这个时候为真:1
	//!(temp&(1<<16))位&是为了判断里面第16位是否为1,为1则temp&(1<<16)为真,
	//   再取反为假,逻辑与&&判断while里面为假:0,则程序结束,表明计数结束
	
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

ms延时函数代码:

//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;							//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;       					//清空计数器	  	    
} 

通过以上对系统滴答定时器的代码配置,我们就可以在日常使用的时候能够精准使用延时。不占用定时器的资源,直接和cpu响应。

3)进中断的延时函数代码(基于基于Cortex M4)- - - 时钟为168M

        可以直接复制使用,不过如果移植为M3架构的芯片,就是需要找到对应的时钟宏定义,把时钟频率改为你当前芯片默认时钟频率就能正常使用了。

#include "delay.h"
#include "stdio.h"

u32 delayTime ;
//延时初始化
void delay_Init(void)
{
	//1us进一次中断
	if(SysTick_Config(SystemCoreClock/1000000) == 1)
		printf("系统滴答初始化失败\r\n");
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}

void SysTick_Handler(void)
{
	if(delayTime > 0 )
		delayTime--;
}
void delay_us(u32 us)
{
	delayTime = us;
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启系统滴答定时器
	while(delayTime);
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}

void delay_ms(u32 ms)
{
	delayTime = ms * 1000;
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启系统滴答定时器
	while(delayTime);
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}

4)代码使用实例


总结 :

        系统滴答定时器的代码并不是很多,知识相对来说也比较少,主要就是看时钟源的选择,和中断的使用,然后就是驱动响应的寄存器位来控制定时器的开启和关闭,等待计数为0就代表计数时间到了。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

  点赞收藏关注双击博主,不定期分享单片机知识,互相学习交流。
   

  • 14
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值