• 【转】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

  • 相关阅读:
    2019西湖论剑网络安全技能大赛(大学生组)部分WriteUp
    C++学习(四十)(C语言部分)之 学生管理系统设计
    C++学习(三十九)(C语言部分)之 游戏项目(2048游戏)
    C++学习(三十八)(C语言部分)之 排序(冒泡 选择 插入 快排)
    C++学习(三十七)(C语言部分)之 链式栈(推箱子实现)
    C++学习(三十六)(C语言部分)之 链表2
    C++学习(三十五)(C语言部分)之 单链表
    C++学习(三十四)(C语言部分)之 链表
    C++学习(三十三)(C语言部分)之 队列
    C++学习(三十二)(C语言部分)之 栈
  • 原文地址:https://www.cnblogs.com/libra13179/p/10691578.html
Copyright © 2020-2023  润新知