首页 > 其他分享 >Cortex-M3及以上32单片机使用内核DWT实现微秒级延时

Cortex-M3及以上32单片机使用内核DWT实现微秒级延时

时间:2024-10-20 08:50:55浏览次数:9  
标签:DWT 32 单片机 CYCCNT 内核 延时 寄存器 时钟

目录

一、什么是DWT

二、为什么要使用DWT实现延时

2.1 延时方法

2.2上述两种方案的缺点

三、如何实现DWT延时

 3.1 DEMCR寄存器

3.2 DWT_CTRL寄存器与DWT_CYCCNT寄存器

四、 代码实现

五、验证延时函数


一、什么是DWT

        在Cortex-M3及以上的内核中有一个外设叫DWT(Data Watchpoint and Trace数据监视点和跟踪单元),是用于系统调试及跟踪。DWT有一个32位的寄存器叫CYCCNT, 它是一个向上的计数器,记录的是内核时钟运行的次数,内核时钟跳动一次,该计数器就加1,精度非常高, 例如GD32F303ZET6内核时钟最大可达120MHz,那精度就是1/120M = 8.3ns。

        解释:120MHz表示内核时钟每秒跳动120*10^6次,那么跳动一次所用的时间就是1S/120MHz = 1*10^9ns / 120*10^6 = 8.3ns,知道了跳动次数就相当于知道了时间,这就是DWT延时的思想,而且单片机程序运行时间通常都是微秒级别的,所以DWT实现延时的精度非常高。

        如果内核时钟为120MHz,则直接使用DWT的CYCCNT寄存器(32位)延时的最大时间就是:

2^32 * (1S/120Mhz) = 36S。最大可以计2^32次内核时钟跳动次数,每次跳动耗时1/120MHz。

图1.1 Cortex-M4内核架构

二、为什么要使用DWT实现延时

2.1 延时方法

我了解到的延时方法有软件延时和systick滴答定时器延时

①软件延时

eg.static void Delay(uint32_t count)

{

        while (count--);

}

②systick滴答定时器延时

这是目前大多数人采用的延时方案。具体实现可搜索文章查看。

2.2上述两种方案的缺点

①软件延时的缺点

延时精度差,受系统主频影响,调校困难。

eg.软件延时本质就是指令的执行时间。假如系统主频120MHz,则t = 1S/120Mhz.假如系统主频72MHz,则t = 1S/72Mhz.在不同的时钟下,一条代码的执行时间是不同的。

②systick滴答定时器的缺点

在非阻塞性程序当中,是不会出现大于几十ms的死等延时。通常延时时间就是us级别,如果使用定时个人感觉有点浪费。

三、如何实现DWT延时

        要实现DWT延时的功能,总共涉及三个内核寄存器:DEMCR, DWT的CTRL寄存器,DWT的CYCCNT寄存器。我会逐一说明为什么涉及这三个寄存器。百度网盘链接如下:https://pan.baidu.com/s/13xTim4urhfEHttxkBFLInQ?pwd=GD32


链接: https://pan.baidu.com/s/13xTim4urhfEHttxkBFLInQ?pwd=GD32 提取码: GD32

1,ARM Cortex-M3与Cortex-M4权威指南,之后简称权威手册。
2,Arm Cortex-M4 Processor Technical Reference Manual Revision r0p1,简称技术手册。
3,Arm®v7M Architecture Reference Manual,简称架构手册。

 3.1 DEMCR寄存器

        配置DEMCR寄存器用于开启DWT功能。在权威手册321页的表14.5中写了:

图3.1.1

3.2 DWT_CTRL寄存器与DWT_CYCCNT寄存器

        配置DWT_CTRL寄存器用于开启DWT_CYCCNT计数寄存器以及获取系统时钟计数值。在技术手册的第85页给出了DWT各种寄存器的基地址。

        在架构手册的第741页有对时钟周期计数寄存器DWT_CYCCNT的详细说明。

图3.2.1

        我翻译一下红色方框中的内容:CYCCNT是一个32位,向上计数的寄存器。当DWT_CTRL寄存器的第0位CYCNTEENA为1并且DEMCR的第24位TRCENA为1时,CYCCNT寄存器被使能,每来一个内核时钟向上计数一次,溢出则被清零。

        下面是架构手册中的一些截图:

