关于嵌入式学习随笔->5《Systick定时器》

1、什么是Systick定时器?

---》Systick定时器是一个简单的滴答定时器,对于ST的M3、M4、M7内核芯片,都有滴答定时器。

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

---》Systick定时器就是一个系统滴答定时器,一个24位的倒计数定时器,记到0时,将从RELOAD寄存器中自动装载定时器初值。只要不把它在Systick控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

---》Systick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。

---》Systick中断的优先级也可以设置。

2、Systick相关寄存器

4个Systick寄存器在HAL库中(core_cm7.h//core_cm4.h)定义位:

  1 typedef struct
  2 {
  3   __IOM uint32_t CTRL;                   /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
  4   __IOM uint32_t LOAD;                   /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register */
  5   __IOM uint32_t VAL;                    /*!< Offset: 0x008 (R/W)  SysTick Current Value Register */
  6   __IM  uint32_t CALIB;                  /*!< Offset: 0x00C (R/ )  SysTick Calibration Register */
  7 } SysTick_Type;

CTRL-------------Systick控制和状态寄存器

image

LOAD-------------Systick自动重装载除值寄存器

image

VAL----------------Systick当前值寄存器

image

CALIB-------------Systick校准值寄存器

image

对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8,内核时钟是HCLK时钟。

3、Systick相关配置函数

配置函数为:

  1 HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource);//Sysick时钟源选择
  1 Systick_Config(uint32_t ticks);//初始化Systick时钟为HCLK并开启中断

Systick中断服务函数为:

  1 void SysTick_Handler(void);

SysTick_Config函数:

  1 __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
  2 {
  3   if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  4   {
  5     return (1UL);                                                   /* Reload value impossible */
  6   }
  7 
  8   SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  9   NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
 10   SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
 11   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
 12                    SysTick_CTRL_TICKINT_Msk   |
 13                    SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
 14   return (0UL);                                                     /* Function successful */
 15 }
SysTick_Config(uint32_t ticks)

HAL_SYSTICK_CLKSourceConfig函数:

  1 void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource)
  2 {
  3   /* Check the parameters */
  4   assert_param(IS_SYSTICK_CLK_SOURCE(CLKSource));
  5   if (CLKSource == SYSTICK_CLKSOURCE_HCLK)
  6   {
  7     SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK;
  8   }
  9   else
 10   {
 11     SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK;
 12   }
 13 }
HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource)

用滴答定时器制作的系统延迟函数(无需支持OS系统):

  1 void delay_init(u8 SYSCLK)
  2 {
  3     HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
  4 	fac_us=SYSCLK;						    //不论是否使用OS,fac_us都需要使用
  5 }
delay_init(u8 SYSCLK)
  1 
  2 void delay_us(u32 nus)
  3 {
  4 	u32 ticks;
  5 	u32 told,tnow,tcnt=0;
  6 	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
  7 	ticks=nus*fac_us; 						//需要的节拍数 
  8 	told=SysTick->VAL;        				//刚进入时的计数器值
  9 	while(1)
 10 	{
 11 		tnow=SysTick->VAL;
 12 		if(tnow!=told)
 13 		{
 14 			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
 15 			else tcnt+=reload-tnow+told;
 16 			told=tnow;
 17 			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
 18 		}
 19 	};
 20 }
delay_us(u32 nus)
  1 void delay_ms(u16 nms)
  2 {
  3 	u32 i;
  4 	for(i=0;i<nms;i++) delay_us(1000);
  5 }
delay_ms(u16 nms)

当使用OS系统时:

  1 //初始化延迟函数
  2 //当使用ucos的时候,此函数会初始化ucos的时钟节拍
  3 //SYSTICK的时钟固定为AHB时钟的1/8
  4 //SYSCLK:系统时钟频率
  5 void delay_init(u8 SYSCLK)
  6 {
  7 #if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
  8 	u32 reload;
  9 #endif
 10     HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
 11 	fac_us=SYSCLK;						    //不论是否使用OS,fac_us都需要使用
 12 #if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
 13 	reload=SYSCLK;					        //每秒钟的计数次数 单位为K	   
 14 	reload*=1000000/delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
 15 											//reload为24位寄存器,最大值:16777216,在216M下,约合77.7ms左右	
 16 	fac_ms=1000/delay_ostickspersec;		//代表OS可以延时的最少单位	   
 17 	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
 18 	SysTick->LOAD=reload; 					//每1/OS_TICKS_PER_SEC秒中断一次	
 19 	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
 20 #endif
 21 }
delay_init(u8 SYSCLK)
  1 //延时nus
  2 //nus:要延时的us数.	
  3 //nus:0~204522252(最大值即2^32/fac_us@fac_us=21)	    								   
  4 void delay_us(u32 nus)
  5 {
  6 	u32 ticks;
  7 	u32 told,tnow,tcnt=0;
  8 	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
  9 	ticks=nus*fac_us; 						//需要的节拍数 
 10 	delay_osschedlock();					//阻止OS调度,防止打断us延时
 11 	told=SysTick->VAL;        				//刚进入时的计数器值
 12 	while(1)
 13 	{
 14 		tnow=SysTick->VAL;
 15 		if(tnow!=told)
 16 		{
 17 			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
 18 			else tcnt+=reload-tnow+told;
 19 			told=tnow;
 20 			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
 21 		}
 22 	};
 23 	delay_osschedunlock();					//恢复OS调度											    
 24 }
delay_us(u32 nus)
  1 //延时nms
  2 //nms:要延时的ms数
  3 //nms:0~65535
  4 void delay_ms(u16 nms)
  5 {
  6 	if(delay_osrunning&&delay_osintnesting==0)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)	    
  7 	{
  8 		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
  9 		{
 10    			delay_ostimedly(nms/fac_ms);	//OS延时
 11 		}
 12 		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
 13 	}
 14 	delay_us((u32)(nms*1000));				//普通方式延时
 15 }
delay_ms(u16 nms)

    根据滴答定时器的递减技术计数特性,就可以很容易的理解编程的思路了。对于Contex-M系统中,Systick代码可以通用,如果使用时发现延时不一致,问题一般都是因为不同内核时钟不一样而已。修改ticks值即可。

猜你喜欢

转载自www.cnblogs.com/vcan123/p/10428760.html
今日推荐