SysTick 定时器

article/2025/10/10 19:34:42

11.1关于 SysTick 定时器

SysTick定时器(又名系统滴答定时器)是存在于Cortex-M3的一个定时器,只要是ARM Cotex-M系列内核的MCU都包含这个定时器。使用内核的SysTick定时器来实现延时,可以不占用系统定时器,节约资源。由于SysTick是在CPU核内部实现的,跟MCU外设无关,因此它的代码可以在不同厂家之间移植。

本 章 将 使用系统滴答定时器实现延时函数, 注 意 SysTick 用于了 HAL 库的毫秒级延时函数“HAL_Delay()”,不建议日常使用SysTick去作为其它用途,这里只作为演示。

SysTick定时器是一个24位递减定时器,即计数器可以从最大值224开始,每个时钟周期减1,当减到0时,会产生Systick异常,同时再自动重载定时初值,开始新一轮计数。通过设置这个定时初值,就可以实现得到指定时间。如下图 11.1.1 所示,y为定时器初值,然后随着时间增加,值逐渐减小,直至为0,再重新加载初值,如此往复,x1、x2、x3这些时间段,就是我们需要的延时时间。
在这里插入图片描述

假设STM32F103工作在72MHz,即72000000Hz,意味着1s时间内,会计数72000000次。那么1ms则计数72000000/1000=72000次。这个72000就可以作为系统滴答定时器的初始值,将这个值写入系统滴答定时器,定时器在每个时钟周期减1,减到0时,就刚好是1ms,同时产生中断通知,再次加载72000如此反复。HAL库提供“HAL_SYSTICK_Config()”函数去设置这个初始值。

系统滴答定时器控制寄存器比较少,整体比较简单,借助本次机会详细分析一下寄存器和HAL之间是调用关系。系统滴答定时器只有四个控制寄存器:STK_CTRL,STK_LOAD,STK_VAL和STK_CALIB。因 为系统滴答定时器属于Cotex-M3内核的外设,相关寄存器介绍不在《参考手册》,而在《3_STM32F10xx Cortex-M3编程手册》,后简称《编程手册》。

系统滴答定时器控制和状态寄存器(STK_CTRL)
在这里插入图片描述
重点关注Bit[0],用于使能系统滴答定时器,Bit[1]使能系统滴答定时器中断,Bit[2]系统滴答时钟的时钟来源。

系统滴答定时器加载值寄存器(STK_LOAD)
在这里插入图片描述
Bit[23:0],一共24位,用来设置系统滴答定时器的初始值,因此范围为1~ 16777216。

系统滴答定时器当前值寄存器(STK_VAL)
在这里插入图片描述
在这里插入图片描述
Bit[23:0],一共24位,用来获取当前系统滴答定时器的计数值。

系统滴答定时器校准值寄存器(STK_CALIB)
在这里插入图片描述
这个寄存器没用到,可以不用管。此外,当处理器在调试期间被暂停(halt)时,系统滴答定时器也将暂停运作。

在理解系统滴答定时器的工作方式,了解系统滴答定时器的寄存器基本信息后,就可以尝试编写程序了。

11.2硬件设计

系统滴答定时器属于Cortex-M3内核资源,不涉及外部硬件电路。实验中会用到LED灯,电路设计参考前面LED点灯实验。

11.3软件设计

11.3.1.1 软件设计思路

实验目的:使用系统滴答定时器实现自定义延时。

  1. 分析HAL库的系统滴答定时器配置函数;
  2. 初始化系统滴答定时器(设置计数初值、使能等);
  3. 封装延时函数,设置系统滴答定时器中断处理函数;
  4. 主函数调用验证;
    本实验配套代码位于“5_程序源码\4_基础重点—SysTick定时器”。

11.3.1.2 软件设计讲解

  1. 分析HAL库的系统滴答定时器配置函数
    在HAL库中,使用“HAL_SYSTICK_Config()”函数配置SysTick的初始值。
    代码段 11.3.1 SysTick 配置函数(stm32f1xx_hal_cortex.c)
/**
* @brief Initializes the System Timer and its interrupt, and starts the System Tick Timer.
* Counter is in free running mode to generate periodic interrupts.
* @param TicksNumb: Specifies the ticks Number of ticks between two interrupts.
* @retval status: - 0 Function succeeded.
* - 1 Function failed.
*/
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
return SysTick_Config(TicksNumb); }

该函数调用“SysTick_Config()”函数,函数内容如下代码段 11.3.2所示。
代码段 11.3.2 SysTick 配置函数(core_cm3.h)

