STM32实现毫秒级精确延时的方法

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

代码来源于野火

猜你喜欢

转载自blog.csdn.net/weixin_43522198/article/details/111304919