STM32之使用DWT外设编写延时函数

1.8k words

WT 是一个调试外设,它的核心功能之一是 CYCCNT(Cycle Count),这是一个32位递增计数器,用于记录 CPU 执行的时钟周期数量。我们可以通过对这个计数器的变化来实现高精度延时。

在使用 DWT 延时之前,需要完成以下操作:

  1. 使能 DWT
  2. 使能 CYCCNT
  3. 清零 CYCCNT
  4. 基于 CYCCNT 实现延时逻辑

源码如下:

  • dwt.delay.h文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __DWT_DELAY_H
#define __DWT_DELAY_H

#include "stm32g4xx.h"

/* 获取内核时钟频率 */
#define GET_CPU_ClkFreq() HAL_RCC_GetSysClockFreq()


uint32_t CPU_TS_TmrRd(void);
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority);

//最大延时值为8秒
void dwt_delay_us(uint32_t us);
#define HAL_Delay(ms) dwt_delay_us(ms*1000)
#define dwt_delay_s(s) dwt_delay_us(s*1000000)


#endif /* __DWT_DELAY_H */
  • dwt_delay.c文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "dwt_delay.h"


#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 读取当前时间戳,每1ms更新一次
* @param 无
* @retval ms时间戳
*/
uint32_t HAL_GetTick(void)
{
return ((uint32_t)DWT_CYCCNT/(GET_CPU_ClkFreq()*1000));
}


/**
* @brief 采用CPU的内部计数实现精确延时,32位计数器,最大延时时间8秒
* @param us : 延迟长度,单位1 us
* @retval 无
*/
void dwt_delay_us(uint32_t us)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;

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

while(1)
{
tnow = (uint32_t)DWT_CYCCNT;
if(tnow != told)
{
if(tnow > told)
{
tcnt += tnow - told;
}
else /* 重新装载 */
{
tcnt += UINT32_MAX - told + tnow;
}

told = tnow;

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