STM32 SYSTick高精度延时功能代码实现

2024-02-13 15:20

本文主要是介绍STM32 SYSTick高精度延时功能代码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、SYSTick定时器介绍
  • 二、SYSTick定时器和其他定时器的区别
  • 三、SYSTick定时器框图讲解
  • 四、HAL库中SYSTick配置代码讲解
  • 五、SYSTick实现高精度延时
  • 总结


前言

本篇文章将给大家讲解一下SYSTICK滴答定时器,以及讲解使用滴答定时器来实现高精度延时功能的代码。

一、SYSTick定时器介绍

SysTick定时器是嵌入式系统中常见的一个系统定时器,在ARM Cortex-M微控制器中广泛使用。下面是关于SysTick定时器的一些介绍:

用途: SysTick定时器通常被用作操作系统的时钟节拍(Tick)或者作为基本的定时器来执行周期性的任务。它可以提供一个精确的时间基准,用于定时器中断、延时函数的实现以及系统的时间管理。

定时器类型: SysTick定时器是一个24位向下计数器。它可以在一个范围内计数从最大值向0的时钟周期,然后在达到0时重新装载计数值,并触发一个中断(如果已使能)。

配置: 在STM32微控制器中,SysTick定时器可以通过设置相关的寄存器来配置。这些寄存器包括:

STK_CTRL:控制寄存器,用于使能或禁用SysTick定时器,选择时钟源和设置中断使能。
STK_LOAD:装载寄存器,用于设置初始的计数值。
STK_VAL:当前值寄存器,用于读取当前的计数值。
STK_CALIB:校准寄存器,用于存储SysTick定时器的校准值。

时钟源: SysTick定时器的时钟源可以选择为外部时钟或者系统时钟的一个分频。通常情况下,它与系统时钟同步,但也可以使用外部时钟来提供更灵活的配置选项。

中断: SysTick定时器可以在计数器溢出时触发中断。这种中断通常被用来实现操作系统的时钟节拍,或者用于周期性任务的执行。

应用: SysTick定时器广泛应用于嵌入式系统中,特别是在实时操作系统(RTOS)中用作系统时钟。它可以用来实现延时函数、精确的定时器中断、周期性任务的执行以及系统的时间管理。

二、SYSTick定时器和其他定时器的区别

1.SYSTick定时器是CPU内部的定时器,其他定时器是作为STM32的外部定时器。

之所以在处理器内增加一个定时器,是为了提高软件的可移植性。由于所有的 Cortex-M处理器都具有相同的SysTick定时器,为一种Cortex-M3/M4微控制器实现的OS也能适用于其他的Cortex-M3/M4微控制器
在这里插入图片描述
2.功能和用途:

SYSTick定时器通常用于实现系统的时间管理、时钟节拍以及简单的定时功能,如延时函数的实现等。
其他定时器(如TIM定时器)通常用于更复杂的定时和计时任务,例如PWM输出、捕获/比较模式、定时触发ADC转换等。

3.精度:

SYSTick定时器的精度通常受限于系统时钟频率,因此在一般情况下可能比较低。
其他定时器通常具有更高的精度,并且可以通过外部时钟源进行精确校准,因此适用于需要更精确时间控制的应用。

4.中断处理:

SYSTick定时器通常只能产生一个中断,用于系统的时钟节拍或简单的定时任务。
其他定时器可以配置多个中断触发条件,允许更灵活的中断处理和定时任务的执行。

5.寄存器和配置:

SYSTick定时器只有几个寄存器,配置相对简单。
其他定时器通常具有更多的寄存器和更复杂的配置选项,以支持各种不同的定时和计时功能。

6.外设依赖性:

SYSTick定时器不依赖于外部器件,因为它是CPU内部的一个特殊功能模块。
其他定时器通常依赖于外部时钟源和其他外部器件(例如计数输入、PWM输出引脚等)。

7.优先级:

由于SYSTick定时器是CPU内部的定时器,因此其中断处理通常具有较高的优先级。
其他定时器的中断处理优先级可能需要根据具体应用情况进行配置,并且可能不如SYSTick定时器的中断处理优先级高。

三、SYSTick定时器框图讲解

在这里插入图片描述
1.首先SYSTick根据处理器时钟或者参考时钟来减小计数

2.配置CTRL寄存器的第0位使能计数器,当前值寄存器在每个处理器时钟周期或参考时钟的上升沿都会减小。若计数减至0,它会从重加载寄存器中加载数值并继续
运行。

3.另外一个寄存器为 SysTick 校准值寄存器。它为软件提供了校准信息。由于 CMSISCore 提供了一个名为 SystemCoreClock 的软件变量(CMSIS 1.2及之后版本可用,CMSIS 1.1或之前版本则使用变量 SystemFrequency),因此它就未使用SysTick 校准值寄存器。系统初始化函数 SystemInit()函数设置了该变量,而且每次系统时钟配置改变时都要对其进行更新这种软件手段比利用SysTick 校准值寄存器的硬件方式更灵活。