图3.2.2
图3.2.3

四、 代码实现

        下面给出基于Keil5,GD32F303系列单片机的代码实现。

#include "gd32f30x.h"                   // Device header
#include <stdint.h>

/**
***********************************************************
* @brief	DWT延时驱动初始化
* @param
* @return 
***********************************************************
*/
void DelayInit(void)
{
    /*TRCENA位清零*/
	CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
	/*TRCENA位置一*/
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;

    /*失能CYCCNT*/
    DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
    /*使能CYCCNT*/
	DWT->CTRL |=  DWT_CTRL_CYCCNTENA_Msk;

    /*计数器清零*/
	DWT->CYCCNT = 0;
}

/**
***********************************************************
* @brief	微秒级延时函数
* @param	nUs,延时时间(us)
* @return 
***********************************************************
*/
void DelayNus(uint32_t nUs)
{
	/*保存初始时钟跳动次数*/
	uint32_t tickStart = DWT->CYCCNT;

	/* 转换为nUs对应的时钟跳动次数*/
	uint32_t nusToTick;
	nusToTick = nUs * ( rcu_clock_freq_get(CK_AHB)/1000000 );

	/*while延时*/
	while ( (DWT->CYCCNT - tickStart) < nusToTick);
}

/**
***********************************************************
* @brief	毫秒级延时函数
* @param	nMs,延时时间(ms)
* @return 
***********************************************************
*/
void DelayNms(uint32_t nMs)
{
	for (uint32_t i = 0; i < nMs; i++)
	{
		DelayNus(1000);
	}
}

        有的朋友可能会好奇DelayNus函数中的这条语句:while ( (DWT->CYCCNT - tickStart) < nusToTick);

        会觉得这条语句存在这样一个bug:我们先假设CYCCNT是一个8位的寄存器,并且nUs的类型改为uint_8,这样方便说明。因为2^8 - 1 = 255,数字很直观。

        那么问题是什么呢?假如tickStart保存了254次跳动次数,nusToTick由时间转换得到的跳动次数是247,DWT_CYCCNT每跳一次都会加1,上限255。

255 - 254 = 1 < 247,while(1)执行;

255++溢出为0, 0 - 254 = ?

        实际上不会出错的,朋友们可以自己算一下,无符号uint8_t类型,

        0000 0000

--      1111 1110

=      0000 0010 == 2

五、验证延时函数

        用示波器可以验证延时是否准确。具体操作大家可以百度。我这就不多写了。我主要在这说一个我犯得很傻的错误。

        

int main(void)
{
	DrvInit();
	AppInit();
	
	while(1)
	{
		LedOn(LED0);
		DelayNms(1000);
		LedOff(LED0);
//		DelayNms(1000);
	}
}

我最开始是在主循环中用这个代码去看闪烁现象的。结果出现的情况是LED灯一直亮它不灭,我把代码改了一下。

int main(void)
{
	DrvInit();
	AppInit();
	
	while(1)
	{
		LedOff(LED0);
		DelayNms(1000);
		LedOn(LED0);
//		DelayNms(1000);
	}
}

        结果LED灯一直灭它不亮。 我以为是DWT配错了,它卡在delay里面的while循环出不来了。我在最后一个语句那打断点,结果函数可以执行到断点出,我就纳闷了。我又去看LED的函数是否右侧,单独测试发现每问题,可以正常亮灭。

        后来我终于发现,是逻辑错误。以上图代码叙述:led灭---Delay1000---亮-灭---1000---亮-灭-----。。。。。

        灭执行,延时1S,很快立刻亮再立刻灭, 这个立刻亮立刻灭太快了,灭又会持续1S,所以根本看不到亮的现象。最后加一个Delay就好了。

int main(void)
{
	DrvInit();
	AppInit();
	
	while(1)
	{
		LedOff(LED0);
		DelayNms(1000);
		LedOn(LED0);
		DelayNms(1000);
	}
}

以后有空更新STM32的DWT延时函数。

标签:DWT,32,单片机,CYCCNT,内核,延时,寄存器,时钟
From: https://blog.csdn.net/m0_73101636/article/details/143028118

