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

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

公共筛选组件(二次封装antd)支持代码提示

如果项目是基于antd组件库为基础搭建,可使用此公共筛选组件 使用到的库 npm i antdnpm i lodash-esnpm i @types/lodash-es -D /components/CommonSearch index.tsx import React from 'react';import { Button, Card, Form } from 'antd'

17.用300行代码手写初体验Spring V1.0版本

1.1.课程目标 1、了解看源码最有效的方式,先猜测后验证,不要一开始就去调试代码。 2、浓缩就是精华,用 300行最简洁的代码 提炼Spring的基本设计思想。 3、掌握Spring框架的基本脉络。 1.2.内容定位 1、 具有1年以上的SpringMVC使用经验。 2、 希望深入了解Spring源码的人群,对 Spring有一个整体的宏观感受。 3、 全程手写实现SpringM

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo