【转】STM32 不占用定时器(包括SysTick)实现精确延时(巧用DWT)

    /**
      ******************************************************************
      * file    core_delay.c
      * author  fire
      * version V1.0
      * date 2018-xx-xx
      * [url=home.php?mod=space&uid=247401]@brief[/url]   使用内核寄存器精确延时
      ******************************************************************
      * @attention
      *
      * 实验平台:野火 STM32开发板
      * 论坛    :http://www.firebbs.cn
      * 淘宝    :https://fire-stm32.taobao.com
      *
      ******************************************************************
      */

    #include "./delay/core_delay.h"   

    /*
    **********************************************************************
    *         时间戳相关寄存器定义
    **********************************************************************
    */
    /*
    在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace),
    该外设有一个32位的寄存器叫CYCCNT,它是一个向上的计数器,
    记录的是内核时钟运行的个数,最长能记录的时间为:
    10.74s=2的32次方/400000000
    (假设内核频率为400M,内核跳一次的时间大概为1/400M=2.5ns)
    当CYCCNT溢出之后,会清0重新开始向上计数。
    使能CYCCNT计数的操作步骤:
    1、先使能DWT外设,这个由另外内核调试寄存器DEMCR的位24控制,写1使能
    2、使能CYCCNT寄存器之前,先清0
    3、使能CYCCNT寄存器,这个由DWT_CTRL(代码上宏定义为DWT_CR)的位0控制,写1使能
    */
    #define  DWT_CR      *(__IO uint32_t *)0xE0001000 //< 0xE0001000 DWT_CTRL RW The Debug Watchpoint and Trace (DWT) unit
    #define  DWT_CYCCNT  *(__IO uint32_t *)0xE0001004 //< 0xE0001004 DWT_CYCCNT RW Cycle Count register, 
    #define  DEM_CR      *(__IO uint32_t *)0xE000EDFC //< 0xE000EDFC DEMCR RW Debug Exception and Monitor Control Register.

    #define  DEM_CR_TRCENA                   (1 << 24)  //DEMCR的DWT使能位
    #define  DWT_CR_CYCCNTENA                (1 <<  0)  //DWT的SYCCNT使能位
    /**
      * @brief  初始化时间戳
      * @param  无
      * @retval 无
      * [url=home.php?mod=space&uid=536309]@NOTE[/url]   使用延时函数前,必须调用本函数
      */
    HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    {
        /* 使能DWT外设 */
        DEM_CR |= (uint32_t)DEM_CR_TRCENA;               

        /* DWT CYCCNT寄存器计数清0 */
        DWT_CYCCNT = (uint32_t)0u;

        /* 使能Cortex-M DWT CYCCNT寄存器 */
        DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;

        return HAL_OK;
    }

    /**
      * @brief  读取当前时间戳
      * @param  无
      * @retval 当前时间戳,即DWT_CYCCNT寄存器的值
      */
    uint32_t CPU_TS_TmrRd(void)
    {
      return ((uint32_t)DWT_CYCCNT);
    }

    /**
      * @brief  读取当前时间戳
      * @param  无
      * @retval 当前时间戳,即DWT_CYCCNT寄存器的值
      */
    uint32_t HAL_GetTick(void)
    {
      return ((uint32_t)DWT_CYCCNT/SysClockFreq*1000);
    }

    /**
      * @brief  采用CPU的内部计数实现精确延时,32位计数器
      * @param  us : 延迟长度,单位1 us
      * @retval 无
      * [url=home.php?mod=space&uid=536309]@NOTE[/url]   使用本函数前必须先调用CPU_TS_TmrInit函数使能计数器,
                或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
                最大延时值为8秒,即8*1000*1000
      */
    void CPU_TS_Tmr_Delay_US(uint32_t us)
    {
      uint32_t ticks;
      uint32_t told,tnow,tcnt=0;

      /* 在函数内部初始化时间戳寄存器, */
    #if (CPU_TS_INIT_IN_DELAY_FUNCTION)
      /* 初始化时间戳并清零 */
      HAL_InitTick(5);
    #endif

      ticks = us * (GET_CPU_ClkFreq() / 1000000);  /* 需要的节拍数 */
      tcnt = 0;
      told = (uint32_t)CPU_TS_TmrRd();         /* 刚进入时的计数器值 */

      while(1)
      {
        tnow = (uint32_t)CPU_TS_TmrRd();
        if(tnow != told)
        {
            /* 32位计数器是递增计数器 */
          if(tnow > told)
          {
            tcnt += tnow - told;
          }
          /* 重新装载 */
          else
          {
            tcnt += UINT32_MAX - told + tnow;
          }

          told = tnow;

          /*时间超过/等于要延迟的时间,则退出 */
          if(tcnt >= ticks)break;
        }
      }
    }

    /*********************************************END OF FILE**********************/
    #ifndef __CORE_DELAY_H
    #define __CORE_DELAY_H

    #include "stm32h7xx.h"

    /* 获取内核时钟频率 */
    #define GET_CPU_ClkFreq()       HAL_RCC_GetSysClockFreq()
    #define SysClockFreq            (218000000)
    /* 为方便使用,在延时函数内部调用CPU_TS_TmrInit函数初始化时间戳寄存器,
       这样每次调用函数都会初始化一遍。
       把本宏值设置为0,然后在main函数刚运行时调用CPU_TS_TmrInit可避免每次都初始化 */  

    #define CPU_TS_INIT_IN_DELAY_FUNCTION   0  

    /*******************************************************************************
    * 函数声明
    ******************************************************************************/
    uint32_t CPU_TS_TmrRd(void);
    HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority);

    //使用以下函数前必须先调用CPU_TS_TmrInit函数使能计数器,或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
    //最大延时值为8秒
    void CPU_TS_Tmr_Delay_US(uint32_t us);
    #define HAL_Delay(ms)     CPU_TS_Tmr_Delay_US(ms*1000)
    #define CPU_TS_Tmr_Delay_S(s)       CPU_TS_Tmr_Delay_MS(s*1000)

    #endif /* __CORE_DELAY_H */

上面代码的核心是:采用Cortex-M3/4内核中的跟踪组件DWT的时钟周期计数CYCCNT实现

参考链接

http://www.firebbs.cn/forum.php?mod=viewthread&tid=19059&fromuid=1

https://blog.csdn.net/linux_liulu/article/details/44998581

原文地址:https://www.cnblogs.com/libra13179/p/10691578.html

时间: 2024-10-05 01:58:08

【转】STM32 不占用定时器(包括SysTick)实现精确延时(巧用DWT)的相关文章

使用系统定时器SysTick实现精确延时微秒和毫秒函数

SysTick定时器简介 SysTick定时器是存在于系统内核的一个滴答定时器,只要是ARM Cortex-M0/M3/M4/M7内核的MCU都包含这个定时器,它是一个24位的递减定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数.使用内核的SysTick定时器来实现延时,可以不占用系统定时器,由于和MCU外设无关,所以代码的移植,在不同厂家的Cortex-M内核MCU之间,可以很方便的实现.而东芝的这款TT_M3HQ开发板使用的TMPM3HQFDFG芯片,正

STM32学习笔记9(SysTick滴答时钟)

我不得不说意法半导体确实有点风骚!甚至有点变态.我对ST文档 STM32F10XXX参考手册的编辑水平真是不敢恭维.手册中好多说明都是含糊不清,甚至将好多对初学者来说很重要的地方都一笔带过,让人着实摸不着头脑.比如前面我说过的关于NVIC嵌套向量中断控制器的介绍,这部分我认为是非常重要的,但当你看完他这部分介绍,你根本不会设置中断服务程序,他有哪些寄存器都不知道,更别说去设置了,NVIC的详细介绍是在Cotex-M3中有详细的介绍,不多说.今天我们说的是systick定时器. systick定时

