STM32实现毫秒级精确延时的方法
在进行STM32进行开发时,使用到延时函数的频率非常高,但HAL库只提供了微秒级的延时函数,最小延时时间是1ms,在某些应用场合下不能达到要求。下面这种方法可达到毫秒级的精确延时。需要新建源文件DELAY.c与DELAY.H,源码用的是SPL开发,但移植到HAL工程下也可以使用,直接调用即可。代码如下:
// DELAY.c
// Created by kkk on 2020/12/16.
//
/************************************************************
* @brief core_delay.c
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 使用内核寄存器精确延时
***********************************************************/
#include "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 使用延时函数前,必须调用本函数
*/
void CPU_TS_TmrInit(void)
{
/* 使能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;
}
/**
* @brief 读取当前时间戳
* @param 无
* @retval 当前时间戳,即DWT_CYCCNT寄存器的值
*/
uint32_t CPU_TS_TmrRd(void)
{
return ((uint32_t)DWT_CYCCNT);
}
///**
// * @brief 读取当前时间戳
// * @param 无
// * @retval 当前时间戳,即DWT_CYCCNT寄存器的值
// * 此处给HAL库替换HAL_GetTick函数,用于os
// */
//uint32_t HAL_GetTick(void)
//{
// return ((uint32_t)DWT_CYCCNT*1000/SysClockFreq);
//}
/**
* @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(__IO uint32_t us)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
/* 在函数内部初始化时间戳寄存器, */
#if (CPU_TS_INIT_IN_DELAY_FUNCTION)
/* 初始化时间戳并清零 */
CPU_TS_TmrInit();
#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;
}
}
}
// DALAY.h
// Created by kkk on 2020/12/16.
//
#ifndef STM32F103_KEY_DELAY_H
#define STM32F103_KEY_DELAY_H
#include "main.h"
#define USE_DWT_DELAY 1 /* 使用dwt内核精确延时 */
#if USE_DWT_DELAY
#define USE_TICK_DELAY 0 /* 不使用SysTick延时 */
#else
#define USE_TICK_DELAY 1 /* 使用SysTick延时 */
#endif
/*简单任务管理*/
#define TASK_ENABLE 0
#define NumOfTask 3
#if USE_DWT_DELAY
//#define Delay_ms(ms) CPU_TS_Tmr_Delay_MS(ms)
//#define Delay_us(us) CPU_TS_Tmr_Delay_US(us)
///* 最大延时 60s=2的32次方/72000000 */
//#define Delay_s(s) CPU_TS_Tmr_Delay_S(s)
/* 获取内核时钟频率 */
#define GET_CPU_ClkFreq() (SystemCoreClock)
#define SysClockFreq (SystemCoreClock)
/* 为方便使用,在延时函数内部调用CPU_TS_TmrInit函数初始化时间戳寄存器,
这样每次调用函数都会初始化一遍。
把本宏值设置为0,然后在main函数刚运行时调用CPU_TS_TmrInit可避免每次都初始化 */
#define CPU_TS_INIT_IN_DELAY_FUNCTION 1
/*******************************************************************************
* 函数声明
******************************************************************************/
uint32_t CPU_TS_TmrRd(void);
void CPU_TS_TmrInit(void);
//使用以下函数前必须先调用CPU_TS_TmrInit函数使能计数器,或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
//最大延时值为60秒
void CPU_TS_Tmr_Delay_US(uint32_t us);
#define CPU_TS_Tmr_Delay_MS(ms) CPU_TS_Tmr_Delay_US(ms*1000)
#define CPU_TS_Tmr_Delay_S(s) CPU_TS_Tmr_Delay_MS(s*1000)
#endif
#endif //STM32F103_KEY_DELAY_H
代码来源于野火