stm32 SysTick滴答定时器

SysTick定时器介绍

在给STM32进行编程的时候,我们在程序中会遇到有的时候需要延时,以前我们采用的是循环多次来达到延时的功能,但这样延时很不准确,而通过STM32的SysTick定时器可以让我们更加精确的进行延时操作。

SysTick定时器是一个24位的倒计数定时器,计到0时,将从 RELOAD 寄存器(下面介绍)中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

SysTick有4个相关的寄存器:

  • 控制和状态寄存器: CTRL
  • 自动重装载初值寄存器: LOAD
  • 当前值寄存器: VAL
  • 校准值寄存器: CALIB(用于校准,不常用)

控制及状态寄存器

根据下表介绍进行相关位的配置即可

image-20210921194406011

位修改的技巧:

例如,NUM表示一个二进制数,要将它的第2位置1,其它位不变,则可以这样写:

NUM |= (1<<2);  //使用位运算

重装载寄存器

注意这是一个24位寄存器,因此装载的最大值为 \(2^{24}-1\)

计时范围:[0, \(2^{24}-1\)]

计数方向:倒计数

image-20210921194514939

当前值寄存器

看表格描述

image-20210921195357589

校准值寄存器(不常用)

image-20210921195551884

SysTick定时器使用

使用SysTick定时器前要导入 misc.c 文件

配置步骤

  1. 选择SysTick定时器的时钟源
  2. 设定重装载初值
  3. 清零定时器当前计数值
  4. 开启SysTick定时器
  5. 判断什么时候停止
  6. 停止后关闭计数,清零计数值

相关库函数

时钟源选择,定义在misc.c文件中

可选择的时钟源:

  • SysTick_CLKSource_HCLK_Div8: AHB 8分频输出
  • SysTick_CLKSource_HCLK : AHB 时钟输出
/**
  * @brief  Configures the SysTick clock source.
  * @param  SysTick_CLKSource: specifies the SysTick clock source.
  *   This parameter can be one of the following values:
  *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
  *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
  * @retval None
  */
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
  /* Check the parameters */
  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;
  }
}

寄存器的定义,在 core_cm3.h文件中

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;

中断服务函数,在 stm32f10x_it.c文件,使用前要配置CTRL寄存器中关于中断的位,开启中断后即可在此函数写中断处理的语句

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
}

例子

用于实现精确延时us和ms,按照前面说的6个步骤:

  1. 选择SysTick定时器的时钟源
  2. 设定重装载初值
  3. 清零定时器当前计数值
  4. 开启SysTick定时器
  5. 判断什么时候停止
  6. 停止后关闭计数,清零计数值

代码太长不想看的,看后面具体函数解析

#include "SysTick.h"

static u8  fac_us=0;		//代表1us延时需要计数的次数		   
static u16 fac_ms=0;		//代表1ms延时需要计数的次数


//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
    //1. 时钟源选择
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  
	fac_us = SYSCLK/8;				//配置延时1us需要计数的次数, 由于时钟源为HCLK/8=9MHz, 所以SYSCLK应该选择72
	fac_ms = (u16)fac_us*1000;		//配置延时1ms需要计数的次数	   
}


/*******************************************************************************
* 函 数 名         : delay_us
* 函数功能		   : us延时,
* 输    入         : nus:要延时的us数
          注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输    出         : 无
*******************************************************************************/		    								

void delay_us(u32 nus)
{
	u32 temp;
    //2. 配置重装载值
	SysTick->LOAD = nus*fac_us; //fac_us表示1us需要计数的次数,则nus需要计数nus*fac_us次	
    //3. 清空计数器
	SysTick->VAL = 0x00;     
    //4. 启动计数(倒计数)
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;  //实际上SysTick_CTRL_ENABLE_Msk值为1	
    
    //5. 判断CTRL寄存器值(赋给temp)的第16位是否为1,如果是1,则计数完成
	do
	{
		temp = SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));//循环条件:第一个表示已经启动定时器,后一个表示还没计数完成
    
    //6. 关闭计数器,清空计数值
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭计数器,将最低位置0
	SysTick->VAL = 0X00;      					 //清空计数器	 
}

/*
 *对系统时钟为72M的情况, 要求参数 nms<=1864ms  因为 (2^24-1)/(72/8*1000)=1864
**/
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载
	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;       					//清空计数器	  	    
} 



函数解析

void SysTick_Init(u8 SYSCLK)
{
  //1. 时钟源选择(HCLK单位为MHz)
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  
  //配置延时1us需要计数的次数, 由于上一行配置SysTick时钟源为HCLK/8
  //而系统时钟频率==HCLK频率,所以SysTick计数(HCLK/8)次==1s,1us需要计数HCLK/(8*1000000)次
  //即SYSCLK/(8*1000000)次,所以按照下面一行的写法,传入的参数SYSCLK不能写72000000(72000000是默认情况下的系统时钟),而是写72
  fac_us = SYSCLK/8;
  fac_ms = (u16)fac_us*1000;    //配置延时1ms需要计数的次数	   
}
void delay_us(u32 nus)
{		
  u32 temp;
  //2. 配置重装载值
  SysTick->LOAD = nus * fac_us; //fac_us表示1us需要计数的次数,则nus需要计数nus*fac_us次	
  //3. 清空计数器
  SysTick->VAL = 0x00;     
  //4. 启动计数(倒计数)
  SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;  //实际上SysTick_CTRL_ENABLE_Msk值为1	
    
  //5. 判断CTRL寄存器值(赋给temp)的第16位是否为1,如果是1,则计数完成
  do
  {
    temp = SysTick->CTRL;
  }while((temp&0x01)&&!(temp&(1<<16))); //循环条件:第一个表示已经启动定时器,后一个表示还没计数完成
    
  //6. 关闭计数器,清空计数值
  SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //将最低位置0,关闭计数器
  SysTick->VAL = 0x00;    //清空计数器	 
}

【虚拟调试注意】
使用keil进行虚拟调试时,要配置晶振频率为8MHZ,因为使用的是最最最初始的时钟源HSE=8MHZ(个人认为的,还不确定是不是真的这样),如图:
image-20210921210735724
调试结果
image-20210921212133077

posted @ 2022-03-31 00:10  aJream  阅读(274)  评论(0编辑  收藏  举报