stm32初学之Systick

一、介绍       

        Systick中,Sys指系统,tick是滴答声,所以Systick就是一个系统滴答定时器。它被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号:15)。SysTick的最大使命,就是定期地产生异常请求,作为系统的时基。操作系统都需要这种“滴答”来推动任务和时间的管理。

        Cortex-M3 在内核部分 包含了一个简单的定时器——SysTick  timer。在 STM32 中 SysTick 以  HCLK(AHB 时钟)或 HCLK/8  作为运行时钟。另外,SysTick 定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间等。


二、SysTick  timer工作分析

        Systick的介绍在STM32参考手册中没有找到,费了些时间才在权威指南和固件库手册中找到。Systick有四个寄存器:

CTRL Systick控制和状态寄存器

LOAD Systick重装载值寄存器

VAL Systick当前值寄存器

CALIB Systick标准值寄存器

在权威指南中有图说明:

stm32初学之Systick_第1张图片

图1

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

SysTick只有三个控制位和一个标志位,都位于寄存器 STK_CTRL(SysTick control and status register )中,如下图:

stm32初学之Systick_第2张图片

图2

Bit0:   ENABLE 为 SysTick timer 的使能位,此位为 1 的时候使能 SysTick timer,此位为 0的时候关闭 SysTick timer。

Bit1:TICKINT 为异常触发使能位,此位为 1 的时候并且 STK_VAL 计数至 0 时会触发SysTick 异常,此位被配置为 0 的时候不触发异常。

Bit2:CLKSOURCE 为 SysTick 的时钟选择位,此位为 1 的时候 SysTick 的时钟为 AHB 时钟,此位为 0 的时候 SysTick 时钟为 AHB/8(AHB 的八分频)。

Bit16:COUNTFLAG 为计数为 0 标志位,若 STK_VAL 计数至 0,此标志位会被置 1。


三、Systick编程

首先看了一下固件库函数,发现有六个,但我只找到了SysTick_CLKSourceConfig这个时钟源配置函数,在misc.c中,其他的库函数没有找到(库版本问题?),之后查资料发现大家都不怎么用这些函数,都是使用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 */
}

函数的功能是启动了 SysTick timer;并把它配置为计数至 0 时引起中断;输入的参数 ticks 为两个中断之间的脉冲数,即相隔 ticks 个时钟周期会引起一次中断;配置 SysTick 成功时返回 0,出错进返回 1。

首先,检查输入参数ticks,这里用了一个宏SysTick_LOAD_RELOAD_Msk,在同一文件中可以找到其定义和代码下面所用到宏的定义,代码如下:

#define SysTick_CTRL_CLKSOURCE_Pos          2                                             /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */

#define SysTick_CTRL_TICKINT_Pos            1                                             /*!< SysTick CTRL: TICKINT Position */
#define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */

#define SysTick_CTRL_ENABLE_Pos             0                                             /*!< SysTick CTRL: ENABLE Position */
#define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)               /*!< SysTick CTRL: ENABLE Mask */

/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */
由图1 可知,STK_LOAD 和 STK_VAL 都是 24 位的,ticks 是脉冲计数值,要被保存到重载寄存器 STK_LOAD 寄存器中,再由硬件把 STK_LOAD 值加载到当前计数值寄存器 STK_VAL 使用,所以ticks不能超过其最大存储值。另外这里的SysTick_xxx_Pos就是相应寄存器中的位置,SysTick_xxx_Msk是相应寄存器的位全部置1后,左移SysTick_xxx_Pos 位,利用&、|运算可以方便地修改寄存器的某些位。

接下来将值赋给STK_LOAD寄存器,这里用到了一个指针SysTick ,找到其定义为:

#define SysTick             ((SysTick_Type *)       SysTick_BASE)     /*!< SysTick configuration struct      */
说明SysTick是一个SysTick_Type类型的指针,继续跳转,可以看到SysTick_Type的定义:

typedef struct
{
  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register */
  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       */
  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      */
  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        */
} SysTick_Type;
原来寄存器的定义就包含在这个结构体中。

然后调用了NVIC_SetPriority函数来配置SysTick中断,同样可以在同一文件中找到它的具体代码:

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); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}
这个是用来配置中断的优先级的,第一个参数是中断类型,第二个是优先级,比较复杂。

配置好中断后,将 STK_VAL 寄存器重新赋值为 0,当使能SysTick时,硬件会把存储在 STK_LOAD 寄存器中的 ticks 值加载给它。

在这段代码的最后,向 STK_CTRL 寄存器写入了 SysTick timer 的控制参数,由以上宏分析知是将CTRL寄存器的[2:0]都设为了1。又由图1及图2可知,设为1时,分别是使用内核时钟(FCLK)、计数至 0 时引起中断和使能 SysTick。当然你也可以自己修改成自己需要的参数。

在执行完这段代码后,SysTick 就开始运行,进行脉冲计数了。不过运行完这个函数后,SysTick就开启了,但我们一般是在需要的时候才用它,所以在运行完这个函数后最好先将它关闭。可以使用如下方法:

// 使能滴答定时器 
SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;
// 失能滴答定时器 
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;

例如:

void SysTick_Init()
{
	if (SysTick_Config(SystemCoreClock / 100000))   //SystemCoreClock 为定义了系统时钟(SYSCLK)频率的宏,即等于 AHB 的时钟频率,被设置成72MHz
	{
		while (1);
	}
	
	SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
至于时间的计算,公式为:  T=ticks*(1/f) , T为总时间,1/ f  即为 SysTick timer 使用的时钟源的时钟周期,f 为该时钟源的时钟频率,当时钟源确定后为常数。

通过计算可得出此函数延时为10us。只要在主函数中将SysTick使能开启,就能使用10us的精确延时了。

int main(void)
{
        SystemInit();
	SysTick_Init();
	
	SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;
	
	......
}





你可能感兴趣的:(stm32)