相关文章

  • 2024-2025-1 20241328 《计算机基础与程序设计》第四周学习总结
    2024-2025-120241328《计算机基础与程序设计》第四周学习总结作业信息课程2024-2025-1-计算机基础与程序设计作业要求2024-2025-1计算机基础与程序设计第四周作业这个作业的目标门电路,组合电路,逻辑电路,冯诺依曼结构,CPU,内存,IO管理,嵌入式系统,并行结构,物理安全......
  • 51单片机PCB板布线走线布局及附铜封装
    51单片机PCB板布线走线布局及附铜封装前言大家好,本文章要给大家分享的是51单片机PCB板布线走线布局及附铜封装。在制作51单片机布线及附铜封装时,首先需要了解单片机的工作原理和电路设计基础。布线是电路设计中的关键步骤,它关系到电路的性能和可靠性。附铜封装则是为了提......
  • 51单片机中PCB板元器件的制作和封装
    51单片机中PCB元器件的制作和封装文章目录前言一、明确设计要求二、在桌面新建立一个新的文件夹三,建立和保存原理图库和PCB器件封装库四,制作元器件及封装 制作元器件 :元器件封装:总结前言大家好,今天要给大家分享的是 51单片机中PCB板的元器件的制作和封......
  • STM32 WINUSB1.0详细适配教程
    废话不多说,先上一个配置好的工程,急于完成适配的可先取走。需要了解原理或者适配后有问题的可继续阅读!通过网盘分享的文件:stm32h750_winusb.rarhttps://pan.baidu.com/s/1ENncscWVfQLHsOEhbe0sXQ?pwd=dr24 提取码:dr24说明:这个工程是基于STM32CUBEIDE1.9.0版本开发,配置了F......
  • P10532 [XJTUPC2024] 筛法 题解
    ~~打表可知答案为$n^2$~~一种几何证明,方法来自于讲评。考虑把$n^2$个整点放到坐标系中,满足$(x,y)(x\len,y\len)$。现在从原点向每个满足$(x,y)(x\perpy)$的点引出一条射线,显然每个点都会唯一的被一条射线覆盖到,因为$(\dfrac{x}{\gcd(x,y)},\dfrac{y}{\g......
  • FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
    ​《FFmpeg开发实战:从零基础到短视频上线》一书的“5.1.2 把音频流保存为PCM文件”介绍了如何把媒体文件中的音频流转存为原始的PCM音频,在样例代码的转存过程中,解码后的PCM数据未经任何加工处理,就直接保存到二进制文件。也就是说,原音频的采样频率是多少,PCM文件的采样频率也是多......
  • 【TFT彩屏移植】STM32F4移植1.8寸TFT彩屏简明教程
    目录一.移植说明二.移植1.例程    物理接口:    延时函数:     底层驱动文件:    GUI界面文件:    测试demo:2.移植至F4创建工程:        调试接口选择SW:        RCC中HSE选择外部晶振:        GPIO配......
  • AI编程Arduino单片机点亮WS2812灯带彩虹灯效
    引言随着物联网(IoT)和智能家居的迅速发展,LED灯带因其灵活性和多样的显示效果,广泛应用于装饰、广告、舞台等多个领域。WS2812作为一种智能可控的LED灯带,以其独立控制、丰富的色彩和高亮度,成为众多开发者的首选。而在控制WS2812灯带的过程中,编写高效、稳定的单片机代码至关重要。......
  • ctfshow(29->32)--RCE/命令执行漏洞
    Web29源代码:error_reporting(0);if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/flag/i",$c)){eval($c);}}else{highlight_file(__FILE__);}代码审计:error_reporting(0)关闭所有php报错。preg_matc......
  • stm32是世界上最简单的
    例一include"stm32f10x.h"include"generic.h"//1.打开时钟2.配置3.初始化例子:点gpioa的第一个灯voidpin_init(GPIO_TypeDef*GPIOx,,uint16_tGPIO_Pin,GPIOMode_TypeDefGPIO_Mode){//直接调用这个函数if(GPIOx=GPIOA)RCC_APB2PeriphClockCdm(RCC_APB2Perph_GPIO......