/* ################################## SysTick function ############################################ */
/**
\ingroup CMSIS_Core_FunctionInterface
\defgroup CMSIS_Core_SysTickFunctions SysTick Functions
\brief Functions that configure the System.
@{
*/
#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)
/**
\brief System Tick Configuration
\details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts.
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* 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 (0UL); /* Function successful */
}
#endif
  • 24~27行:判断传入的SysTick初始值是否大于最大值2的24次方;
  • 29行:设置SysTick初始值;
  • 30行:设置SysTick中断的优先级,默认为最低;
  • 31行:将SysTick当前计数值清零;
  • 32~34行:设置SysTick的控制和状态寄存器,展开对应的宏,值为“(1<<2) | (1<<1) | (1)”,结合前面STK_CTRL寄存器介绍,可知这里使能了SysTick,使能了SysTick中断,时钟源为AHB。当系统时钟为72MHz时,AHB不分频,也为72MHz,则SysTick的时钟也为72MHz。

通过对“HAL_SYSTICK_Config()”函数分析,可知只需要传入SysTick初始值,其它的都默认已经设置完成了。

  1. 初始化系统滴答定时器
    假设当MCU工作在72MHz,SysTick也工作在72MHz。时钟在1s内完成周期性变化的次数叫做频率(单位:Hz),因此72MHz则表示1秒SysTick计数72000000次,即1毫秒计数72000次。
    因此,如果将72000传入“HAL_SYSTICK_Config()”函数,则SysTick从72000减到0,花费时间为1毫秒,创建函数“SysTickInit()”初始化系统滴答定时器,如代码段 11.3.3 所示。
    代码段 11.3.3 初始化 SysTick(driver_systick.c)
/*
* 函数名:void SysTickInit(uint32_t cycle)
* * 输入参数:cycle,设置系统滴答时钟周期
* 输出参数:无
* 返回值:无
* 函数作用:初始化系统滴答时钟的频率和中断优先级
*/
void SysTickInit(uint32_t cycle)
{
uint32_t init_t = 0;
init_t = SystemCoreClock/cycle;
/* 时间(单位:s)=1/频率(单位:HZ)
* SystemCoreClock 频率: 72MHz = 72,000,000
* 即 MCU 1 秒会计数 72,000,000 次 * 1ms 则计数 72MHz/1000 = 72000 次 * 72000 就是滴答时钟的初始值,它向下计数 72000 次,计数将变为 0,就会产生一次中断
* 滴答时钟初始值范围:1~16777216
** SystemCoreClock/1000: 1ms 中断一次
* SystemCoreClock/100000: 10us 中断一次
* SystemCoreClock/1000000: 1us 中断一次
*/
if(HAL_SYSTICK_Config(init_t) != HAL_OK)
{
Error_Handler(); }
// 设置滴答定时器中断优先级:最高
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
// 使能滴答定时器中断
HAL_NVIC_EnableIRQ(SysTick_IRQn); }
  • 12行:使用HAL库提供的全局变量“SystemCoreClock”获取当前系统时钟,再根据传入的cycle,计算出
    SysTick的初始值;
  • 25~28行:使用“HAL_SYSTICK_Config()”函数设置SysTick的初始值,并检测是否设置成功;
  • 31行:设置滴答定时器中断优先级,这里设置为最高。前面分析“HAL_SYSTICK_Config()”函数,知道该函数也会设置中断优先级,这里重新设置为最高优先级,在当前示例里,SysTick中断的优先级不重要;
  • 33行:使能SysTick中断;这里是使能NVIC,而“HAL_SYSTICK_Config()”函数使能的是SysTick;

为了方便修改SysTick的初始值,这里定义几个常见的延时周期,如代码段 11.3.4 所示。当需要延时周期为1毫秒时,传入“CYCLE_1MS”给“SysTickInit()”,则SysTick计数到零花费1毫秒

代码段 11.3.4 定义延时周期(driver_systick.h)

#define CYCLE_100MS 10
#define CYCLE_10MS 100
#define CYCLE_1MS 1000
#define CYCLE_100US 10000
#define CYCLE_10US 100000
#define CYCLE_1US 1000000
  1. 封装延时函数,设置系统滴答定时器中断处理函数
    创建延时函数“SysTickDelay()”,在该函数里设置自定义全局变量systick_t的初始值,SysTick每计数完一次则进入SysTick中断,将全局变量systick_t的值减1,如代码段 11.3.6 所示。一直到systick_t变为零,结束延时,如代码段 11.3.5 所示。

代码段 11.3.5 SysTick 延时函数(driver_systick.c )

/*
* 函数名:void SysTickDelay(uint16_t m)
* 输入参数:m-延时时间
* 输出参数:无
* 返回值:无
* 函数作用:滴答定时器实现的延时函数
*/
void SysTickDelay(uint32_t m)
{
systick_t = m;
while(systick_t != 0);
}

代码段 11.3.6 SysTick 中断处理函数(stm32f1xx_it.c)

