欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32之SysTick定时器

程序员文章站 2022-06-08 16:41:54
...

1.SysTick简介:

     SysTick是系统滴答定时器,可以说是操作系统的的“心跳”,它被绑在NVIC中,用于产生SysTick异常(异常号:15)。一旦产生SysTick异常,就会产生滴答中断,这个滴答中断对操作系统尤其重要。例如:操作系统可以为多个任务分配不同数目的时间片,确保没有一个任务霸占系统,或者将每个定时器周期的某个时间范围赐于特定的任务等。操作系统提供的各种定时功能都与这个滴答定时器有关,因此需要一个定时器产生周期性的中断,而且最好让用户程序不能随意访问它的寄存器。以维持操作系统“心跳”的节律。

    而STM32内核包含了一个简单的定时器——SysTick,所有CM3芯片都带有这个定时器,该定时器的时钟源可以是内部时钟,也可以是外部时钟,在STM32中的SysTick以HCLK(AHB时钟)或HCLK/8作为运行时钟。

   SysTick定时器能产生中断,CM3为它专门开出一个异常类型,便且在向量表中有它的一席之地。 SysTick定时器除了能服务于操作系统外,还能用于其他目的,比如作为闹铃,用于测量时间。

STM32之SysTick定时器

2.SysTick工作分析

 SysTick是一个24位的定时器,即一次最多可以计数2的24次方个时钟脉冲,这个脉冲计数值被保存到当前计数值寄存器STK_VAL中,只能向下计数,每收到一个时钟脉冲STK_VAL的值就向下减1,直至0。当STK_VAL的值被减为0时,由硬件自动把重载寄存器STK_LOAD中保存的数据加载到STK_VAL,重新向下计数。当STK_VAL的值被计数至0,就可以在中断服务函数中处理定时事件了。

STM32之SysTick定时器

STM32之SysTick定时器

STM32之SysTick定时器

STM32之SysTick定时器

STM32之SysTick定时器

下面写了个跑马灯的程序来体验一下SysTick定时器的作用。看代码:

主函数:main.c

#include "systick.h"
#include "led.h"

int main(void)
{  
	LED_Init();
  SysTick_Init();	  //配置SysTick为1ms中断一次
	while(1)
	{
             LED1_OFF;
             Delay_ms(500);     //500 * 1ms=500ms
	     LED1_ON;
	     LED2_OFF;
	     Delay_ms(500);     //500 * 1ms=500ms
	     LED2_ON;
	     LED3_OFF;
	     Delay_ms(500);     //500 * 1ms=500ms
	     LED3_ON;
  }
	
}

SysTick初始化函数:systick.c

#include "systick.h"
__IO uint32_t TimingDelay;
void TimingDelay_Decrement(void)
{
    if(TimingDelay !=0x00)
     {
       TimingDelay--;
     }
}
void SysTick_Init(void)
{
    if(SysTick_Config(SystemCoreClock / 1000))//
     {
      /*Capture error */
        while(1);
     }
	  /*关闭滴答定时器*/
	SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
void Delay_ms(__IO uint32_t nTime)
{
    TimingDelay=nTime;
	  /* 使能滴答定时器*/
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    while(TimingDelay !=0);
}

*****************************************************************************************************************************************************************************************************

注意:这里最重要的就是SysTick_Config()配置函数了,追踪这个函数,发现它是在core_cm3.h文件中,

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}
(1)这个函数都有英文注释,通过看这个注释就能知道函数的功能,这个函数启动了SysTick,便把它配置为计数至0时引起中断输入的参数ticks为两个中断之间的脉冲数,即相隔ticks个时钟周期会引起一次中断;配置Systick成功时返回0,出错时返回1。

(2)从这个函数可以知道,ticks也是有上限值的,具体多大,可以追这个SysTick_LOAD_RELOAD_Msk宏,#define SysTick_LOAD_RELOAD_Msk(0xFFFFFFul << SysTick_LOAD_RELOAD_Pos),通过这个宏我们知道ticks的最大值不能超过十六进制0xffffff,也就是十进制16777215,一般用到中断,我们都要进行NVIC配置,而这个代码用到了中断,为什么外部没有进行NVIC配置呢?仔细观察,你会发现SysTick_Config()函数中已经调用了NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1)函数,这就是答案。

(3)SysTick_Config()函数中的注释没有告诉我们SysTick的时钟是AHB时钟还是AHB/8,这个很关键,注意看,这个函数向STK_CTRL寄存器写入了SysTick的控制参数,在这里我们就知道配置为了AHB时钟,当然这里还要参考STM32的datasheet,查看STK_CTRL寄存器介绍,一切就明白了。

(4)本例子采用的是AHB时钟,其频率被配置为72MHz.调用函数时,把ticks赋值为ticks=SystemCoreClock/1000=7200,表示7200个时钟周期中断一次;定时j计算公式为:T=ticks x (1/f),T为要定时的总时间,ticks为输入参数,1/f为SysTick使用的时钟源的时钟周期,f为该时钟源的时钟频率,当时钟源确定后就为一个常数。由于时钟源是AHB,AHB又默认时钟为72MHz ,所以我写的程序中1/f=1/72us,最终定时总时间T=7200 x (1/72)us=1ms,还要注意一点,由于SysTick定时器是24位的,故最大定时周期不能超过2的24次方个。

LED初始化函数:led.c

#include "led.h"

void LED_Init()	 //LED初始化函数
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
	
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_Init(GPIOD,&GPIO_InitStructure);
		
}
中断服务函数:stm32f10x_it.c

void SysTick_Handler(void)
{
	TimingDelay_Decrement();
}
中断服务函数中的TimingDelay_Decrement()是我自己定义的,在systick.c文件中定义的。