在 Cortex-M 系列 MCU 中,常见的内核级延时方式主要有SysTick 定时和DWT(CYCCNT)定时。两者虽然都能实现延时,但定位和适用场景完全不同。
一、基本概念
SysTick
Cortex-M 内核自带的 24 位定时器,主要用于系统节拍(Tick),通常通过中断实现延时和计时。DWT(CYCCNT)
Cortex-M 内核调试模块中的CPU 周期计数器,每个 CPU 时钟周期加 1,不依赖中断。
二、相同点
- 都是 Cortex-M 内核资源
- 都与 CPU 主频相关
- 都可用于延时
- 都不占用普通外设定时器(TIM)
三、主要区别
| 对比项 | SysTick | DWT |
|---|---|---|
| 工作方式 | 定时器 + 中断 | CPU 周期计数 |
| 是否中断 | 是 | 否 |
| 精度 | 毫秒级为主 | 微秒级、极高 |
| 抖动 | 受中断影响 | 几乎无 |
| 系统侵入性 | 高 | 低 |
| 低功耗支持 | 好 | 差 |
四、使用场景
适合 SysTick:
- RTOS 系统节拍
- 系统毫秒时基
- 任务调度
- 长时间延时
适合 DWT:
- 微秒级延时
- 驱动层延时
- 外设初始化时序
- Bit-bang 通信
五、工程选型结论
SysTick 是“系统的钟表”,用于调度和系统时基
DWT 是“CPU 的秒表”,用于高精度短时延时
在实际项目中通常组合使用:
SysTick → 系统节拍 / RTOS
DWT → 驱动层微秒延时
ps:第一次接触DWT延时的概念,该篇内容仅做简单介绍,由AI辅助整理
附代码(引用郭天祥团队):
#include <stdint.h> #include "gd32f30x.h" /** *********************************************************** * @brief DWT初始化配置 * @param * @return *********************************************************** */ void DelayInit(void) { /* 关闭 TRC */ CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; /* 打开 TRC */ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; /* 关闭计数功能 */ DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; /* 打开计数功能 */ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; /* 计数清零 */ DWT->CYCCNT = 0; } /** *********************************************************** * @brief 微秒级延时函数 * @param nUs,最大延时时间( 2^32 / 内核主频 ) * 10^6 us * @return *********************************************************** */ void DelayNus(uint32_t nUs) { uint32_t tickStart = DWT->CYCCNT; /* 转换为nUs对应的时钟跳动次数*/ nUs *= (rcu_clock_freq_get(CK_AHB) / 1000000); /* 延时等待 */ while ((DWT->CYCCNT - tickStart) < nUs); } /** *********************************************************** * @brief 毫秒级延时函数 * @param nMs,延时时间n毫秒 * @return *********************************************************** */ void DelayNms(uint32_t nMs) { for (uint32_t i = 0; i < nMs; i++) { DelayNus(1000); } }