首页 > 其他分享 >04. 系统滴答定时器

04. 系统滴答定时器

时间:2024-02-24 20:57:42浏览次数:21  
标签:定时器 HAL 函数 04 滴答 us Delay 延时 SysTick

一、系统滴答定时器概述

  SysTick,即系统滴答定时器,它包含在 M3/4/7 内核里面,核心是一个 24 位的递减计数器。当计数值减到 0 时,将从 RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。

SysTick工作原理

二、SysTick寄存器介绍

  SysTick 定义在 core_m4.h 里面,里面包含 CTRL、LOAD、VAL、CALIB 等 4 个寄存器。

【1】、SysTick 控制及状态寄存器(CTRL)

SysTick的CTRL寄存器

ST 公司将 SysTick->CTRL 的位 2 定义成时钟源的分频系数,CLKSOURCE=0 时 8 分频,CLKSOURCE=1 时 1 分频;

【2】、SysTick 重装载数值寄存器(LOAD)

SysTick的LOAD寄存器

【3】、SysTick 当前数值寄存器(VAL)

SysTick的VAL寄存器

三、实现延迟

  这里我们使用 时钟摘取法 的方式实现延迟。以 Delay_us() 函数为例,比如 Delay_us(50),在刚进入 Delay_us() 函数的时候先计算好这段延时需要等待的 SysTick 计数次数,这里为 50 * 168 * 168

(假设系统时钟为 168Mhz,因为 SysTick 的频率等于系统时钟频率,那么 SysTick 每增加 1,就是 1/168us),然后我们就一直统计 SysTick 的计数变化,直到这个值变化了 50 * 168,一旦检测到变化达到或者超过这个值,就说明延时 50us 时间到了。这样,我们只是抓取 SysTick 计数器的变化,并不需要修改 SysTick 的任何状态。

3.1、延迟初始化函数

uint16_t g_frequency_us = 0;                                                    // us延时倍乘数

/**
 * @brief 延迟初始化函数
 * 
 * @param clock 系统时钟频率,单位为MHz
 */
void Delay_Init(uint16_t clock)
{
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);                        // 设置SysTick时钟源为HCLK
    g_frequency_us = clock;                                                     // 1us定时的计数频率
}
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  这句代码把 SysTick 的时钟选择为 内核时钟,这里需要注意的是:SysTick 的时钟源自 HCLK,假设我们外部晶振为 8MHz,然后倍频到 168MHz,那么 SysTick 的时钟即为 168MHz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/168us。

3.2、微秒级延迟函数

/**
 * @brief 微秒级延迟函数
 * 
 * @param time 要延迟的微秒数
 */
void Delay_us(uint32_t time)
{
    uint32_t tick = 0;                                                     
    uint32_t t_old = 0, t_now = 0, t_count = 0;
    uint32_t reload = SysTick->LOAD;                                            // LOAD的值

    tick = time * g_frequency_us;                                               // 延迟时间加载
    t_old = SysTick->VAL;                                                   

    while (1)
    {
        t_now = SysTick->VAL;                                                   // tnow用于记录当前的SysTick->VAL 值
        if (t_now < t_old)                                                      // 在一轮内,t_count加等于t_old到t_now的差值
        {
            t_count += t_old - t_now;
        }
        else                                                                    // 超过一轮内,t_count加等于重装值减t_now到t_old的差值,即VAL-(t_now-t_old)
        {
            t_count += reload - t_now + t_old;
        }
        t_old = t_now;                                                          // t_old用于记录最近一次的SysTick->VAL值
        if (t_count >= tick)                                                    // 时间超过或等于要延迟的时间,则定时时间到,退出
        {
            break;
        }
    }
}

  这里使用了 时钟摘取法,tick 是延时 time 需要等待的 SysTick 计数次数(也就是延时时间),t_old 用于记录最近一次的 SysTick->VAL 值,然后 t_now 则是当前的 SysTick->VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 t_count 里面,然后通过对比 t_count 和 tick,来判断延时是否到达,从而达到不修改 SysTick 实现 time 的延时。

3.3、毫秒级延迟函数

void Delay_ms(uint32_t time)
{
    // 这里用540,是考虑到可能有超频应用,比如248M的时候,delay_us()最大只能延时541ms左右了
    uint32_t repeat = time / 540;
    uint32_t remain = time % 540;

    while (repeat)
    {
        Delay_us(540 * 1000);                                                   // 利用delay_us()实现540ms延时
        repeat--;
    }

    if (remain)
    {
        Delay_us(remain * 1000);                                                // 利用delay_us(),把尾数延时(remain ms)给做了
    }
}

  该函数其实就是多次调用 delay_us() 函数,来实现毫秒级延时的。我们做了一些处理,使得调用 delay_us() 函数的次数减少,这样时间会更加精准。

3.4、重定向HAL库延迟函数

  HAL 库提供的延时函数,只能实现简单的毫秒级别延时,没有实现 us 级别延时。HAL 库的 HAL_Delay() 函数定义如下:

__IO uint32_t uwTick;
uint32_t uwTickPrio   = (1UL << __NVIC_PRIO_BITS); /* Invalid PRIO */