/*
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void) {
HAL_IncTick();
if(systick_t) {
systick_t--; } }
  1. 主函数调用验证
    代码段 11.3.7 SysTick 延时点灯(main.c)
/*
* 初始化滴答时钟
* * 通过改变传入参数改变滴答时钟的频率,即 SysTickDelay(1)的时长
*/
SysTickInit(CYCLE_1MS);
// 初始化 LED
LedGpioInit();
while(1) {
/* 通过延时一段时间让 LED 亮灭实现 LED 闪烁,可以通过示波器打 LED 的引脚反转周期,精确看时间是否与设置的一致*/
RLED(ON); // 点亮 LED
SysTickDelay(1000); // 延时 CYCLE_1MS*1000=1s
RLED(OFF); // 熄灭 LED
SysTickDelay(1000); // 延时 CYCLE_1MS*1000=1s
}
  • 5行:初始化SysTick,这里传入CYCLE_1MS,则延时函数“SysTickDelay()”的单位为1毫秒;
  • 7~16行:初始化LED,调用延时函数“SysTickDelay()”,传入1000,则延时为1秒;

11.4实验效果

本实验对应配套资料的“5_程序源码\4_基础重点—SysTick定时器\”。打开工程后,编译,下载,可以看到红色LED灯间隔1秒,交替闪烁。读者可修改代码段 11.3.7 中的第5行时钟周期,或者13、15行的延时时间,改变LED灯的闪烁间隔时间。

通过LED展示SysTick的延时结果不够严谨,有条件的读者可以使用示波器或逻辑分析仪测试LED灯对应引脚(PB0,在J21_3引出)的翻转时间,如图 11.4.1 所示,分别修改延时时间10us、1ms、1s后逻辑分析仪测量值。
在这里插入图片描述


百问网技术论坛:
http://bbs.100ask.net/

百问网嵌入式视频官网:
https://www.100ask.net/index

百问网开发板:
淘宝:https://100ask.taobao.com/
天猫:https://weidongshan.tmall.com/

技术交流群(鸿蒙开发/Linux/嵌入式/驱动/资料下载)
QQ群:869222007(已满)752871361

单片机-嵌入式Linux交流群:
QQ群:536785813


http://chatgpt.dhexx.cn/article/vKOGuWqB.shtml

相关文章

JavaScript之定时器

定时器 一、 setTimeout() 定时器二、停止 setTimeout() 定时器三、setInterval() 定时器四、清除setInterval() 定时器五、电子时钟案例 在很多页面中&#xff0c;我们都可以看到一些倒计时或者和时间相关的效果&#xff0c;今天小熊将就JavaScript里面的倒计时做一概述。 首先…

STM32-通用定时器-定时器中断

1 STM32的定时器 STM32F103ZET6一共有8个定时器&#xff0c;其中分别为&#xff1a;高级定时器&#xff08;TIM1、TIM8&#xff09;&#xff1b;通用定时器&#xff08;TIM2、TIM3、TIM4、TIM5&#xff09;&#xff1b;基本定时器&#xff08;TIM6、TIM7&#xff09;。 …

Python——定时器

1.定时器 Timer定时器源码实现&#xff0c;和自定义一个线程方式一样&#xff0c;都是继承Thread类&#xff0c;重写了run()方法&#xff0c;只是实现的功能是延时执行一个函数或方法。 &#xff08;1&#xff09;线程定时器(Timer)解释&#xff1a; Timer类是Thread的子类&a…

[JavaEE]定时器

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.定时器的概念 2.标准库中的定时器 3.实现定时…

定时器的作用

一、简介。 在很多时候&#xff0c;我们设计网页时&#xff0c;为了某种表现形式&#xff0c;会使用到定时器这一功能&#xff0c;如&#xff1a;为了保证用户有仔细阅读我们的用户条款&#xff0c;我们会给确认按钮设置只有条款被打开&#xff0c;并超过5秒才允许点击。 二、…

定时器简介

文章目录 一.定时器基本介绍A.CPU时序B.定时器的原理 二.定时/计数器的相关寄存器A.定时器工作方式寄存器&#xff08;TMOD&#xff09;B.控制寄存器&#xff08;TCON&#xff09; 三.定时器的四种工作方式图解 一.定时器基本介绍 A.CPU时序 振荡周期&#xff1a;CPU外部晶振…

定时器基本常识

1.概念解读 1.1定时器和计数器&#xff0c;电路一样 1.2定时或者计数的本质就是让单片机某个部件数数 1.3当定时器用的时候&#xff0c;靠内部震荡电路数数 1.4当计数器用的时候&#xff0c;书外面的信号&#xff0c;读取针脚的数据 2.定时器怎么定时 定时器的本质原理&a…

定时器详解

