# 利用 DWT 计数器实现微秒级精确延时 在嵌入式开发中,精确延时是基础需求。SysTick 是最常用的延时工具,但在 RTOS 环境下,SysTick 被内核占用;或者当需要更高精度(如微秒级)时,SysTick 的配置可能不够灵活。此时,ARM Cortex-M 内核自带的 DWT 单元提供了一个优雅的解决方案。 ## 1. DWT 计数器原理 DWT (Data Watchpoint and Trace) 是 Cortex-M3/M4/M7 内核中的一个调试组件,其中包含一个 32 位的自由运行计数器(CYCCNT)。该计数器每个内核时钟周期递增一次,因此可以用于精确测量时间。 关键寄存器: - `DWT_CTRL` (0xE0001000): 控制寄存器,bit0 使能 CYCCNT,bit1 使能 CYCCNT 中断(通常不用)。 - `DWT_CYCCNT` (0xE0001004): 当前计数值,32 位,溢出后回绕。 - `CoreDebug_DEMCR` (0xE000EDFC): 调试控制寄存器,bit24 (TRCENA) 必须置 1,否则 DWT 无法访问。 原理:通过读取 CYCCNT 的差值,乘以内核时钟周期,即可得到经过的时间。由于是硬件计数器,精度可达 1 个时钟周期,通常为纳秒级。 ## 2. 为什么替代 SysTick? - **SysTick 被 RTOS 占用**:FreeRTOS 等系统使用 SysTick 作为时基,直接调用会导致冲突。 - **精度限制**:SysTick 通常配置为 1ms 中断,若用于微秒延时需频繁重载,开销大。 - **灵活性**:DWT 不依赖外设,只需内核支持,且代码可移植到所有 Cortex-M3+ 芯片。 ## 3. 配置步骤 1. **使能 TRCENA**:设置 `CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk`。 2. **使能 CYCCNT**:设置 `DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk`。 3. **清零计数器**(可选):`DWT->CYCCNT = 0`。 4. **读取计数值**:`uint32_t count = DWT->CYCCNT`。 注意:在 Cortex-M0/M0+ 上,DWT 可能不可用(部分芯片有),需检查芯片手册。 ## 4. 完整代码示例 以下代码基于 STM32F4 (Cortex-M4),可直接移植到其他 Cortex-M3/M4/M7 芯片。 ```c #include "stm32f4xx.h" // 初始化 DWT 计数器 void DWT_Init(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 使能 DWT 访问 DWT->CYCCNT = 0; // 清零计数器 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 使能 CYCCNT } // 获取当前计数值 static inline uint32_t DWT_GetCount(void) { return DWT->CYCCNT; } // 微秒延时函数 void delay_us(uint32_t us) { uint32_t start = DWT_GetCount(); uint32_t ticks = us * (SystemCoreClock / 1000000); // 计算所需时钟周期数 while ((DWT_GetCount() - start) < ticks); // 等待差值达到目标 } // 毫秒延时(基于微秒延时) void delay_ms(uint32_t ms) { for (uint32_t i = 0; i < ms; i++) { delay_us(1000); } } // 示例:主函数中初始化并使用 int main(void) { SystemInit(); DWT_Init(); while (1) { GPIO_ToggleBits(GPIOA, GPIO_Pin_5); // 翻转 LED delay_us(500); // 精确延时 500us } } ``` **代码说明**: - `SystemCoreClock` 是系统时钟频率,需正确配置(如 168MHz)。 - 使用无符号减法处理溢出,保证正确性。 - `delay_us` 中 `ticks` 计算可能溢出,若 us 过大,可分段延时。 ## 5. 注意事项 - **确保 DWT 可用**:Cortex-M0/M0+ 部分芯片无 DWT,需查勘手册。 - **时钟频率**:`SystemCoreClock` 必须与实际一致,否则延时不准。 - **中断影响**:DWT 计数器不受中断影响,但若中断服务函数耗时过长,延时可能被拉长(因为 CPU 被抢占)。 - **溢出处理**:32 位计数器在 168MHz 下约 25.6 秒回绕一次,使用差值计算可避免问题。 - **功耗**:DWT 在调试模式下可能增加功耗,量产代码可考虑关闭。 ## 6. 扩展应用 - **性能分析**:测量函数执行时间,只需记录前后计数值。 - **脉冲生成**:结合 GPIO 翻转,生成精确 PWM 或时序信号。 - **与 RTOS 共存**:在 FreeRTOS 中,DWT 延时不会干扰调度器。 ## 7. 总结 DWT 计数器是 Cortex-M 内核的隐藏宝藏,利用它可实现微秒级精确延时,且不占用外设资源。通过简单的寄存器配置和差值计算,即可替代 SysTick 在特定场景下的不足。希望本文能帮助你提升嵌入式开发的效率和灵活性。