STM32-笔记10-手写延时函数(SysTick)

news2025/2/14 1:34:44
1、什么是SysTick

        Systick,即滴答定时器,是内核中的一个特殊定时器,用于提供系统级的定时服务。该定时器是一个24位的倒计数定时器‌。它从设定的初值(即重载值)开始计数,每经过一个系统时钟周期,计数值就减1,直到计数到0时,SysTick计数器会自动从RELOAD寄存器中重装初值并继续计数‌ 1  。如果中断使能,当计数到0时,还会触发中断‌ 1  。
        Systick定时器的主要功能包括实现简单的延时、生成定时中断以及进行精确定时和周期定时操作。此外,Systick定时器还可以被用于其他目的,例如作为操作系统的时基(如FreeRTOS),或者用于软件看门狗等系统调度操作。在STM32中,Systick通常以HCLK(AHB时钟)或HCLK/8作为运行时钟。

2、SysTick工作原理

        在使用Systick定时器进行延时操作时,可以设定初值并使能后,每经过一个系统时钟周期,计数值就减1。 当计数到0时,Systick计数器自动重装初值并继续计数,同时内部的COUNTFLAG标志会置位,触发中断 (如果中断使能)。这样,可以在中断处理函数中实现特定的延时逻辑。

3、SysTick寄存器介绍

SysTick控制及状态寄存器(CTRL)

SysTick重装载数值寄存器(LOAD)

SysTick当前数值寄存器(VAL)

 4、手写代码
#include "delay.h"

/**
  * @brief  微秒级延时
  * @param  nus 延时时长,范围:0~233015
  * @retval 无
  */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = 72 * nus;                           /* 设置定时器重装值 */
    SysTick->VAL = 0x00;                                /* 清空当前计数值 */
    SysTick->CTRL |= 1 << 2;                            /* 设置分频系数为1分频 */
    SysTick->CTRL |= 1 << 0;                            /* 启动定时器 */
    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));     /* 等待计数到0 */
    SysTick->CTRL &= ~(1 << 0);                         /* 关闭定时器 */
}

/**
  * @brief  毫秒级延时
  * @param  nms 延时时长,范围:0~4294967295
  * @retval 无
  */
void delay_ms(uint32_t nms)
{
    while(nms--)
        delay_us(1000);
}
 
/**
  * @brief  秒级延时
  * @param  ns 延时时长,范围:0~4294967295
  * @retval 无
  */
void delay_s(uint32_t ns)
{
    while(ns--)
        delay_ms(1000);
}

/**
  * @brief  重写HAL_Delay函数
  * @param  nms 延时时长,范围:0~4294967295
  * @retval 无
  */
void HAL_Delay(uint32_t nms)
{
    delay_ms(nms);
}
5、手写代码分析

关于函数delay_s(); = 1000*delay_ms(); = 1000*delay_us();之间的换算

等价于=>1s = 1000ms; 1ms = 1000us;

秒,毫秒之间的延迟函数只需要相互调用就好,重点是关于微秒的实现

下面这段代码是微妙的实现方法:

void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = 72 * nus;                           /* 设置定时器重装值 */
    SysTick->VAL = 0x00;                                /* 清空当前计数值 */
    SysTick->CTRL |= 1 << 2;                            /* 设置分频系数为1分频 */
    SysTick->CTRL |= 1 << 0;                            /* 启动定时器 */
    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));     /* 等待计数到0 */
    SysTick->CTRL &= ~(1 << 0);                         /* 关闭定时器 */
}

我们知道SysTick有三个寄存器,分别是CTRL、LOAD、VAL

SysTick启动对应的寄存器使用方法为:SysTick->LOAD

给相应的寄存器存它的意义,作用,使用=、|=、&=

= :直接赋值

|= :把某一位给它置1

