# 引言 在嵌入式开发中,精确延时是基础需求。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` 宏。 ```c #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 冲突,实现简单且精度高,特别适合需要精确时序控制的场景,如传感器读取、通信协议时序等。只需几行代码即可集成,是嵌入式开发者的实用技巧。希望本文能帮助你解决延时难题,提升开发效率。