STM32系统定时器的三种延时方式

SYSTick 简介

系统定时器,24bit,只能递减,存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。一般我们设置系统时钟 SYSCLK 等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环复。因为 SysTick 是属于 CM3内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。SysTick 在STM32的参考手册里面介绍的很简单,其详细介绍,请参阅《Cortex-M3 权威指南》。

Systick 寄存器

SysTick控制及状态寄存器

位段 名称 类型 复位值 描述
16 COUNTFLAG R/W 0 如果在上次读取本寄存器后,SYSTick已经计数到0后则该为为1
2 CLKSOURCE R/W 0 0:外部时钟 1:内部时钟
1 TICKINT R/W 0 1:SYSTick倒数计数到0后产生SYSTick异常请求 0:计数到0无动作
0 ENABLE R/W 0 SYSTick定时器使能位

SysTick重装载数值寄存器

位段 名称 类型 复位值 描述
23:0 RELOAD R/W 0 当倒数计数为0时,此值将会被重装载

SysTick当前数值寄存器

位段 名称 类型 复位值 描述
23:0 CURRENT R/W 0 读取时返回当前倒计数的值,写他则使之清0 ,同时还会清除在systick控制及状态寄存器中的COUNTFLAG标志

Systick 定时时间计算

在这里插入图片描述

接下来就进行三种延时的讲解,普通的减数延时我就不进行赘述了,相信大家都会。而我要将的是单片机编程过程中经常用到延时函数,当然莫过于是毫秒 ms 级 延时和微秒 us 级延时


  • 中断延时法
  • 仿火哥延时,该方法非常适合于初学者
  • 仿原子延时,直接操作寄存器。强烈推荐此方法!!!

首先跟大家讲解下SYSTick库函数的配置,只要理解了其中的原理。那么最后一种操作寄存器的方法就很容易上手。

// 这个 固件库函数 在 core_cm3.h中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  // reload 寄存器为24bit,最大值为2^24
	if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);
  
  // 配置 reload 寄存器的初始值	
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
	
	// 配置中断优先级为 1<<4-1 = 15,优先级为最低
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 
	
	// 配置 counter 计数器的值
  SysTick->VAL   = 0;
	
	// 配置systick 的时钟为 72M
	// 使能中断
	// 使能systick
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0); 
}
SysTick_Config()库函数主要配置的是 SysTick中的三个寄存器: LOAD、 VAL和 CTRL。

1. 中断延时法

/***************************************************************************************
* 函  数: void Interrupt_Delay_us(__IO u32 nTime)
* 功  能: 毫秒级延时
* 参  数: us传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,nTime的最大传入值为 2的24次方(0xFFFFFF*72) / SYSCLK = 16777216 us
*****************************************************************************************/
void Interrupt_Delay_us(__IO u32 nTime)
{ 
	//SYSTICK分频--10us的系统时钟中断
    if (SysTick_Config(SystemCoreClock/100000))
    {
   
        while (1);
    }
	TimingDelay = nTime;	
	while(TimingDelay != 0);
	 
	SysTick->CTRL=0x00; //关闭计数器
    SysTick->VAL =0X00; //清空计数器
}

/***************************************************************************************
* 函  数: void Interrupt_Delay_us(__IO u32 nTime)
* 功  能: 毫秒级延时
* 参  数: ms 传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,nTime的最大传入值为 2的24次方(0xFFFFFF*72*1000) / SYSCLK = 16777 ms
*****************************************************************************************/
void Interrupt_Delay_us(__IO u32 nTime)
{ 
	//SYSTICK分频--1ms的系统时钟中断
    if (SysTick_Config(SystemCoreClock/1000))
    {
   
        while (1);
    }
	TimingDelay = nTime;	
	while(TimingDelay != 0);
	 
	SysTick->CTRL=0x00; //关闭计数器
    SysTick->VAL =0X00; //清空计数器
}

//获取节拍程序
void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}

2. 仿火哥延时

/***************************************************************************************
* 函  数: SysTick_Delay_Us( __IO uint32_t us)
* 功  能: 毫秒级延时
* 参  数: us传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,ms的最大传入值为 2的24次方(0xFFFFFF*72) / SYSCLK = 16777216 us
*****************************************************************************************/
void SysTick_Delay_Us( __IO uint32_t us)
{
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000000);
	
	for(i=0;i<us;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1	
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}

/***************************************************************************************
* 函  数: void SysTick_Delay_Ms( __IO uint32_t ms)
* 功  能: 毫秒级延时
* 参  数: ms 传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,ms的最大传入值为 2的24次方(0xFFFFFF*72*1000) / SYSCLK = 16777 ms
*****************************************************************************************/
void SysTick_Delay_Ms( __IO uint32_t ms)
{
	uint32_t i;	
	SysTick_Config(SystemCoreClock/1000);
	
	for(i=0;i<ms;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
		// 当置1时,读取该位会清0
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}

3. 非中断方式,直接操作寄存器

uint8_t    D_us=0;			//微秒系数
uint16_t   D_ms=0;			//毫秒系数

/***************************************************************************************
* 函  数: void Delay_Init(void)
* 功  能: 延时函数初始化
* 参  数: 无
* 返回值:无
* 备  注: T(s) = 1/F(Hz) //赫兹时间转换公式
*****************************************************************************************/
void Delay_Init(void)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
	
	D_us = SystemCoreClock/1000000;// 1us
	D_ms = (uint16_t)D_us * 1000;
	
}

/***************************************************************************************
* 函  数: void Delay_us(uint32_t nus)
* 功  能: 微秒级延时
* 参  数: nus 传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,nus 的最大传入值为 2的24次方(0xFFFFFF*72) / SYSCLK = 16777216 us
*****************************************************************************************/
void Delay_us(uint32_t nus)
{
	uint32_t temp;
	SysTick->CTRL = 0x00;			//关闭SysTick定时器
	SysTick->LOAD = nus*72; 		//延时重装载值	  		 
	SysTick->VAL  = 0x00;        	//清空计数器
	SysTick->CTRL |= 0x01 ;			//启动SysTick定时器  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	  	//等待延时结束  
	SysTick->CTRL = 0x00;						//关闭SysTick定时器
	SysTick->VAL  = 0X00;      					//清空计数器
}

/***************************************************************************************
* 函  数: void Delay_ms(uint32_t nms)
* 功  能: 毫秒级延时
* 参  数: nms 传入微秒参数
* 返回值:无
* 备  注: LOAD为24位寄存器,nms 的最大传入值为 2的24次方(0xFFFFFF*72*1000) / SYSCLK = 16777 ms
*****************************************************************************************/
void Delay_ms(uint32_t nms)
{
	uint32_t temp;
	SysTick->CTRL = 0x00;					//关闭SysTick定时器
	SysTick->LOAD = nms*72000; 				//延时重装载值	  		 
	SysTick->VAL  = 0x00;        			//清空计数器
	SysTick->CTRL |= 0x01 ;					//启动SysTick定时器  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	 	 				//等待延时结束  
	SysTick->CTRL = 0x00;										//关闭SysTick定时器
	SysTick->VAL  = 0X00;      									//清空计数器	
}

三种方式各有利弊,第一种方式采用采用中断方式延时,由于中断的存在,不利于在其他中断中调用此延时函数。第二种方式非常适合初学者使用,简单偏于理解。第三种方式直接操作寄存器,看起来比较繁琐,其实只要耐心去看几遍也就很容易理解了,个人感觉第三种比较好用。

猜你喜欢

转载自blog.csdn.net/qq_36588941/article/details/88926822