STM32:SysTick定时器

Posted caesura-k

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32:SysTick定时器相关的知识,希望对你有一定的参考价值。

1 系统时钟树

  单片机首先要在正确的时钟频率下才能正常工作,几乎外设的使用都会用到时钟,先了解一下原理也是不错的;

  时钟树对系统的时钟结构进行了描述,时钟来源主要有4个,分别是HSI, HSE, LSI, LSE;

  1.1 HSI 高速内部时钟信号: 由内部的RC振荡器生成,8MHz,

  1.2 HSE 高速外部时钟信号:由外部的时钟源接入,4-16MHz;

  1.3 LSI 低速内部时钟信号:  由内部的RC振荡器生成,40KHz;

  1.4 LSE 低速外部时钟信号:由外部32.768KHz的时钟源接入;

  1.5 PLL 锁相环时钟信号:由HSI和HSE倍频而来;

    技术图片

2 SysTick定时器

  定义:systick定时器为24位倒数计数器,产生的中断由由NVIC控制;时钟频率为AHB或AHB/8;

     当处理器处于低功耗模式下的时候,可能会终止SYSCLK时钟源,于是systick也随之终止;

  作用:可以为多任务系统的任务设置一个执行周期,如果在时钟周期内任务未完成,就产生systick中断,保证了系统的稳定性;

     可以为操作系统提供“心跳”,提供周期性的定时;

  2.1 SysTick的4个寄存器

    (插入表格)

  2.2 SysTick的标准库封装

    2.2.1寄存器全部封装于core_cm3.h中,并且需要#include"stm32f10x.h";

/*以下内容位于标准库的core_cm3.h中;
*首先是将systick的4个寄存器封装成了SysTick_Type结构体;
*然后用宏定义一个指向该结构体类型的指针SysTick_BASE,指针本身表示地址,*指针表示取出地址的数据;这么一想,指针搭配寄存器很好用啊;
*然后用宏定义寄存器的位,这样就可以通过指针实现寄存器的位操作了,&&为布尔运算符,&为逻辑运算符;
*/

typedef struct
{
  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register STK_CTRL寄存器*/
  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       STK_LOAD寄存器*/
  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      STK_VAL寄存器*/
  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        STK_CALIB寄存器*/
} SysTick_Type;

/* Memory mapping of Cortex-M3 Hardware */
#define SCS_BASE            (0xE000E000)                              /*!< System Control Space Base Address */
#define SysTick_BASE        (SCS_BASE +  0x0010)                      /*!< SysTick Base Address              */
#define SysTick             ((SysTick_Type *)       SysTick_BASE)     /*!< SysTick configuration struct      */

/* STK_CTRL寄存器 位定义 */
#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 */

/* STK_LOAD寄存器 位定义 */
#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */

/* STK_VAL寄存器 位定义 */
#define SysTick_VAL_CURRENT_Pos             0                                             /*!< SysTick VAL: CURRENT Position */
#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFul << SysTick_VAL_CURRENT_Pos)        /*!< SysTick VAL: CURRENT Mask */

/* STK_CALIB寄存器 位定义 */
#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 */

     2.2.2 SysTick的配置使能函数  SysTick_Config(uint32_t ticks)

/*以下一行的声明在stm32f10x.h中第161行;*/
#define __Vendor_SysTickConfig    0 /*!< Set to 1 if different SysTick Config is used */

/*以下函数位于core_cm3.h中第1680行开始;
*初始化加载值寄存器,当前值寄存器;然后打开CTRL控制寄存器,配置使能systick;
*/
#if (!defined (__Vendor_SysTickConfig)) || (__Vendor_SysTickConfig == 0)
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 */
}
#endif

/*当SysTick的中断标志位置1后,会进入中断函数void SysTick_Handler(void);位于stm32f10x_it.c内第135行开始;
*这些中断以及中断函数,都是在.s启动文件内映射的;
*/ void SysTick_Handler(void) { }

    2.2.3 SysTick的时钟源配置函数  SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)

