STM32微秒级精确延时实现方案
该延时方案可在FreeRTOS启动前后均可正常使用,已在STM32F407和STM32F767平台完成验证。
对于不支持DWT模块的芯片,系统会自动回退到软件循环延时模式。FreeRTOS内核启动后,延时函数会自动切换为非阻塞式的任务延时调用。
使用说明:
DWT_Init函数中的解锁操作在F4系列上可选(默认未锁定),但F7系列必须执行此步骤才能正常访问DWT寄存器。
//根据是否使用FreeRTOS来决定是否包含相关头文件
#define ENABLE_FREERTOS 1
#ifdef ENABLE_FREERTOS
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h>
#endif
//DWT外设寄存器地址定义
#define DWT_CTRL_REG (*(volatile uint32_t *)0xE0001000)
#define DWT_CYCLE_CNT (*(volatile uint32_t *)0xE0001004)
#define DEMCR_REG (*(volatile uint32_t *)0xE000EDFC)
#define DEMCR_TRACE_EN (1 << 24)
//DWT访问控制寄存器
#define DWT_ACCESS_KEY (*(volatile uint32_t *)0xE0001FB0)
#define DWT_STATUS_REG (*(volatile uint32_t *)0xE0001FB4)
//DWT模块状态标志
static uint8_t dwt_initialized = 0;
void DWT_Init(void) {
DWT_ACCESS_KEY = 0xC5ACCE55;
__DSB();
__ISB();
//验证DWT模块可用性
if ((DWT_CTRL_REG & 1) == 0) {
DEMCR_REG |= DEMCR_TRACE_EN;
DWT_CYCLE_CNT = 0;
DWT_CTRL_REG |= 1;
}
dwt_initialized = (DWT_CTRL_REG & 1) ? 1 : 0;
if (!dwt_initialized) {
printf("DWT模块初始化失败");
}
}
void delay_us(uint32_t usec) {
if (dwt_initialized) {
uint32_t begin_tick = DWT_CYCLE_CNT;
uint32_t required_cycles = usec * (SystemCoreClock / 1000000);
while ((DWT_CYCLE_CNT - begin_tick) < required_cycles);
}
else {
basic_delay_us(1);
}
}
void delay_ms(uint32_t msec) {
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
//系统尚未启动RTOS,采用DWT延时
for (uint32_t i = 0; i < msec; i++) {
delay_us(1000);
}
}
else {
//RTOS已运行,切换至任务延时
vTaskDelay(pdMS_TO_TICKS(msec));
}
}
//阻塞式微秒延时(基于168MHz HCLK时钟)
void basic_delay_us(uint16_t usec) {
volatile uint32_t loops = usec * (168 / 4);
while (loops--);
}
main()函数中,基本初始化完成后调用:
DWT_Init();
printf("系统启动:%d,%d\n",API_TIM7_Count,SystemCoreClock);
delay_ms(200);
delay_ms(200);
printf("延时结束:%d\n",API_TIM7_Count);
F407平台实测数据:
[17:15:19.452]系统启动:1,168000000
[17:15:20.191]延时结束:403
数据说明:
API_TIM7_Count为TIM7定时器计数值,每1ms累加一次。上述测试结果表明,400ms延时期间TIM7中断触发406次,延时精度良好。