嵌入式开发十九:SysTick—系统定时器

在前面实验中我们使用到的延时都是通过SysTick进行延时的。 我们知道,延时有两种方式:软件延时,即CPU 循环等待产生的,这个延时是不精确的。第二种就是滴答定时器延时,本篇博客就来介绍 STM32F4 内部 SysTick 系统定时器,通过一个简单的 LED 流水灯程序来讲述如何配置 SysTick 系统定时器实现精确延时。学习可以参考《STM32F3 与 F4 系列 Cortex M4 内核编程手册》 4.5 SysTick timer (STK) 章节或者参考库函数中 core_cm4.h 文件 。

目录

[一、SysTick 定时器介绍](#一、SysTick 定时器介绍)

[二、SysTick 定时器操作](#二、SysTick 定时器操作)

[2.1 SysTick 定时器寄存器](#2.1 SysTick 定时器寄存器)

[2.1.1 控制和状态寄存器:CTRL](#2.1.1 控制和状态寄存器:CTRL)

[2.1.2 重装载寄存器:LOAD](#2.1.2 重装载寄存器:LOAD)

[2.1.3 当前数值寄存器:VAL](#2.1.3 当前数值寄存器:VAL)

[2.1.4 校准数值寄存器:CALIB](#2.1.4 校准数值寄存器:CALIB)

[2.2 系统节拍定时器的工作原理](#2.2 系统节拍定时器的工作原理)

[2.3 SysTick 定时器操作步骤](#2.3 SysTick 定时器操作步骤)

[2.4 使用SysTick 定时器实现精准延时](#2.4 使用SysTick 定时器实现精准延时)

[2.4.1 实现1微秒延时](#2.4.1 实现1微秒延时)

[2.4.2 实现1毫秒延时](#2.4.2 实现1毫秒延时)

[2.4.3 实现1秒延时](#2.4.3 实现1秒延时)

[三、SysTick 定时实验](#三、SysTick 定时实验)

一、SysTick 定时器介绍

SysTick 定时器也叫 SysTick 滴答定时器,它是 Cortex-M4 内核的一个外设, 被嵌入在 NVIC 中,用来产生SYSTICK异常(异常号:15)。它是一个 24 位向下递减的定时器,每计数一次所需时间为 1/SYSTICK,SYSTICK 是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟 8 分频后获取,本套程序中我们采用后者,即每计数一次所需时间为 1/(168/8)us,换句话说在 1us 的时间内会计数 21 次。当定时器计数到 0 时,将 从 LOAD 寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启 SysTick 中断的话,当定时器计数到 0,将产生一个中断信号。如下图所示,因此只要知道计数的次数就可以准确得到它的延时时间。 因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统, 用于产生时基,维持操作系统的心跳。

如何计算延时时间?

如果时钟源选择8分频后的即21MHZ,那么,1秒钟就会计数21000000次,(计数一次的时间就是:1/21000000),如此:如果想要定时1毫秒,就要计数21000次,定时1微秒,就要计数21次!

二、SysTick 定时器操作

在 STM32F4 库函数中,并没有提供相应的 SysTick 定时器配置函数,我们要操作 SysTick 定时器就需要了解它的寄存器功能。其实 SysTick 定时器寄存器很 简单,只有 4 个,分别是 CTRL、LOAD、VAL、CALIB,在使用 SysTick 产生定时的时候, 只需要配置前三个寄存器,最后一个校准寄存器不需要使用。对应如下图所示:

2.1 SysTick 定时器寄存器

2.1.1 控制和状态寄存器:CTRL

CTRL 是 SysTick 定时器的控制及状态寄存器。其相应位功能如下:

注:CLKSOUTCE 位是用于选择 SysTick 定时器时钟来源:

  1. 如果该位为 1,表示其时钟是由系统时钟直接提供即 168M。
  2. 如果该位为 0,表示其时钟是由系统时钟八分频后提供即 168/8=21M。

2.1.2 重装载寄存器:LOAD

LOAD 是 SysTick 定时器的重装载数值寄存器。其相应位功能如下:

因为 STM32F4 的 SysTick 定时器是一个 24 位递减计数器,因此重装载寄存器中只使用到了低 24 位,即 bit0-bit23。当系统复位时,其值为 0。

2.1.3 当前数值寄存器:VAL

VAL 是 SysTick 定时器的当前数值寄存器。其相应位功能如下:

同样只有 bit0-bit24 有效,复位时值为 0。

2.1.4 校准数值寄存器:CALIB

CALIB 是 SysTick 定时器的校准数值寄存器。其相应位功能如下:

此寄存器在定时实验中不需要使用,可以不用了解。

2.2 系统节拍定时器的工作原理

当系统节拍定时器⼯作时,该定时器⾸先会从寄存器LOAD存储的值开始递减计数。当递减为0 后,寄存器CTRL的COUNTFLAG状态位会置1,同时会重装载寄存器LOAD预置的值。 当计数到0时,通过设置寄存器CTRL的TICKINT的值来产⽣异常(中断),或是⽆动作。

2.3 SysTick 定时器操作步骤

SysTick 定时器的操作可以分为 4 步:

  1. 设置 SysTick 定时器的时钟源。
  2. 设置 SysTick 定时器的重装初始值(如果要使用中断的话,就将中断使能打开)。
  3. 清零 SysTick 定时器当前计数器的值。
  4. 打开 SysTick 定时器。

2.4 使用SysTick 定时器实现精准延时

2.4.1 实现1微秒延时

cpp 复制代码
void Sleep_us(uint32_t us)
{
  while(us--)
  {
     SysTick ->CTRL = (1 << 0);   //定时器使能第0位置1
     SysTick ->CTRL &= ~(1<<2);   //选择时钟源:第2位置0,选择外部时钟源,由系统时钟八分频后提供即 168/8=21M
     SysTick ->CTRL &= ~(1<<1);   //延时时间到无动作:第1位置0

     SysTick ->VAL = 0x0;        //当前数值寄存器初值赋0
     SysTick ->LOAD = 21;       //重装载数值寄存器的值,定时1微秒,所以是21

     while(!(SysTick ->CTRL & (1<<16)));  //死循环等待计数值减到0
     SysTick ->CTRL = ~(1<<0);    //关闭定时器,第0位置0
  }
 
}


//复用上述函数实现延时1秒
void Sleep_s(uint32_t s)
{
   while(s--)
  {
      Sleep_ms(1000);
 }
}

2.4.2 实现1毫秒延时

cpp 复制代码
void Sleep_ms(uint32_t ms)
{
  while(ms--)
  {
     SysTick ->CTRL = (1 << 0);   //定时器使能第0位置1
     SysTick ->CTRL &= ~(1<<2);   //选择时钟源:第2位置0,选择外部时钟源,由系统时钟八分频后提供即 168/8=21M
     SysTick ->CTRL &= ~(1<<1);   //延时时间到无动作:第1位置0

     SysTick ->VAL = 0x0;        //当前数值寄存器初值赋0
     SysTick ->LOAD = 21000;       //重装载数值寄存器的值,定时1毫秒,所以是21000

     while(!(SysTick ->CTRL & (1<<16)));  //死循环等待计数值减到0
     SysTick ->CTRL = ~(1<<0);    //关闭定时器,第0位置0
  }
 
}

2.4.3 实现1秒延时

cpp 复制代码
void Sleep_s(uint32_t s)
{
  while(s--)
  {
     SysTick ->CTRL = (1 << 0);   //定时器使能第0位置1
     SysTick ->CTRL &= ~(1<<2);   //选择时钟源:第2位置0,选择外部时钟源,由系统时钟八分频后提供即 168/8=21M
     SysTick ->CTRL &= ~(1<<1);   //延时时间到无动作:第1位置0

     SysTick ->VAL = 0x0;        //当前数值寄存器初值赋0
     SysTick ->LOAD = 21000000;       //重装载数值寄存器的值,定时1秒,所以是21000000

     while(!(SysTick ->CTRL & (1<<16)));  //死循环等待计数值减到0
     SysTick ->CTRL = ~(1<<0);    //关闭定时器,第0位置0
  }
 
}

1秒=1000毫秒=1000微秒。

三、SysTick 定时实验

利用 SysTick 产生 1s 的时基,LED 以 1s 的频率闪烁。

led.h文件

cpp 复制代码
#ifndef __MYLED_H
#define __MYLED_H

void LED_Init(void);

#endif

led.c 文件

cpp 复制代码
#include "stm32f4xx.h"                  // Device header
#include "myled.h"

/*开时钟  打开外设对应的时钟(查看参考手册,该外设挂在哪个数据总线上),对应GPIO在哪条总线开哪条
	GPIOF外设 挂在AHB1总线上,所以要打开AHB1的时钟,双击函数,右键->go to definition*/


void LED_Init(void)
{
    //第一步:使能GPIOF的时钟 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟
	
   //第二步:GPIOF9,F10 初始化设置
   GPIO_InitTypeDef GPIO_InitStructure;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 对应 IO 口
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO

   //第三步:设置灯的初始状态
   GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 设置高电平,灯灭
}

mydelay.h

cpp 复制代码
#ifndef __MYLED_H
#define __MYLED_H

void LED_Init(void);

#endif

mydelay.c

cpp 复制代码
#include "stm32f4xx.h"                  // Device header
#include "mydelay.h"

void My_Delay_us(uint32_t num)
{
    while(num--)
    {
        SysTick ->CTRL = (1 << 0);
    
        SysTick ->CTRL &= ~(1<<2);
    
        SysTick ->CTRL &= ~(1<<1);
    
        SysTick ->VAL = 0x0;
    
        SysTick ->LOAD = 21;
    
        while(!(SysTick ->CTRL & (1<<16)));
        SysTick ->CTRL = ~(1<<0);
    }
}

void My_Delay_ms(uint32_t num)
{
    while(num--)
    {
        My_Delay_us(1000);
    }
}


void My_Delay_s(uint32_t num)
{
    while(num--)
    {
        My_Delay_ms(1000);
    }
}

main.c文件

cpp 复制代码
#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "mydelay.h"
#include "myled.h"

int main(void)
{
    LED_Init();
    
    while(1)
    {
        My_Delay_ms(1000);           //延时1秒
        GPIO_ToggleBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
    }
}

实验现象:

两个灯每隔一秒闪烁一次。

至此,我们的本次的学习就结束了。通过以上几个实验,相信对串口通信有了深入的理解,这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!

相关推荐
FreakStudio1 小时前
开源一款串口舵机驱动扩展板-FreakStudio多米诺系列
单片机·嵌入式·大学生·电子diy
艾格北峰2 小时前
STM32 物联网智能家居 (六) OLED显示设备
arm开发·stm32·单片机·嵌入式硬件·物联网·智能家居
weixin_535854225 小时前
oppo,汤臣倍健,康冠科技,高途教育25届春招内推
c语言·前端·嵌入式硬件·硬件工程·求职招聘
热爱嵌入式的小许6 小时前
STM32 HAL库&标准库+ESP8266+机智云
stm32·单片机·嵌入式硬件·stm32移植机智云·stm32连接机智云·hal库移植机智云·标准库移植机智云
无际单片机编程6 小时前
面对STM32的庞大体系,如何避免迷失在细节中?
java·stm32·单片机·嵌入式硬件·嵌入式开发
2301_764602239 小时前
stm32hal库寻迹+蓝牙智能车(STM32F103C8T6)
stm32·单片机·嵌入式硬件
楼台的春风9 小时前
PWM(脉宽调制)技术详解:从基础到应用实践示例
c语言·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式
Jack1530276827910 小时前
芯谷D668:便携式录音机与耳机式盒式录音机的理想音频解决方案
嵌入式硬件·音视频·家庭影院·麦克风阵列处理器·便携式录音机·耳机式盒式录音机
深圳市青牛科技实业有限公司 小芋圆10 小时前
芯谷D2761:为扬声器保护而生的音频限幅器
人工智能·科技·单片机·嵌入式硬件·机器人·音视频
程序员JerrySUN11 小时前
树莓派 4B:AI 物联网完整部署方案
linux·人工智能·嵌入式硬件·物联网·分类·数据挖掘