文章目錄
STM32 HAL庫微秒延時函數的實現
天下苦STM32 HAL
庫微秒延時久已。不佔用其他定時器資源又不使用循環的方式就不能實現微秒延時函數了嗎?答案是否定的,我們還有方式實現,且還不止一種方法。詳情且看下文分解:
以下兩種延時方式來源:Arduino_Core_STM32
源碼delayMicroseconds(uint32_t us)
函數的實現。
利用SysTick再實現微秒延時函數
雖然SysTick
已經被配置爲1ms中斷一次的模式,但每個1ms之間SysTick
的當前值寄存器是一直在計數的(每計一個數的時間是1/SytemCoreClock
)我們便可以利用該機制實現微秒延時函數。
void delayMicroseconds(uint32_t us)
{
__IO uint32_t currentTicks = SysTick->VAL;
/* Number of ticks per millisecond */
const uint32_t tickPerMs = SysTick->LOAD + 1;
/* Number of ticks to count */
const uint32_t nbTicks = ((us - ((us > 0) ? 1 : 0)) * tickPerMs) / 1000;
/* Number of elapsed ticks */
uint32_t elapsedTicks = 0;
__IO uint32_t oldTicks = currentTicks;
do {
currentTicks = SysTick->VAL;
elapsedTicks += (oldTicks < currentTicks) ? tickPerMs + oldTicks - currentTicks :
oldTicks - currentTicks;
oldTicks = currentTicks;
} while (nbTicks > elapsedTicks);
}
以上函數可以直接複製到工程中使用,不需要額外的任何配置。
Note
雖然函數參數us
爲uint32_t
類型,但是延時數不能過大,原因自己分析。建議超過1ms的延時時間使用HAL_Delay()
。
利用DWT(數據觀測點)實現微秒延時函數
對於DWT大家可以搜索具體瞭解,這裏我也不是很瞭解,就直說實現方法好了。
dwt.h
文件
/**
******************************************************************************
* @file dwt.h
* @author Frederic Pillon
* @brief Header for dwt.c module
******************************************************************************
* @attention
*
* Copyright (c) 2019, STMicroelectronics
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef _DWT_H_
#define _DWT_H_
#include "stm32f4xx.h"
#include <stdbool.h>
#ifndef UNUSED
#define UNUSED(x) (void)x
#endif
#ifdef DWT_BASE
uint32_t dwt_init(void);
#ifdef __cplusplus
extern "C" {
#endif
//uint32_t dwt_init(void);
void dwt_access(bool ena);
static inline uint32_t dwt_max_sec(void)
{
return (UINT32_MAX / SystemCoreClock);
};
static inline uint32_t dwt_max_msec(void)
{
return (UINT32_MAX / (SystemCoreClock / 1000));
};
static inline uint32_t dwt_max_usec(void)
{
return (UINT32_MAX / (SystemCoreClock / 1000000));
};
static inline uint32_t dwt_getCycles(void)
{
return (DWT->CYCCNT);
};
#ifdef __cplusplus
}
#endif
#endif /* DWT_BASE */
#endif /* _DWT_H_ */
dwt.c
文件
/**
******************************************************************************
* @file dwt.c
* @author Frederic Pillon
* @brief Provide Data Watchpoint and Trace services
******************************************************************************
* @attention
*
* Copyright (c) 2019, STMicroelectronics
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
#include "dwt.h"
#ifdef DWT_BASE
#ifdef __cplusplus
extern "C" {
#endif
uint32_t dwt_init(void)
{
/* Enable use of DWT */
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
}
/* Unlock */
dwt_access(true);
/* Reset the clock cycle counter value */
DWT->CYCCNT = 0;
/* Enable clock cycle counter */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
/* 3 NO OPERATION instructions */
__asm volatile(" nop \n\t"
" nop \n\t"
" nop \n\t");
/* Check if clock cycle counter has started */
return (DWT->CYCCNT) ? 0 : 1;
}
void dwt_access(bool ena)
{
#if (__CORTEX_M == 0x07U)
/*
* Define DWT LSR mask which is (currentuly) not defined by the CMSIS.
* Same as ITM LSR one.
*/
#if !defined DWT_LSR_Present_Msk
#define DWT_LSR_Present_Msk ITM_LSR_Present_Msk
#endif
#if !defined DWT_LSR_Access_Msk
#define DWT_LSR_Access_Msk ITM_LSR_Access_Msk
#endif
uint32_t lsr = DWT->LSR;
if ((lsr & DWT_LSR_Present_Msk) != 0) {
if (ena) {
if ((lsr & DWT_LSR_Access_Msk) != 0) { //locked
DWT->LAR = 0xC5ACCE55;
}
} else {
if ((lsr & DWT_LSR_Access_Msk) == 0) { //unlocked
DWT->LAR = 0;
}
}
}
#else /* __CORTEX_M */
UNUSED(ena);
#endif /* __CORTEX_M */
}
#ifdef __cplusplus
}
#endif
#endif
delayMicroseconds()
函數
void delayMicroseconds(uint32_t us)
{
#if defined(DWT_BASE) && !defined(DWT_DELAY_DISABLED)
int32_t start = dwt_getCycles();
int32_t cycles = us * (SystemCoreClock / 1000000);
while ((int32_t)dwt_getCycles() - start < cycles);
#endif
}
Note:
在使用DWT實現的延時函數時,程序下載到單片機中不能直接運行,需要按一下復位鍵才能正常運行(使用keil-MDK環境,ST-Link下載)。
結論
對比上面兩種方式的延時效果:DWT實現的延時精度更高,SysTick的使用更加簡單,大家可針對自己的需求選擇。