__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while((HAL_GetTick() - tickstart) < wait)
  {
  }
}
__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}
void SysTick_Handler(void)
{
  HAL_IncTick();
}
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

  HAL 库实现延时功能非常简单,首先定义了一个 32 位全局变量 uwTick,在 Systick 中断服务函数 SysTick_Handler() 中通过调用 HAL_IncTick() 实现 uwTick 值不断增加,也就是每隔 1ms 增加 uwTickFreq,而 uwTickFreq 默认是 1。而 HAL_Delay() 函数在进入函数之后先记录当前 uwTick 的值,然后不断在循环中读取 uwTick 当前值,进行减运算,得出的就是延时的毫秒数。

  但是,HAL 库的延时函数有一个局限性,在中断服务函数中使用 HAL_Delay() 会引起混乱(虽然一般禁止在中断中使用延时函数),因为它是通过中断方式实现,而 SysTick 的中断优先级是最低的,所以在中断中运行 HAL_Delay() 会导致延时出现严重误差。

  HAL 库默认的延时函数(HAL_Dealy() 函数)是用 __weak 修饰的弱类型的函数,当我们重写 HAL_Delay() 函数来覆盖 HAL 库默认的延时函数。

/**
 * @brief 重定向HAL库的延迟函数
 * 
 * @param time 要延迟微秒数
 */
void HAL_Delay(uint32_t time)
{
    Delay_ms(time);
}

标签:定时器,HAL,函数,04,滴答,us,Delay,延时,SysTick
From: https://www.cnblogs.com/kurome/p/18031549

相关文章

  • 04 Jvav 安装开发环境
    Jvav安装开发环境JKD卸载JDK删除jvav的安装目录删除JAVA_HOME删除path下关于Java的目录java-version安装JDK百度搜索JDK8,找到下载地址JDK官方下载地址同意协议下载电脑对应的版本(列如本人电脑是windows需要选择win版本下载)双击安装JDK-->点击下一步......
  • Ubuntu22.04安装mysql8数据库
    1、去官网下载APT存储库文件2、到/usr/local目录下创建mysql目录并且用rz命令上传下载的文件若没有安装lrzsz自行安装。3、解压下载的文件)直接选择OK4、更新包信息5、安装设置初始密码选第二个选项6、登录数据库......
  • Ubuntu20.04 系统 ALERT! UUID=xxx does not exist. Dropping to a shell!
    Gaveupwaitingforrootdevice.Commonproblems:-Bootargs(cat/proc/cmdline)-Checkrootdelay=(didthesystemwaitlongenough?)-Missingmodules(cat/proc/modules;ls/dev)ALERT!UUID=718ed077-947d-4018-80ad-59825678e81ddoesnotexist.Dropping......
  • 使用基本定时器使led灯闪烁
    基本定时器原理图这两个成员在基本定时器中用不到,在stm32df103中timer6和timer7是基本定时器不可少的代码TIM_IT_Config()\\定时器中断TIM_ClearFlag()\\清除计时器中断标志位BASIC_TIM_APBxClock_FUN()\\计时器开启或者中断函数voidTIM6_IRQHandler()\\定时器中端服......
  • linux(ubuntu22.04)+PicGo(gui版)+阿里云oss搭建图床教程
    linux(ubuntu22.04)+PicGo(gui版)+阿里云oss搭建图床教程资源库PicGo下载链接:山东镜像源github原版阿里云oss链接linux下PicGo(gui版)的安装从资源库链接里下载后缀为.AppImage的安装包,版本可以选择稳定版2.3.1也可以用更新的beta版。修改文件权限,打开文......
  • 04. 场景绘制和叠层设置
    将素材中的Forest-1进行切割spritemode改为Multiplepixelperunit改为16FilterMode改为Point(nofilter)Compression改为None切割图片的时候,按大小16x16进行切割,pivot改成中间创建瓦片调色盘打开Window->2D->TilePalette创建新的调色盘,把调色......
  • timeBeginPeriod 高精度定时器 Sleep
    #include"timeapi.h"#pragmacomment(lib,"winmm")//DWORD__stdcallThreadTest(LPVOIDpThreadParam){CLogmLog;inti=100;timeBeginPeriod(1);//1表示1ms精度while(i--){mLog.WriteLog("%d",i);......
  • Java基础04:数据类型扩展及面试题讲解
    1.进制运算......
  • 洛谷题单指南-贪心-P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
    原题链接:https://www.luogu.com.cn/problem/P1090题意解读:两两合并,是典型的哈夫曼编码算法思想,贪心即可。解题思路:要是合并体力消耗最少,就要让尽可能少的果子越晚合并越好,因此,贪心策略为优先选择数量最少的两堆果子合并,一直到剩下一堆果子,把合并过程中的消耗值累加即可,要快速......
  • ubuntu server 22.04.2 LTS安装流程
    1、下载Ubuntu镜像下载地址:清华大学开源软件镜像站 选择Ubuntu版本22.04.XX(amd64,Server),其中XX小版本自选(当前示例版本为22.04.2)。 2、本地加载Ubuntu的iso镜像默认选中「TryorInstallUbuntuServer」安装选项,回车(或等待30秒后),等待系统镜像自检并进行安装初始化......