[stm32] STM32的通用定时器TIMx系统了解

通用定时器(TIMx) 一.TIMx简介 二.TIMx主要功能 三.TIMx功能描述 3.1 时基单元 3.2 计数器模式 3.3 时钟选择 3.4 捕获/比较通道 3.5 输入捕获模式 3.6 PWM输入模式 3.7 强置输出模式 3.8 输出比较模式 3.9 PWM 模式 3.10 单脉冲模式 四.简单例子理解TIMx 4.1 使得PB5-TIM3通道2产生频率为12.5Hz的方波,该方波控制LED1的闪烁 4.2 周期控制通用定时器3的2通道,实现1KHz的不同占空比波形,控制LED实现呼

stm32之通用定时器TIM

STM32系列的CPU,有多达8个定时器: 1.其中TMI1和TIM8是能够产生三对PWM互补输出的高级定时器,常用于三相电机的驱动:它们的时钟有APB2的输出产生: 2.其它6个为普通定时器,时钟由APB1的输出产生: 定时器的作用: 1.定时功能 2.计数功能 3.输入捕获 4.匹配输出 5.PWM脉冲波输出 概述: 通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成.它适用于多种场合,包括测量输入信号的脉冲长度(输入采集)或者产生输出波形(输出比较和PWM). 定时器是完全独

STM32 Timer (2) 定时器中断代码框架

3. 代码框架 3.1 分频系数的计算 x1/x2 /N(预分频) APB1时钟------->F(CK_PSC)--------------->CK_CNT 如果APB1的分频系数为1, CK_INT的倍频系数就是x1 如果APB1的分频系数不为1, CK_INT的倍频系数就是x2   STM32F1(MHz) STM32F4(MHz) STM32F7(MHz) SYSCLK 72 168 216 AHB 72 168 216 APB1 (AHB/4) 18 42 54 CK_INT(x1/

STM32 Timer (1) 定时器分类和框图

1.定时器简单分类 1. 高级控制定时器 TIM1 TIM8 2. 通用定时器 TIM2 TIM3 TIM4 TIM5 3. 基本定时器 TIM6 TIM7 定时器: 1. 16/32位 向上 向下 上下 计数模式, 自动重装载计数器 TIMx_CNT 2. 16位可编程(实时修改) 预分频器(TIMx_PSC) 3. 4个独立通道(TIMx_CH1 ~ 4) 输入捕获 输出比较 PWM生成 单脉冲模式 4. 可以和外部定时器集联 2. 定时器结构图 定时器结构框图分为5部分 时钟产生部分 定时

STM32的精确延时

/*---------------------------------------------------------- 文件名称:systick.c 文件描述:sysTick 系统滴答时钟1us中断函数库,中断时间可自由配置 备注:程序默认使用72M时钟,无分频 -----------------------------------------------------------------*/ #include "delay.h" static __IO u32 TimingDel

Angular 定时器$timeout和$interval,延时调用

项目中有用到定时器定时刷新页面的数据,在网上查看了一些资料,整理了一下,备忘. $timeout 用法如下:$timeout(fn,[delay],[invokeApply]); fn:一个将被延迟执行的函数. delay:延迟的时间(毫秒). invokeApply:如果设置为false,则跳过脏值检测,否则将调用$apply. 方法:cancel(promise); promise:$timeout函数的返回值. 具体使用:在项目中用到的其中一处是键入字符然后自动发送请求查询,如果每键入一个

51定时器延时函数(精确延时)

1 bit f,m; 2 3 void T0_service(void) interrupt 1 4 { 5 TH0=(65536-5000)>>8; 6 TL0=(unsigned char)(65536-5000); 7 Count5ms=Count5ms+1; 8 m=~m; 9 if (Count5ms==100) 10 { 11 Count5ms=0; 12 f=~f; 13 } 14 } 15 16 void delay1s(void) 17 { 18 while(f); 19 w