用DWT周期计数器做us级精确定时,替代定时器资源的实战配置
1. 为什么需要DWT做us级定时?
在嵌入式项目中,硬件定时器(TIM)是稀缺资源。当多个外设需要PWM、输入捕获、编码器接口时,定时器往往捉襟见肘。而软件延时(如for循环)精度差、受编译器优化影响大。
Cortex-M内核自带DWT(Data Watchpoint and Trace) 单元,其中的CYCCNT寄存器是一个32位自由运行的周期计数器,每个CPU时钟周期加1。利用它可实现纳秒级分辨率的延时,且不占用任何外设定时器。
2. DWT周期计数器原理
-
CYCCNT在DWT使能后从0开始递增,溢出后自动回绕(32位)。 - 计数频率等于CPU主频(如STM32F103为72MHz,STM32F4为168MHz)。
- 通过读取前后两次
CYCCNT的差值,即可换算出经过的时间。 - 关键寄存器:
-
DWT->CTRL:使能CYCCNT(bit0)。 -
DWT->CYCCNT:当前周期计数值。 -
CoreDebug->DEMCR:使能DWT跟踪(bit24 TRCENA)。
-
3. 配置步骤
-
使能DWT跟踪:设置
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk。 -
清零并启动CYCCNT:
DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;。 -
获取CPU频率:通常使用
SystemCoreClock变量(需在系统初始化后更新)。 - 实现微秒延时函数:计算目标周期数 = 延时us × (SystemCoreClock / 1000000)。
- 处理溢出:使用无符号减法自动处理回绕。
4. 完整代码示例
#include "stm32f4xx.h" // 根据实际芯片修改
// 初始化DWT周期计数器
void DWT_Init(void)
{
// 使能DWT跟踪
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
// 清零周期计数器
DWT->CYCCNT = 0;
// 使能周期计数器
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
// 微秒级延时(阻塞式)
void DWT_DelayUs(uint32_t us)
{
uint32_t start = DWT->CYCCNT;
uint32_t ticks = us * (SystemCoreClock / 1000000U);
// 无符号减法自动处理溢出
while ((DWT->CYCCNT - start) < ticks) {
__NOP(); // 防止编译器优化掉空循环
}
}
// 毫秒级延时(基于us延时)
void DWT_DelayMs(uint32_t ms)
{
while (ms--) {
DWT_DelayUs(1000);
}
}
// 获取当前周期计数(用于非阻塞超时判断)
uint32_t DWT_GetCycles(void)
{
return DWT->CYCCNT;
}
// 判断是否超时(单位:us)
// 返回值:1-超时,0-未超时
uint8_t DWT_IsTimeout(uint32_t startCycles, uint32_t timeoutUs)
{
uint32_t ticks = timeoutUs * (SystemCoreClock / 1000000U);
return ((DWT->CYCCNT - startCycles) >= ticks) ? 1 : 0;
}
使用示例:
int main(void)
{
HAL_Init();
SystemClock_Config(); // 配置系统时钟,更新SystemCoreClock
DWT_Init();
while (1) {
DWT_DelayUs(500); // 精确500us延时
// 非阻塞超时检测示例
uint32_t start = DWT_GetCycles();
while (!DWT_IsTimeout(start, 1000)) {
// 等待1ms超时,期间可执行其他任务
}
}
}
5. 注意事项
-
SystemCoreClock必须准确:若使用HAL库,在
SystemClock_Config()后调用SystemCoreClockUpdate()确保变量值正确。 - 调试器影响:某些调试器会暂停CYCCNT计数,在线调试时延时可能变长,脱机运行正常。
-
中断干扰:
DWT_DelayUs是阻塞延时,若被高优先级中断打断,实际延时会略长。对时间敏感的场景建议关闭中断或使用非阻塞方式。 - 溢出处理:32位计数器在72MHz下约59.6秒溢出一次,无符号减法可正确处理单次溢出,但超长延时(>59秒)需额外处理。
- 低功耗模式:进入Sleep/Stop模式后CYCCNT可能停止,唤醒后需重新校准或避免依赖。
- 移植性:DWT是Cortex-M内核通用外设,但不同厂商的头文件定义可能略有差异,需包含正确的CMSIS头文件。
6. 总结
DWT周期计数器为嵌入式开发者提供了一种零硬件成本、高精度、不占用定时器的延时方案。通过本文的配置与代码,你可以轻松实现us级定时,并将宝贵的TIM资源留给PWM、编码器等关键功能。在电机控制、传感器时序、通信协议等对时间精度要求较高的场景中,DWT延时是值得掌握的实战技巧。
暂无评论,来抢沙发~