SysTick时钟学习以及相关程序分析

SysTick时钟学习以及相关程序分析

一、 SysTick时钟简介

​ 最近在网上看到一篇学习STM32的帖子,看到里面学习SysTick部分,写的很不
错。附上链接 http://www.openedv.com/thread-272592-1-1.html
回去翻看正点原子教程和STM32F4xx参考手册,发现两者均未提及SysTick的配置说明,STM32F4xx参考手册只在RCC部分提及一点点关于SysTick的内容
SysTick时钟学习以及相关程序分析_第1张图片
此处提及的寄存器在参考手册中并没有找到,百度一下,找到了两个不错的帖子:
SysTick—系统定时器:https://www.cnblogs.com/firege/p/5805734.html
STM32_SysTick时钟的配置:https://blog.csdn.net/lin_duo/article/details/79592503
其中野火的博客写的非常详细,提及了SysTick的简介和寄存器的详细描述。根据野火的博客,我在参考资料《STM32F3与F4系列Cortex M4内核编程手册》4.5 章 SysTick timer (STK)也找到了SysTick详细介绍。
SysTick时钟学习以及相关程序分析_第2张图片从资料中可以看出,在Cortxe-M4内核中,系统定时器是一个24bit的递减计数器,当计数器从重装值递减至0时,会在下一个时钟沿从STK_LOAD寄存器中重新加载value,然后继续递减计数。

二、SysTick寄存器组成

1、SysTick控制和状态寄存器(STK_CTRL)
位段(bit) 名称 读写类型 复位值 描述
16 计数标志位 RW 0 如果在上次读取了此寄存器后,计数到0则返回1
2 时钟源选择 RW 0 选择时钟源,0:AHB/8 1:AHB
1 SysTick异常请求使能 RW 0 1:使能 0:禁止
0 计数使能 RW 0 1:计数使能 0:计数禁止
2、SysTick重装载数值寄存器(STK_LOAD)
位段(bit) 名称 读写类型 复位值 描述
23:0 重装值 RW 0 计数到0时,重装入寄存器的值
3、SysTick当前计数值寄存器(STK_VAL)
位段(bit) 名称 读写类型 复位值 描述
23:0 重装值 RW 0 读操时返回当前的计数值;写操作时会清零,同时STK_CTRL中的计数标志位也会清零
3、SysTick校准值寄存器(STK_CALIB)
位段(bit) 名称 读写类型 描述
31 NOREF标志位 R 读的时候为0。表示当提供单独的参考时钟时,该时钟频率为HCLK/8
30 SKEW标志 R 指示TENMS值是否准确。读为1
23:0 校准值 R 当SysTick计数器作为外部时钟以HCLK max / 8运行时,表示校准值。当HCLK以最大频率编程时,SysTick周期为1ms。

在core_m4.h文件中可以找到SysTick结构体以及位段定义

/** \brief  Structure type to access the System Timer (SysTick).
 */
typedef struct
{
  __IO uint32_t CTRL;                    /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
  __IO uint32_t LOAD;                    /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register       */
  __IO uint32_t VAL;                     /*!< Offset: 0x008 (R/W)  SysTick Current Value Register      */
  __I  uint32_t CALIB;                   /*!< Offset: 0x00C (R/ )  SysTick Calibration Register        */
} SysTick_Type;

/* SysTick Control / Status Register Definitions */
#define SysTick_CTRL_COUNTFLAG_Pos         16                                             /*!< SysTick CTRL: COUNTFLAG Position */
#define SysTick_CTRL_COUNTFLAG_Msk         (1UL << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: COUNTFLAG Mask */

#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 */

/* SysTick Current Register Definitions */
#define SysTick_VAL_CURRENT_Pos             0                                             /*!< SysTick VAL: CURRENT Position */
#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos)        /*!< SysTick VAL: CURRENT Mask */

/* SysTick Calibration Register Definitions */
#define SysTick_CALIB_NOREF_Pos            31                                             /*!< SysTick CALIB: NOREF Position */
#define SysTick_CALIB_NOREF_Msk            (1UL << SysTick_CALIB_NOREF_Pos)               /*!< SysTick CALIB: NOREF Mask */

#define SysTick_CALIB_SKEW_Pos             30                                             /*!< SysTick CALIB: SKEW Position */
#define SysTick_CALIB_SKEW_Msk             (1UL << SysTick_CALIB_SKEW_Pos)                /*!< SysTick CALIB: SKEW Mask */

#define SysTick_CALIB_TENMS_Pos             0                                             /*!< SysTick CALIB: TENMS Position */
#define SysTick_CALIB_TENMS_Msk            (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos)        /*!< SysTick CALIB: TENMS Mask */

/*@} end of group CMSIS_SysTick */

三、SysTick程序分析

​ 根据《Cortex M4内核编程手册》,当处理器在调试期间被停止(halt)时,则SysTick将不会继续计数;由于SysTick的时钟源来自处理器时钟,因此低功耗模式下SysTick也不会继续计数。
SysTick计数器的初始化顺序(编程要点):
1.程序重载值;
2.清除当前值;
3.设置程序控制和状态寄存器

SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件core_cm4.h中,其配置函数如下:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);
  /* Reload value impossible */
  //判断重装值是否正常
  SysTick->LOAD  = ticks - 1;
  /* set reload register */
  //设置重装寄存器
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
  /* set Priority for Systick Interrupt */
  //设置中断优先级
  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,即AHB时钟,此处定时计算公式为:

1000000usAHBticks 1000 000 u s A H B ∗ t i c k s

例如定时1ms ,则ticks = AHB/1000
接下来分析正点原子延时函数, 最前面提到STM32F4xx参考手册中给出了SysTick的时钟源来源有两个,可以是HCLK,也可以是AHB/8。其中正点原子的delay.c文件中则是SysTick_CLKSource_HCLK_Div8,即AHB/8。

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{

    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
    fac_us = SYSCLK/8;                      //不论是否使用OS,fac_us都需要使用
    fac_ms=(u16)fac_us*1000;                //非OS下,代表每个ms需要的systick时钟数

}   
//延时nus
//nus为要延时的us数.  
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
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;                     //清空计数器 
}

​ 前面表格中列出了SysTick_CTRL中bit16位为计数标志位,如果在上次读取了此寄存器后,计数到0则返回1,此处所用的延时正是通过判断该标志位来实现微秒级延时,即查询的方式。至此分析完毕。

你可能感兴趣的:(STM32)