&= :把某一位给它置0

  • 为什么给定时器重装值是 72*nus ?

        因为这里选用设置的是72MHZ;分频系数是1分频的SysTick,所以是72*nus(nus是传递进来多少微妙的数值)

        如果选用设置的是72MHZ;分频系数是8分频的SysTick,就应该是72/8 = 9,是9*nus.

  • 那么为什么是72乘以微妙数呢?

        72MHZ = 72 000 000 HZ

        1S = 1000 MS = 1000 000 US

        72MHZ是指STM32微控制器的系统时钟频率‌。在STM32微控制器中,72MHz通常是系统时钟(SYSCLK)的频率,72MHZ表示每秒钟有72,000,000个时钟周期。

        所以1000 000 us= 71 000 000 HZ

        1us有72个时钟周期

        所以,重装值被赋值为72*nus

  • 为什么设置分频系数为1分频时是SysTick->CTRL |= 1 << 2;这种表现形式?

        CTRL是控制及状态寄存器,对应配置分频系数是位段2,要把位段2赋值为1,

        把CTRL赋值为1,向左移2位

  • 思考:启动定时器,怎么设置?

        读上述信息可以知道,定时器的启动和关闭是CTRL寄存器的ENABLE来管理,当ENABLE为1的时候启动定时器,复位值为0的时候关闭计时器。所以CTRL赋值为1,向左移0位,表示为:1<<0;

        所以设置为:SysTick->CTRL |= 1 << 0; // |= :把某一位给它置1

        关闭定时器就是:SysTick->CTRL &= ~(1 << 0); // &= :把某一位给它置0

        因为在关闭定时器的时候CTRL的倒数第三位,也就是开启定时器时向左移两位的位置,已经赋值为1了,现在要把这个位置赋值为0,可以使用与运算,1&0 = 0;原理,所以,先把CTRL赋值为1,向左移0位,然后取反,就搞出了一个倒数第三位是0的一个位,使用与运算,与原来CTRL的值进行与,就会把1置为0.

  • 为什么要使用do-while来进行循环?

        因为从启动滴答定时器开始,滴答定时器就在一直倒数,我们要等滴答定时器,数完,然后将定时器关闭,这样就完成了1us的延时,所以do-while语句就是一个等的操作。

  • do-while中具体怎么实现?

        首先,我们明白了do-while存在的意义,那么我们知道在SysTick中CTRL寄存器的位段16 COUNTFLAG的作用是 当SysTick数到0时,该位为1,所以我们只需要在while中判断COUNTFLAG什么时候为1就知道SysTick什么时候数完了。

while(!(SysTick->CTRL & (1 << 16))); //可以这么写,但是有点小错误还需要完善

  •  会出现什么错误呢?

        如果你只是单一的使用delay,那么不会出错,但是如果在很多地方使用了delay,可能会在其他地方被关掉了,所以还需要一个判断条件。需要判断你此时的定时器是正常的(还在开启的模式下)。

 do{
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));

        使用do-while多做一步,定义一个临时变量temp来承接SysTick->CTRL;的值,方便用来进行判断。

6、系统HAL_Delay()函数代码流程

在上述代码中可以看出来HAL库自带的delay函数最小只支持到ms级别(HAL_Delay();)

 可以在这里看到: __weak void HAL_Delay(uint32_t Delay)这条语句。

双击点开,看到该函数由于_weak void...是弱函数,可以重写的函数所以可以在delay.c文件中重写void HAL_Delay(uint32_t Delay);函数

在上面的图片中我们可以看到,Delay形参是有外界传过来的时间,也就是想要延迟多久的时间,最小单位是ms,uint32表示32位无符号整数,不能表示小数,所以Delay的值为整数。

在HAL_Delay函数的上下文中,HAL_GetTick()和tickstart都是调用HAL_GetTick()函数时获取的系统时钟滴答数(通常是以毫秒为单位的)。HAL_GetTick()函数返回一个无符号的32位整数,表示自系统启动以来的滴答数。用tickstart变量来承接HAL_GetTick();函数是当前系统时钟滴答数也就是刚进到这个函数时的时间。

后面在while子句(HAL_GetTick() - tickstart) < wait中,while函数不断循环,HAL_GetTick()函数不断被调用,此时这里的HAL_GetTick()函数就是每一次读完一轮数以后执行中断服务函数时的时间。

 

是SysTick的中断服务函数,思考:那么在什么时候会触发这个中断服务函数呢?

答案:SysTick是递减计数器,当计数器数到0的时候,就会触发一次中断(前提中断使能)

         故上述的while子句中HAL_GetTick()函数就是每一次读完一轮数以后执行中断服务函数时的时间。

        SysTick是递减计数器,HAL_GetTick() - tickstart) < wait也就是 (新获取的时间-最开始的时间)<设置想要的时间 ,在实际应用中,HAL_GetTick()的值在每次调用时都会增加(通常是因为SysTick定时器的中断服务例程会在每经历过1ms时增加它),所以HAL_GetTick() - tickstart的值最终会超过wait,从而退出while循环。