四、HAL库中SYSTick配置代码讲解

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
滴答定时器配置代码:

__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 */
}

下面是对这段代码的配置流程的讲解:

1.参数检查:

if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{return (1UL);  /* Reload value impossible */
}

这一部分首先检查传入的 ticks 参数是否超过了SysTick定时器的重装载寄存器(LOAD寄存器)的最大值。如果超过了,说明无法设置这么大的重载值,函数返回1表示配置失败。否则,继续执行后续的配置步骤。

2.设置重载寄存器:

SysTick->LOAD  = (uint32_t)(ticks - 1UL);  /* set reload register */

这一行代码设置SysTick定时器的重载寄存器,确定计数器计数到多少时触发一次SysTick中断。ticks - 1 是因为计数器是从零开始计数的。

3.设置中断优先级:

NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);  /* set Priority for Systick Interrupt */

这里设置了SysTick定时器的中断优先级。NVIC_SetPriority 是一个用于设置中断优先级的CMSIS(Cortex Microcontroller Software Interface Standard)函数。__NVIC_PRIO_BITS 表示中断优先级位的数量,通常由硬件定义。这里将SysTick中断的优先级设置为最低,即最高数值。

4.清零计数器寄存器:

SysTick->VAL   = 0UL;  /* Load the SysTick Counter Value */

这一行代码清零SysTick定时器的计数器寄存器,确保计数器从零开始计数。

5.配置并启用SysTick定时器:

SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |SysTick_CTRL_TICKINT_Msk   |SysTick_CTRL_ENABLE_Msk;  /* Enable SysTick IRQ and SysTick Timer */

这一行代码配置并启用SysTick定时器。具体配置包括:

SysTick_CTRL_CLKSOURCE_Msk:选择SysTick定时器的时钟源,通常选择处理器时钟。
SysTick_CTRL_TICKINT_Msk:使能SysTick定时器中断。
SysTick_CTRL_ENABLE_Msk:启用SysTick定时器。

返回配置结果:

   return (0UL);  /* Function successful */

如果上述配置步骤都成功执行,函数返回0,表示配置成功。

五、SYSTick实现高精度延时

创建systick.c和systick.h来管理高精度延时的代码。

systick.c

#include "systick.h"//us级延时
void udelay(int us)
{uint32_t told = SysTick->VAL;uint32_t tnow;uint32_t load = SysTick->LOAD;/* LOAD+1个时钟对应1ms* n us对应 n*(load+1)/1000个时钟*/uint32_t ticks = us*(load+1)/1000;uint32_t cnt = 0;while (1){tnow = SysTick->VAL;if (told >= tnow)cnt += told - tnow;elsecnt += told + load + 1 - tnow;told = tnow;if (cnt >= ticks)break;}	
}//ms级延时
void mdelay(int ms)
{for (int i = 0; i < ms; i++)udelay(1000);	
}//获取系统上电到现在经过了多少ns
uint64_t system_get_ns(void)
{uint64_t ns = HAL_GetTick(); /* ms */ns = ns*1000000;uint32_t tnow = SysTick->VAL;	uint32_t load = SysTick->LOAD;uint64_t cnt;cnt = load+1-tnow; /* 没有考虑tnow等于0的情况 */ns += cnt * 1000000 / (load+1) ;return ns;	
}

systick.h

#ifndef __SYSTICK_H__
#define __SYSTICK_H__#include "main.h"void udelay(int us);void mdelay(int ms);uint64_t system_get_ns(void);#endif
  1. udelay(int us)
    这个函数实现了微秒级延时。其原理是利用SysTick定时器进行精确计时。首先,获取当前SysTick计数器的值 told,然后计算出需要延时的时钟周期数 ticks,接着在一个循环中不断获取当前计数器的值 tnow,并计算经过的时钟周期数 cnt。当经过的时钟周期数达到了延时所需的时钟周期数 ticks 时,跳出循环,延时结束。

  2. mdelay(int ms)
    这个函数实现了毫秒级延时,它通过多次调用微秒级延时函数来实现。每次调用 udelay(1000) 即相当于延时了1毫秒。

  3. system_get_ns(void)
    这个函数用于获取系统上电到当前时刻经过的纳秒数。首先,获取系统运行时间的毫秒数 ns,然后通过当前SysTick计数器的值 tnow 和 SysTick加载值 load 计算出当前经过的时钟周期数 cnt,进而将其转换为纳秒数并加到系统运行时间上,最终返回总的纳秒数。

总结

本篇文章主要讲解了SYSTick的概念,寄存器配置和使用,并且使用SYSTick实现了高精度延时,大家可以自己写代码实验验证一下正确性。

这篇关于STM32 SYSTick高精度延时功能代码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/705889

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja