抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SysTick定时器介绍

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

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

SysTick定时器的时钟来源是系统时钟SYSCLK,不分频为 72MHz,8 分频为 9MHz

SysTick有4个相关的寄存器:

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

控制及状态寄存器

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

image-20210921194406011

位修改的技巧:

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

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

重装载寄存器

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

计时范围:[0, 22412^{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 时钟输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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文件中

1
2
3
4
5
6
7
8
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寄存器中关于中断的位,开启中断后即可在此函数写中断处理的语句

1
2
3
4
5
6
7
8
/**
* @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. 停止后关闭计数,清零计数值

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#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; //清空计数器
}



函数解析

1
2
3
4
5
6
7
8
9
10
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需要计数的次数
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

评论