7、在main.c函数中查找系统HAL滴答定时器的初始化设置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2266066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch-脚本查询

脚本查询 概念 Scripting是Elasticsearch支持的一种专门用于复杂场景下支持自定义编程的强大的脚本功能&#xff0c;ES支持多种脚本语言&#xff0c;如painless&#xff0c;其语法类似于Java,也有注释、关键字、类型、变量、函数等&#xff0c;其就要相对于其他脚本高出几倍的性…

C项目 天天酷跑(下篇)

上篇再博客里面有&#xff0c;接下来我们实现我们剩下要实现的功能 文章目录 碰撞检测 血条的实现 积分计数器 前言 我们现在要继续优化我们的程序才可以使这个程序更加的全面 碰撞的检测 定义全局变量 实现全局变量 void checkHit() {for (int i 0; i < OBSTACLE_C…

设计模式详解(建造者模式)

1、简述 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它通过将对象的构造过程与表示分离&#xff0c;使得相同的构造过程可以创建不同的表示。建造者模式尤其适用于创建复杂对象的场景。 2、什么是建造者模式&#xff1f; 建造者模式…

【Git 常用操作:pull push】

Git 基本概念 Git 是一个先进的开源的分布式版本控制系统&#xff0c;常用于管理工作内容、项目代码等功能。 Git 工作流程 图片来源&#xff1a;https://www.runoob.com/git/git-basic-operations.html 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff…

个人笔记:ORM数据库框架EFCore使用示例,运行通过,附源码

个人笔记&#xff1a;ORM数据库框架EFCore使用示例&#xff0c;运行通过&#xff0c;附源码 0.新建项目1. 设置环境1.1. 添加 NuGet 包1.2. 创建模型类 2. 创建上下文类3. 创建数据库和表3.1. 启用迁移3.2. 更新数据库 4. 插入数据5. 查询数据6. 更新数据7. 删除数据8. 完整示例…

IndexOf Apache Web For Liunx索引服务器部署及应用

Apache HTTP Server 是一款广泛使用的开源网页服务器软件,它支持多种协议,包括 HTTP、HTTPS、FTP 等 IndexOf 功能通常指的是在一个目录中自动生成一个索引页面的能力,这个页面会列出该目录下所有的文件和子目录。比如网上经常看到的下图展现的效果,那么接下来我们就讲一下…

Ubuntu 24.04.1 LTS快速源码安装postgresql15

虽然在Ubuntu中我们可以通过apt直接获取postgresql安装&#xff0c;但有些时候&#xff0c;为了自行配置postgresql安装路径和部分组件参数&#xff0c;我们需要源码安装postgresql。今天我们就通过源码编译postgresql15源码进行安装。 一、获取安装包 我们登录postgresql官网…

数据结构之栈,队列,树

目录 一.栈 1.栈的概念及结构 2.栈的实现 3.实现讲解 1.初始化栈 2.销毁栈 3.压栈 4.出栈 5.返回栈顶元素 6.返回栈内元素个数 7.判断栈内是否为空 二.队列 1.队列的概念及结构 2.队列的实现 3.实现讲解 1.初始化队列 2.销毁队列 3.单个成员入队列 4.单个成员…

Git基本操作快速入门(30min)

Git基本操作快速入门&#xff08;30min&#xff09; 文章目录 Git基本操作快速入门&#xff08;30min&#xff09;1. 建立本地仓库2. 本地仓库链接到远端仓库3. 将本地仓库推送到远端4. Git常用命令 作为一名程序员&#xff0c;使用Github来进行代码的版本管理是必修课&#xf…

汽车IVI中控开发入门及进阶(42):OpenVG

概览: OpenVG是一个无版权、跨平台的API,它为高级用户界面和矢量图形库(如SVG)提供了一个低级硬件加速接口。OpenVG主要针对需要便携式加速高质量矢量图形以获得引人注目的用户界面和文本的消费电子产品、手持设备、可穿戴设备和汽车设备,同时使硬件加速能够在非常低的功…

