在单片机程序当中,延时函数是标配,简单的不精确延时可用软件实现,但如果想要获得精准的可重入微秒级延时,就必须得使用硬件解决。
使用硬件延时主要有3种方案:
1、systick定时器;
2、timerx定时器;
3、DWT系统时钟计数器;
本文所讲解的就是第3种,如何利用DWT硬件,实现硬件延时功能。
![](https://img-blog.csdnimg.cn/ef0d9e8b949d4f8fa9f01a249c8efe4f.png)
DWT是ARM Cortex-M3/M4/M7/M33处理器的标配模块之一,因此只要是基于此内核的单片机都可以实现DWT延时功能,比如常见的STM32F1xx、STM32F4xx、STM32F7xx、GD32F1xx等等等。
DWT模块有很多功能,当前我们只用到它的系统时钟计数功能,DWT模块的系列寄存器中有1个32bit的CYCCNT寄存器,它存储着当前PC采样周期计数值,每个系统时钟其值自增1。例如STM32F103ZET6系统频率为72MHz,那么每秒钟,DWT->CYCCNT会增加72000000。
开启DWT->CYCCNT计数步骤如下:
1、DEMCR的TRCENA置1;(使能DWT功能)
2、DWT->CYCCNT清零;
3、DWT->CTRL的CYCCNTENA置1;(使能DWT_CYCCNT计数器)
经过如上对DWT初始化后,DWT->CYCCNT的值便以系统时钟频率进行递增,溢出后值变成0后再次自动递增。
函数实现逻辑:
1、读取起始时刻CYCCNT值,记录到Start变量;
2、计算需要延时的系统时钟数,并加上Start变量值,记录到Stop变量;
3、一直读取CYCCNT的值,直到CYCCNT超过Stop变量值;
如此,需要分为2种情况:
1、Start <= Stop:
此时,如果读取到的CYCCNT实时值处在B区间,表示当前延时时间还未结束;如果读取到的CYCCNT实时值处在A或者C区间时,表示延时时间已到。
2、Start > Stop:
此时,如果读取到的CYCCNT实时值处在B区间,表示当前延时时间已到;如果读取到的CYCCNT实时值处在A或者C区间时,表示延时时间还未结束。
代码实现如下:
#include "stdint.h"
#include "core_cm4.h"
/*
* Return: void
* Parameters: void
* Description: DWT初始化
*/
void vDWTDelayInit(void)
{
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
/*
* Return: void
* Parameters: Time: 延时时间
* Description: 秒延时
*/
void vDWTDelayS(float fTime)
{
int32_t iTime = fTime;
while((iTime--) > 0)
{
vDWTDelayUs(1000000.0f);
}
fTime -= iTime;
vDWTDelayUs(fTime * 1000000.0f);
}
/*
* Return: void
* Parameters: Time: 延时时间
* Description: 毫秒延时
*/
void vDWTDelayMs(float fTime)
{
vDWTDelayUs(fTime * 1000.0f);
}
/*
* Return: void
* Parameters: Time: 延时时间
* Description: 微秒延时
*/
void vDWTDelayUs(float fTime)
{
volatile uint32_t uiTimeStop = 0u, uiTimeStart = 0u;
uiTimeStart = DWT->CYCCNT;
uiTimeStop = (uint32_t)((SystemCoreClock / 1000000u) * fTime) + uiTimeStart;
if(uiTimeStop >= uiTimeStart)
while((DWT->CYCCNT > uiTimeStart) && (DWT->CYCCNT < uiTimeStop));
else
while(!((DWT->CYCCNT > uiTimeStop) && (DWT->CYCCNT < uiTimeStart)));
}