目录
一、基于内核DWT实现硬件延时
1. 软件延时
- 实现原理:通过while循环空转实现延时,在system_init函数中提高内核主频后,delay函数的延时时间会变短。
- 特点:
- 精度差,受内核主频和while循环指令周期数影响
- 主频改变时需要重新调校,过程困难
- 示例代码:static void Delay(uint32_t count){while(count--);}
2. 硬件延时方案

- 实现方式:
- 使用片上定时器(Timer)
- 使用内核滴答定时器(systick)
- systick实现原理:
- 中断服务函数SysTick_Handler每1ms触发一次
- 全局变量uwTick每次中断递增1
- 延时函数通过比较当前uwTick与起始值的差值实现延时
- 缺点:需要占用定时器和中断资源,对CPU资源有浪费
3. Cortex-M4内核架构-DWT
- DWT模块:
- 数据跟踪监视点单元,位于内核中
- 仅Cortex-M3及以上内核支持,M0/M1不支持
- 与systick同属内核调试子系统
4. DWT硬件延时方案
1)DWT寄存器CYCCNT介绍

- 特性:
- 32位向上计数器,记录内核时钟运行次数
- 120MHz主频时精度为:1/120M=8.3ns
- 溢出后自动从0重新开始计数
- 最大延时时间:
- 120MHz时最大延时:2^32∗1/120M=36s
- 满足单片机程序需求(通常微秒到毫秒级)
2)DWT延时功能涉及的寄存器
- 关键寄存器:
- DEMCR:开启DWT功能
- DWT_CTRL:开启CYCCNT计数
- DWT_CYCCNT:获取系统时钟计数值
3)使能DWT的方法:

- 操作步骤:
- 向DEMCR寄存器的第24位(TRCENA)写1
- 寄存器定义位于core_cm4.h头文件
4)使能计数器的方法

- 操作步骤:
- 向DWT_CTRL寄存器的第0位写1
- 计数器清零:DWT->CYCCNT = 0
5)延时初始化完整代码整理:
/**
****************************************************************************
*@brief DWT初始化配置
*@param
*@return
****************************************************************************
*/
void Delaylnit(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;
}
6)延时(微秒级)函数实现
/**
****************************************************************************
*@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);
}
- 实现原理:
- 获取当前CYCCNT值存入tickStart
- 将微秒数转换为时钟跳动次数:nUs *= (rcu_clock_freq_get(CK_AHB)/1000000)
- 循环等待直到(DWT->CYCCNT - tickStart) >= nUs
- 注意事项:
- 使用uint32_t类型保证减法运算正确性
- 最大延时时间限制为36秒(120MHz时)
7)延时(毫秒级)函数实现
/**
****************************************************************************
*@brief 毫秒级延时函数
*@param nMs,延时时间n毫秒
*@return
****************************************************************************
*/
void DelayNms(uint32_t nMs)
{
void DelayNms(uint32_t nMs)
for (uint32_t i= 0;i<nMs; i++)
{
DelayNus(1000);
}
}
- 实现方式:
- 循环调用DelayNus(1000)实现毫秒延时
8)代码实现与头文件包含
- 必要头文件:
- stdint.h:标准整数类型定义
- gd32f30x.h:GD32库函数和寄存器定义
- core_cm4.h:内核相关寄存器定义
9)delay.h头文件内容
- 函数声明:
- 延时初始化:void DelayInit(void)
- 微秒延时:void DelayNus(uint32_t nUs)
- 毫秒延时:void DelayNms(uint32_t nMs)
- 条件编译:使用宏定义防止重复包含
二、应用案例
1)例题:LED延时闪烁代码实现
main.c:
#include <stdint.h>
#include "gd32f30x.h"
#include "delay.h"
int main(void)
{
/*使能GPIO的时钟*/
rcu_periph_clock_enable(RCU_GPIOA);
/*配置为推挽输出模式*/
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_8);
while (1)
{
/*配置为输出高电平*/
gpio_bit_set(GPIOA, GPIO_PIN_8);
DelayNms(1000);
/*配置为输出低电平*/
gpio_bit_reset(GPIOA, GPIO_PIN_8);
DelayNms(1000);
}
}
delay.c
#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);
}
}
- 代码目录结构:

- 调试注意事项:
- 下载后可能需要手动复位开发板
- 在Keil魔术棒设置中勾选"Reset and Run"选项
- 硬件延时精度验证:通过秒表实测5秒延时误差很小
- 方案优势:
- 不占用定时器资源
- 不需要中断处理
- 系统资源占用少
- 延时精度高
三、知识小结
| 知识点 | 核心内容 | 关键实现细节 | 技术对比 |
| 硬件延时方法 | 利用单片机内置计数功能硬件实现精确延时 | 使用DWT模块的32位CYCCNT计数器,精度达8.3纳秒(120MHz主频) | vs软件延时:不受主频变化影响,无需循环调优 |
| DWT模块原理 | Cortex-M3+内核专用数据跟踪监视单元 | 需配置DEMCR(24位)、DWT_CTRL(0位)寄存器,CYCCNT自动累加时钟周期 | vs SysTick:无需中断,资源占用更少 |
| 微秒级延时实现 | delay_us()函数动态计算时钟周期数 | 通过RCU_Clock_Freq_Get()获取实时主频,当前计数值-起始值≥目标周期数时退出 | 最大延时36秒(120MHz时2³²/120M) |
| 毫秒级延时封装 | 循环调用delay_us(1000)组合实现 | 在业务逻辑中限制使用场景(建议<1ms) | 长延时替代方案:需配合任务调度机制 |
| 寄存器操作关键 | 内核寄存器定义在core_cm4.h | 三步使能流程: 1. DEMCR|=1<<24; 2. DWT_CTRL|=1<<0; 3. CYCCNT=0 | M0/M1内核限制:无DWT功能 |
| 精度验证方法 | 实际测试5秒延时误差<1% | 需注意: - 下载后需手动复位; - CYCCNT溢出自动归零 | 主频适应性:代码通过动态获取主频保持通用性 |
1134

被折叠的 条评论
为什么被折叠?