[1111].集成开发工具Pycharm安装与使用

所有博客大纲 后端学习大纲 Python大纲 1.下载&#xff1a; 官方下载地址 2.安装&#xff1a; 1.双击exe文件&#xff0c;然后下一步选择安装目录 2.选择桌面快捷方式及安装&#xff1a; 3.安装完成 3.启动&#xff1a; 4.设置&#xff1a; 4.1.设置运行时环境&#xff1a;…

Windows11 安装 Ubuntu-20.04,同时安装配置 zsh shell,配置 git 别名(alias),大大提高开发效率

背景&#xff1a;家里配置了一台 Windows 电脑&#xff0c;有时候需要用到 vscode 开发测试一些代码&#xff0c;在使用过程中发现原生 windows 敲代码不是很友好&#xff0c;于是想到配置 wsl&#xff0c;安装 Ubuntu&#xff0c;并安装配置 zsh shell&#xff0c;同时配置 gi…

面试场景题系列:设计限流器

首先看看使用API限流器的好处。 •预防由拒绝服务攻击(Denial of Service&#xff0c;DoS)引起的资源耗尽问题。大型科技公司发布的所有API几乎都强制执行某种形式的限流操作。例如&#xff0c;推特限制每个用户每3小时最多发300条推文。谷歌文档API的默认限制是每个用户每60秒…

Node.js 工具:在 Windows 11 中配置 Node.js 的详细步骤

一、概述 记录时间 [2024-12-25] 本文讲述如何在 Windows 11 中进行 Node.js 工具的安装和配置。 以下是详细的步骤和说明。 二、安装 Node.js 1. 官网下载 通过官网&#xff0c;下载 Node.js&#xff0c;上面有好几种下载方式&#xff0c;文中下载的是 zip 压缩包。 如图&…

DevOps实战:用Kubernetes和Argo打造自动化CI/CD流程(2)

DevOps实战&#xff1a;用Kubernetes和Argo打造自动化CI/CD流程&#xff08;2&#xff09; 背景 架构图 正片开始之前&#xff0c;请一定先熟悉上面的架构图&#xff0c;跟着我的步骤&#xff0c;一步一步执行成功&#xff0c;相信后续根据自己特定的需求定制CI/CD。 需求 …

【Java 学习】详细讲解---包和导包、Scanner类、输入源

1. 包 1.1 什么是包&#xff1f; 举个例子&#xff0c;你和你的同学有不同的家庭&#xff0c;你们都有自己的爸爸妈妈&#xff0c;都有自己的家。在自己的家中你们可以按照自己爱好摆放东西&#xff0c;都互不干扰。但是&#xff0c;假如你们的家都在一起&#xff0c;你们就不…

EasyExcel停更,FastExcel接力

11月6日消息&#xff0c;阿里巴巴旗下的Java Excel工具库EasyExcel近日宣布&#xff0c;将停止更新&#xff0c;未来将逐步进入维护模式&#xff0c;将继续修复Bug&#xff0c;但不再主动新增功能。 EasyExcel以其快速、简洁和解决大文件内存溢出的能力而著称&#xff0c;官方…

Python PDF批量加密工具

Python PDF批量加密工具 1.简介&#xff1a; ‌一个Python写的PDF批量加密工具。PDF批量加密‌是一种保护PDF文件安全性的方法&#xff0c;通过为多个PDF文件设置相同的密码&#xff0c;防止未经授权的用户访问这些文件。批量加密可以大大提高工作效率&#xff0c;特别是在处…

项目2路由交换

背景 某学校为满足日常教学生活需求&#xff0c;推动数字校园的建设&#xff0c;学校有办公楼和学生宿舍楼和服务器集群三块区域&#xff0c;请合理规划IP地址和VLAN&#xff0c;实现企业内部能够互联互通现要求外网能通过公网地址访问服务器集群&#xff0c;学生和老师能正常…

知识梳理笔记--Kerberos 协议

Kerberos 协议概述 Kerberos 是一种计算机网络认证协议&#xff0c;旨在为不安全的网络提供强认证服务。它通过中心化的身份验证系统&#xff08;即 Key Distribution Center&#xff0c;KDC&#xff09;来确保通信双方的身份验证和数据加密。Kerberos 协议主要用于确保计算机系…