引言

在嵌入式开发中,精确延时是基础需求。STM32F4 系列默认使用 SysTick 作为系统节拍,若同时用于微秒级延时,不仅会干扰系统调度,还可能因中断优先级问题导致延时不准。DWT(Data Watchpoint and Trace)单元是 Cortex-M4 内核自带的调试组件,其中的 CYCCNT 计数器以内核时钟频率递增,可轻松实现微秒级延时,且与 SysTick 完全独立,互不干扰。

DWT 原理简介

DWT 是 Cortex-M 内核的调试与跟踪单元,包含多个计数器,其中 CYCCNT 是一个 32 位自由运行计数器,每个内核时钟周期加 1。当内核时钟为 168MHz(STM32F4 最高主频)时,CYCCNT 每秒计数 168,000,000 次,即每微秒约 168 次。通过读取 CYCCNT 的差值,即可精确计算耗时。

关键寄存器:

  • DWT_CTRL(地址 0xE0001000):控制 DWT 功能,其中 bit0(CYCCNTENA)使能 CYCCNT 计数。
  • DWT_CYCCNT(地址 0xE0001004):当前计数值。
  • CoreDebug_DEMCR(地址 0xE000EDFC):bit24(TRCENA)必须置 1,以允许访问 DWT。

配置步骤

  1. 使能 DWT 访问:设置 CoreDebug_DEMCR 的 TRCENA 位。
  2. 复位 CYCCNT:将 DWT_CYCCNT 清零。
  3. 使能 CYCCNT:设置 DWT_CTRL 的 CYCCNTENA 位。
  4. 编写延时函数:读取当前计数值,计算目标差值,循环等待。

完整代码示例

以下代码适用于 STM32F4 系列,主频 168MHz。若主频不同,请调整 SYSTEM_CLOCK 宏。

#include "stm32f4xx.h"

#define SYSTEM_CLOCK 168000000UL  // 根据实际主频修改

// 初始化 DWT 延时功能
void DWT_Delay_Init(void)
{
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;  // 使能 DWT 访问
    DWT->CYCCNT = 0;                                 // 清零计数器
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;             // 使能 CYCCNT
}

// 微秒级延时,参数 us 为微秒数
void DWT_Delay_us(uint32_t us)
{
    uint32_t start = DWT->CYCCNT;
    uint32_t ticks = us * (SYSTEM_CLOCK / 1000000UL);  // 计算所需时钟周期数
    while ((DWT->CYCCNT - start) < ticks);            // 等待达到目标
}

// 毫秒级延时,基于微秒实现
void DWT_Delay_ms(uint32_t ms)
{
    for (uint32_t i = 0; i < ms; i++)
    {
        DWT_Delay_us(1000);
    }
}

// 示例:主函数中初始化并测试
int main(void)
{
    DWT_Delay_Init();
    
    while (1)
    {
        GPIOA->ODR ^= (1 << 5);  // 翻转 PA5
        DWT_Delay_us(500);       // 延时 500us
    }
}

注意事项

  • 主频一致性SYSTEM_CLOCK 必须与实际内核时钟一致,否则延时误差较大。可通过 SystemCoreClock 变量获取实际主频,但需注意该变量在启动文件中初始化。
  • 计数器溢出:CYCCNT 是 32 位,在 168MHz 下约 25.6 秒溢出一次。对于微秒级延时,差值计算(DWT->CYCCNT - start)利用无符号整数回绕特性,可正确处理溢出,但延时时间不能超过溢出周期。
  • 中断影响:DWT 延时是忙等待,期间不会响应中断(除非中断嵌套),若需在延时中处理中断,请考虑其他方案。
  • 调试器干扰:使用调试器时,DWT 可能被调试工具占用,建议在发布代码前确认 DWT 未被其他功能使用。
  • 低功耗模式:在低功耗模式下,内核时钟可能停止,DWT 计数也会暂停,因此不适用于低功耗唤醒后的精确延时。

总结

DWT 延时方法利用内核调试单元,避免了 SysTick 冲突,实现简单且精度高,特别适合需要精确时序控制的场景,如传感器读取、通信协议时序等。只需几行代码即可集成,是嵌入式开发者的实用技巧。希望本文能帮助你解决延时难题,提升开发效率。