【STM32学习笔记】(8)——SysTick系统定时器详解

SysTick 简介

        SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72MHz。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。 因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器, 使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

        Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。

SysTick 寄存器介绍

        SysTick—系统定时器有 4 个寄存器,在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。

  1. 4个Systick寄存器

            ●CTRL                  SysTick 控制和状态寄存器             

            ●LOAD                 SysTick 自动重装载除值寄存器 
            ●VAL                     SysTick 当前值寄存器            

            ●CALIB                 SysTick 校准值寄存器

 

/** @addtogroup 
    CMSIS_CM3_SysTick CMSIS CM3 系统标记的内存映射结构  

 */
typedef struct
{
  __IO uint32_t CTRL;                      /*!< Offset: 0x00  SysTick 控制和状态寄存器    */
  __IO uint32_t LOAD;                      /*!< Offset: 0x04  SysTick 自动重装载除值寄存器 */
  __IO uint32_t VAL;                       /*!< Offset: 0x08  SysTick 当前值寄存器        */
  __I  uint32_t CALIB;                     /*!< Offset: 0x0C  SysTick 校准值寄存器        */
} SysTick_Type;

 

1、SysTick 控制和状态寄存器- CTRL

 

 

对于STM32可以把外部时钟源可以配置:

        ①外部时钟源是 HCLK(AHB总线时钟)的1/8

        ②内核时钟是 HCLK时钟

     配置SysTick系统定时器时钟的函数:SysTick_CLKSourceConfig();

 

2、SysTick 重装载数值寄存器- LOAD

 

3、SysTick 当前值寄存器- VAL

 

固件库中的Systick相关函数:

SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)    //Systick时钟源选择  misc.c文件中

 

/**
  * @brief  配置系统时钟源。
  * @param  SysTick_CLKSource:指定 SysTick 时钟源。
   			此参数可以是下列值之一:  
* @arg 	SysTick_CLKSource_HCLK_Div8: AHB 时钟除以 8,选择作为系统时钟源。  
* @arg 	SysTick_CLKSource_HCLK: 选择 AHB 时钟作为系统时钟源。
  * @retval 	无
  */
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
  /* 检查参数 */
  assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  {
    SysTick->CTRL |= SysTick_CLKSource_HCLK;
  }
  else
  {
    SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
  }
}

 

  SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断

                                                //core_cm3.h/core_cm4.h文件中

 

/**
 * @brief  初始化并启动 SysTick 计数器及其中断。
 *
 * @param   ticks   两个中断之间的刻度数
 * @return  1 = failed, 0 = successful
 *
 * 初始化系统滴答计时器及其中断,然后启动系统滴答计时器/计数器在自由运行模式下生成周期性中断。
 */
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
//判断tick的值是否大于2……24,如果大于,则不符合规则。
  if (ticks > SysTick_LOAD_RELOAD_Msk)  
        return (1);            /* 无法重新加载值 */
                                                          
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     /* 设置重新加载寄存器 */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* 设置 Cortex-M0 系统中断的优先级 */
  SysTick->VAL   = 0;                                /* 加载系统计数器值 */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;          /* 启用系统计数器 IRQ 和系统图标计时器 */
  return (0);                                                  /* 功能成功 */
}

        用ST官方的固件库编程的时候我们只需要调用库函数 SysTick_Config() 即可,形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 16777216,当重装载寄存器的值递减到 0 的时候产生中断, 然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。

        紧随其后设置好中断优先级, 最后配置系统定时器的时钟等于AHBCLK=72M,使能定时器和定时器中断,这样系统定时器就配置好了。 SysTick_Config() 库函数主要配置了 SysTick 中的三个寄存器:LOAD、VAL 和 CTRL。

SysTick 中断时间的计算

        SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,当重装载寄存器 中的值 VALUELOAD 减到 0 的时候,产生中断,可知中断一次的时间 TINT=VALUELOAD * TDEC= VALUELOAD/CLKAHB,其中 CLKAHB =72MHZ。如果设置 VALUELOAD 为 72,那中断一次的时间 TINT=72/72M=1us。不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有 时间处理其他的任务。

        SysTick_Config(SystemCoreClock / 100000)

        SysTick_Config()的形我们配置为 SystemCoreClock / 100000=72M/100000=720,从刚刚分析我们 知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中 断一次的时间 TINT=720/72M=10us。

SysTick 定时时间的计算

        当设置好中断时间 TINT 后,我们可以设置一个变量 t,用来记录进入中断的次数,那么变量 t 乘 以中断的时间 TINT 就可以计算出需要定时的时间。

配置 SysTick 中断优先级

        在 SysTick_Config() 库函数还调用了固件库函数 NVIC_SetPriority() 来配置系统定时器的中断优先级,该库函数也在 core_m3.h 中定义。

 

/**
 * @brief  设置中断的优先级
 *
 * @param  IRQn      设置优先级的中断次数
 * @param  priority  要设置的优先级设置指定中断的优先级。中断数字可以为正数以指定外部(特定于设备)中断,或负数指定内部(核心)中断。
 * Note: 不能为每个内核中断设置优先级。
 */
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) 
    {
        SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); 
    } /* 设置皮质 M3 系统中断的优先级 */
  else 
    {
        NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    
    } /* 为设备特定中断设置优先级  */
}

 

        函数首先先判断形参 IRQn 的大小,如果是小于 0,则表示这个是系统异常,系统异常的优先级 由内核外设 SCB 的寄存器 SHPRx 控制,如果大于 0 则是外部中断,外部中断的优先级由内核外 设 NVIC 中的 IPx 寄存器控制。 因为 SysTick 属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级和子优先 级的说法。在 STM32F103 中,内核外设的中断优先级由内核 SCB 这个外设的寄存器:SHPRx (x=1.2.3)来配置。

        SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一个内核外设 的中断优先级的配置。在 STM32F103 中,只有位 7:4 这高四位有效,低四位没有用到,所以内核 外设的中断优先级可编程为:0~15,只有 16 个可编程优先级,数值越小,优先级越高。如果软 件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

 

延时函数详解(delay_us()、delay_ms()与delay_init()) 

 

        下面的代码是delay_init()初始化延时函数的代码。

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//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时钟数
}	

        下面的代码是delay_us(u32 nus)延时us函数的代码。 

//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	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;      					 //清空计数器	 
}

        下面的代码是delay_ms(u32 nms)延时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;       					//清空计数器	  	    
}

  • 9
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂飙的犇牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值