/*以下函数位于misc.c中第199行开始;用来设置systick的时钟源是AHB还是AHB/8;
*输入实参为SysTick_CLKSource_HCLK或SysTick_CLKSource_HCLK_Div8;
*SysTick是在core_cm3.h中定义的结构体指针;其他参数又在misc.h中重新定义了;然后*/
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
  /* 检查参数是否有效的,因为输入实参只有两个值;其他值的话会影响寄存器的其他位,assert_param()的声明常用来检查输入值是否有效; */
  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;
  }
}

  2.3 systick的延时应用

    运行环境为keil5,前提是按 <STM32:预备知识> 配置好了固件库工程,尤其是头文件路径的配置;

    以下三小节为三种systick定时器的使用方法;

    2.3.1 以下代码提供了一个systick应用的实例,主要是对systick的四个寄存器进行配置;

       标准库中systick一共提供了两个函数供调用,此处用了SysTick_CLKSourceConfig()来配置加载值,函数原型位于misc.c中;

/*delay.c文件源码*/
#include "delay.h"

int delay_ms(int ms)
{
    u32  load_ms = 0;
    u32  load_total = 0;
    u32 temp_systick = 0;

    /*直接用算好的0x00FF_FFFF内的数值加载则可以省略以下4步;
    *0xFFFFFF=16777215;AHB为72M,systick为8分频,则定时约1.86秒;
    *1 设置systick的时钟为AHB/8;
    *2 每毫秒内systick时钟源震动的次数;
    *3 本例的定时范围不超过1.86秒,超过的话返回1表示失败;*/
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    load_ms = SystemCoreClock/(8*1000);                     
    load_total = ms*load_ms;
    if(load_total > 0xFFFFFF) return 1;
    
    SysTick->LOAD = load_total;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | 
                    SysTick_CTRL_TICKINT_Msk |
                    (~SysTick_CTRL_CLKSOURCE_Msk);
    do{
        temp_systick = SysTick->CTRL;
    }while(!(temp_systick&SysTick_CTRL_COUNTFLAG_Msk));
    SysTick->CTRL = ~SysTick_CTRL_ENABLE_Msk;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    return 0;
}
/*delay.h文件源码*/
#ifndef _DELAY_H_
#define _DELAY_H_

#include "stm32f10x.h"     
int delay_ms(int ms);

#endif

    2.3.2 systick也可以直接调用core_cm3.h中提供的SysTick_Config()函数来配置开启systick;注意事项如下;

       (1) 输入实参ticks是用来直接赋值给ATK_LOAD寄存器的;

       (2) 需要编写位于stm32f10x_it.c中的SysTick_Handler(void),来设置中断的处理任务;

       (3) 如果在实时操作系统中用来作为周期性心跳什么的这样应该很方便,只是延个时用不着这么配置把;

    2.3.3 假设我们的实时操作系统中已经开启了systick中断,并且一直在运行,那么我们可以通过不断比较SysTick->VAL的值,来编写一个延时函数;

       这样编写延时函数的优点是不会占用额外的中断,也不会占用systick;

/*systick计数范围为0到val_load;val_orginal为开始计数值,val_current为当前计数值;
*通过对比VAL寄存器中开始值和当前值的差值,来达到计时的效果;
*本例前提为系统时钟72M,AHB/8为时钟源;且计数范围不超过val_load;
*/
int delay_ms(int ms)
{
    u32 val_load = SysTick->LOAD;
    u32 user_count = (SystemCoreClock/(8*1000))*ms;
    u32 val_orginal = 0, val_current = 0;    
    u32 cnt_tempif = 0, cnt_tempelse = 0, cnt_sum = 0 ;
    u32 cnt_flag = 0;

    val_orginal = SysTick->VAL;
    do{
        val_current= SysTick->VAL;
        if(val_orginal >= val_current)
            cnt_tempif = val_orginal-val_current;
        else
            cnt_tempelse = val_load-val_orginal;
        cnt_sum = cnt_tempif + cnt_tempelse;
        cnt_flag++;
        if(cnt_flag > val_load) return 1;
    }while(cnt_sum < user_count);
    return 0;
}

 

以上是关于STM32:SysTick定时器的主要内容,如果未能解决你的问题,请参考以下文章

STM32 学习9 SysTick 定时器

STM32 系统定时器(SysTick)

STM32学习及应用笔记一:SysTick定时器学习及应用

stm32的systick原理与应用

STM32系统定时器SysTick

STM32F103五分钟入门系列SysTick滴答定时器+SysTick中断实现跑马灯