1. 什么是定时器&#xff08;timer&#xff09; 定时器实际上就是Soc当中的一个内部外设。 &#xff08;1&#xff09;定时器与计数器 定时器常与计数器扯到一起&#xff0c;计数器也是soc当中的一个内部外设&#xff0c;计数器顾名思义是用来计数的&#xff0c;就和我们的秒…

定时器(Timer)

一、定时器是什么&#xff1f; 定时器类似于我们生活中的闹钟&#xff0c;可以设定一个时间来提醒我们。 而定时器是指定一个时间去执行一个任务&#xff0c;让程序去代替人工准时操作。 标准库中的定时器: Timer 方法作用void schedule(TimerTask task, long delay)指定dela…

STM32-定时器详解

目录 前言 一、定时器基本介绍 1. STM32定时器 2. 通用定时器功能和特点 3. 计数器模式 4. 定时器工作原理 a.定时器框图 b.时钟产生器部分 c.时基单元 d.输入捕获通道 e.输出比较通道&#xff08;PWM&#xff09; 二、定时器中断应用 1.内部时钟选择 2.计数器模式 …

typedef和#define

typedef是c语言中一个重要的关键字其作用是为一种数据类型定义了一个新的名字这里的类型包括&#xff08;int&#xff0c;char,double 等)和自定义数据类型&#xff0c;通俗一点来说就是为一种数据类型起一个别名 举个例子&#xff1a; 定义一个整型变量a并将其初始化为666&a…

typedef和define的区别、typedef的具体用法

typedef最核心的用法&#xff1a;给数据类型取别名&#xff0c;这个别名既可以是此数据类型的替换&#xff0c;也是指向此数据类型的指针。 具体用法&#xff08;对普通数据类型取别名&#xff09;&#xff1a; 对结构体数据类型取别名&#xff1a; typedef与define的区别&…

#define与typedef的区别

目录 &#xff08;1&#xff09;原理不同 &#xff08;2&#xff09;功能不同 &#xff08;3&#xff09;作用域不同 &#xff08;4&#xff09;对指针的操作不同 &#xff08;5&#xff09;补充 a.指针常量 b.常量指针 typedef和define都是替一个对象取一个别名&#x…

C语言中的typedef

C语言中的"typedef" 一、什么是typedef typedef是用于定义新的类型名&#xff0c;在编程中可以用typedef来定义新的类型名来代替已有的类型名 格式&#xff1a; typedef 已有类型名 新的类型名 通俗点说&#xff0c;就是为已有的类型取别名&#xff0c;例如 老鼠&am…

define 与typedef的区别

define 与typedef大体功能都是使用时给一个对象取一个别名&#xff0c;增强程序的可读性&#xff0c;但它们在使用时有以下几点区别&#xff1a; 1.定义不一样 define定义后面不用加分号&#xff0c;并且它的别名在对象的前面 typedef需要加分号&#xff0c;并且它的别后面替…

C语言学习笔记---typedef 简介

在单片机和操作系统中 typedef 会经常用到&#xff0c;它可以为某一个类型自定义名称。和#define比较类似。但是又有不同的地方。 typedef 创建的符号只能用于数据类型&#xff0c;不能用于值。而#define 创建的符号可以用于值。typedef 是由编译器来解释&#xff0c;而不是预…

typedef介绍

[20210330更新]&#xff1a;这篇博客写的时间有点久了:)。本次更新修改了博客内容中的错误和表述不当的地方。 本文介绍C语言中的关键字 typedef 的用法。 1 概述 typedef 为C语言的关键字&#xff0c;作用是为一种数据类型定义一个新名字&#xff0c;这里的数据类型包括内部…

【C语言】typedef的使用

目录 一、什么是typedef 二、typedef用法 1、对于数据类型使用例如&#xff1a; 2、对于指针的使用例如 3、对于结构体的使用 三、进阶typedef 1、数组指针 2、指针函数 3、Int *(*array[3])(int); 4、Void (*funA(int,void(*funB)(int)))(int); 四、Typedef与defin…

ubuntu etc 设置权限777带来的问题

ubuntu etc 设置权限777带来的问题--sudoers权限错误 在一次工作中&#xff0c;在ubutu通过命令行输入 sudo chmod -R 777 /etc 命令&#xff0c;误将etc目录权限更改为了777&#xff08;rwxrwxrwx&#xff09; 导致服务器无法ssh远程连接&#xff0c;而且sudo命令无法使用 提…

Linux修改文件权限为777

将文件权限改为777的命令为chmod 777 文件名 777说明&#xff1a; 你可以在linux终端先输入ll,可以看到如: -rwx-r--r-- (一共10个参数) 第一个跟参数跟chmod无关,先不管. 2-4参数:属于user 5-7参数:属于group 8-10参数:属于others 接下来就简单了:r>可读 w>可写 x&…