STM32内核DWT精确延时详解

1.概述

在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使能

2.代码

c 复制代码
#define  DWT_CR      *(__IO uint32_t *)0xE0001000
#define  DWT_CYCCNT  *(__IO uint32_t *)0xE0001004
#define  DEM_CR      *(__IO uint32_t *)0xE000EDFC

#define  DEM_CR_TRCENA                   (1 << 24)
#define  DWT_CR_CYCCNTENA                (1 <<  0)

/**
  * @brief  初始化时间戳
  * @param  无
  * @retval 无
  * @note   使用延时函数前,必须调用本函数
  */
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);
}
c 复制代码
/* 获取内核时钟频率 */
#define GET_CPU_ClkFreq()       HAL_RCC_GetSysClockFreq()
#define CPU_MHZ                 168
#define SysClockFreq            (CPU_MHZ*1000000)

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

DWT_CYCCNT为内核的计数次数,SysClockFreq为内核时钟频率。

1/SysClockFreq为内核每运行一次耗费的时间,单位为秒。

(1/SysClockFreq) * 1000为将内核每运行一次耗费的时间单位由秒转换为ms。

DWT_CYCCNT / SysClockFreq * 1000即将内核的运行的次数转换为ms值

c 复制代码
/**
  * @brief  采用CPU的内部计数实现精确延时,32位计数器
  * @param  us : 延迟长度,单位1 us
  * @retval 无
  * @note   使用本函数前必须先调用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;
    }  
  }
}

ticks = us * (GET_CPU_ClkFreq() / 1000000)的推导过程如下:

GET_CPU_ClkFreq()为内核的时钟频率。

在本程序中CPU的内核频率为168000000,

计算一个时钟运行的秒:(1/168000000)秒

计算一个时钟运行的秒数转换为us:(1/168000000)*1000000=1000000/168000000us

CPU_TS_Tmr_Delay_US(uint32_t us)中函数参数为us单位

假设需要延时500us,那么需要多少个内核时钟

500us=1000000 / 168000000us * X;

X=500us * (168000000us / 1000000);

相关推荐
Qingniu01几秒前
【青牛科技】应用方案 | RTC实时时钟芯片D8563和D1302
科技·单片机·嵌入式硬件·实时音视频·安防·工控·储能
Mortal_hhh1 小时前
VScode的C/C++点击转到定义,不是跳转定义而是跳转声明怎么办?(内附详细做法)
ide·vscode·stm32·编辑器
深圳市青牛科技实业有限公司1 小时前
【青牛科技】应用方案|D2587A高压大电流DC-DC
人工智能·科技·单片机·嵌入式硬件·机器人·安防监控
Mr.谢尔比2 小时前
电赛入门之软件stm32keil+cubemx
stm32·单片机·嵌入式硬件·mcu·信息与通信·信号处理
LightningJie2 小时前
STM32中ARR(自动重装寄存器)为什么要减1
stm32·单片机·嵌入式硬件
鹿屿二向箔3 小时前
STM32外设之SPI的介绍
stm32
西瓜籽@3 小时前
STM32——毕设基于单片机的多功能节能窗控制系统
stm32·单片机·课程设计
远翔调光芯片^138287988725 小时前
远翔升压恒流芯片FP7209X与FP7209M什么区别?做以下应用市场摄影补光灯、便携灯、智能家居(调光)市场、太阳能、车灯、洗墙灯、舞台灯必看!
科技·单片机·智能家居·能源
极客小张6 小时前
基于STM32的智能充电桩:集成RTOS、MQTT与SQLite的先进管理系统设计思路
stm32·单片机·嵌入式硬件·mqtt·sqlite·毕业设计·智能充电桩
m0_739312879 小时前
【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)
stm32·单片机·嵌入式硬件