|
好久好久没发技术贴,于是想水一贴。
搜了论坛,好像提及DWT模块的帖子不多,最近刚好用到,所以来吹吹水。
参考的转载原文:http://blog.csdn.net/linux_liulu/article/details/44998581
//-----------------------------------------------------------------------------------------------//
正文(其实是转载网上其他电工的博客):
DWT,全称是The Debug Watchpoint and Trace (DWT) unit,用于系统调试及跟踪,详细的介绍可以参考
ARM官方文档:ARMv7-M Architecture Reference Manual。本文将使它来实现一个系统的延时功能。
(所以,所有ARMv7-M架构,即Cortex-M系列的单片机,无论M0/M3/M4/M7都适用。另,只在M3和M4上使用过。)
1. 寄存器简单介绍
要实现延时的功能,总共涉及到三个寄存器:DEMCR 、DWT_CTRL、DWT_CYCCNT,分别用于开启DWT功能、开启CYCCNT及获得系
统时钟计数值。
DEMCR
其官方手册说明如下,这里我们只需要关注其第24位TRCENA。
该寄存器的TRCENA位置位,使能DWT功能。
DWT_CTRL寄存器
其包含很多功能,这里我们只开启其循环计数功能。
CYCCNT寄存器
该寄存器地址见上图,其描述如下:
当DWT的CYCCNTENA位置位后,该寄存器的值与系统周期计数值保持同步,我们可以用它的值来实现一个延时的功能。
(注意,当CYCCNT计数器溢满后,会复位为0x0,重新开始计数,不停循环)
2. 延时程序编写
//.c文件
- #include "DWT_Delay.h"
- /*
- * 添加注释
- * (1)DWT寄存器是ARMv7架构的芯片内部的一个通用模块
- * 所有的ARMv7架构的芯片都有该功能,所以此处定义为common功能
- * (2)DWT模块全称为"The Debug Watchpoint and Trace (DWT) unit",
- * 用于系统调试及跟踪,详细可以参考ARM公司的官方手册
- * (3)在调试模式下,该功能自动会使能,但正常模式下则不会使能,
- * 所以首先要使能该功能模块
- * (4)该模块的CYCCNT计数器是需要用到的功能,该计数器从模块使能后,
- * 会随着CPU的每次跳动,自加1次,溢满后复位重来
- */
- //0xE000EDFC DEMCR RW Debug Exception and Monitor Control Register.
- //使能DWT模块的功能位
- #define DEMCR ( *(unsigned int *)0xE000EDFC )
- #define TRCENA ( 0x01 << 24) // DEMCR的DWT使能位
-
- //0xE0001000 DWT_CTRL RW The Debug Watchpoint and Trace (DWT) unit
- //使能CYCCNT计数器开始计数
- #define DWT_CTRL ( *(unsigned int *)0xE0001000 )
- #define CYCCNTENA ( 0x01 << 0 ) // DWT的SYCCNT使能位
- //0xE0001004 DWT_CYCCNT RW Cycle Count register,
- //CYCCNT计数器的内部值(32位无符号)
- #define DWT_CYCCNT ( *(unsigned int *)0xE0001004) //显示或设置处理器的周期计数值
-
- //系统CPU的频率 Hz
- static unsigned int m_ulSysClk = 0;
- //计时器的定义
- typedef struct _Time_Counter_Struct_
- {
- unsigned int BeginTime; //开始时刻
- unsigned int EndTime; //结束时刻
- unsigned int DeltaTime; //计时器记录的差值
- unsigned int MaxVal; //计时器记录的最大差值
- unsigned int MinVal; //计时器记录的最小差值
- }TIME_COUNTER_STRUCT;
- TIME_COUNTER_STRUCT m_stTimeCounter[DWT_TIME_COUNTER_NUM] =
- {
- {0,0,0,0,1000000000},{0,0,0,0,1000000000},{0,0,0,0,1000000000},{0,0,0,0,1000000000},
- {0,0,0,0,1000000000},{0,0,0,0,1000000000},{0,0,0,0,1000000000},{0,0,0,0,1000000000},
- {0,0,0,0,1000000000},{0,0,0,0,1000000000}
- };
- /*
- 功 能: 初始化DWT模块功能
- 参 数: ulsysclk -- 当前CPU的时钟频率 单位:Hz
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_INIT(unsigned int ulsysclk)
- {
- DEMCR |= TRCENA;
- DWT_CTRL |= CYCCNTENA;
- m_ulSysClk = ulsysclk; //保存当前系统的时钟周期,eg. 72,000,000(72MHz).
- }
-
- /*
- 功 能: 精确的微妙级延时
- 参 数: nCount -- 需要延时的时间 单位:微妙(us)
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_DELAY_US(unsigned int ulTime)
- {
- unsigned int ultickstart, ultick_end, ultickdelay;
-
- //记录当前的CYCCNT计数值
- ultickstart = DWT_CYCCNT;
-
- //计算延时条件
- if(!m_ulSysClk )
- DWT_INIT(DEFAULT_CPU_SYSCLK_HZ);
-
- //将微秒数换算成滴答数
- ultickdelay = (ulTime*(m_ulSysClk/(1000*1000)));
-
- //目标计数值
- ultick_end = ultickstart + ultickdelay;
-
- if(ultick_end > ultickstart )
- {//正常不溢出情况下
- while(DWT_CYCCNT < ultick_end);
- }
- else
- {//计数溢出,复位翻转
- while(DWT_CYCCNT >= ultick_end);
- while(DWT_CYCCNT < ultick_end);
- }
- }
- /*
- 功 能: 计时器记录时间的动作
- 参 数: iNo -- 计时器的编号 范围从0到(DWT_TIME_COUNTER_NUM-1)
- flag -- 计时器的动作 DWT_TIME_COUNTER_BEGIN:计时开始
- DWT_TIME_COUNTER_END :计时结束
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_TIMECOUNTER(int iNo,int flag)
- {
- //范围不能超限
- if(iNo >= DWT_TIME_COUNTER_NUM)
- return ;
-
- //需要初始化DWT模块
- if (!m_ulSysClk)
- DWT_INIT(DEFAULT_CPU_SYSCLK_HZ);
-
- if(flag == DWT_TIME_COUNTER_BEGIN)
- { //计时器开始,记录该时刻计数
- m_stTimeCounter[iNo].BeginTime = DWT_CYCCNT;
- }
- else if(flag == DWT_TIME_COUNTER_END)
- { //计时器结束,记录该时刻计数,并计算差值
- m_stTimeCounter[iNo].EndTime = DWT_CYCCNT;
-
- m_stTimeCounter[iNo].DeltaTime = m_stTimeCounter[iNo].EndTime - m_stTimeCounter[iNo].BeginTime;
-
- if(m_stTimeCounter[iNo].DeltaTime > m_stTimeCounter[iNo].MaxVal)
- m_stTimeCounter[iNo].MaxVal = m_stTimeCounter[iNo].DeltaTime;
-
- if(m_stTimeCounter[iNo].DeltaTime < m_stTimeCounter[iNo].MinVal)
- m_stTimeCounter[iNo].MinVal = m_stTimeCounter[iNo].DeltaTime;
- }
- }
复制代码
//.h文件
- #ifndef _DWT_DELAY_H_
- #define _DWT_DELAY_H_
- //默认的系统CPU频率 100MHz
- #define DEFAULT_CPU_SYSCLK_HZ (100000000)
- //定义计时器个数
- #define DWT_TIME_COUNTER_NUM (10)
- //计时器开始标志
- #define DWT_TIME_COUNTER_BEGIN (0)
- //计时器结束标志
- #define DWT_TIME_COUNTER_END (1)
- #ifdef __cplusplus
- extern "C"{
- #endif
- /*
- 功 能: 初始化DWT模块功能
- 参 数: ulsysclk -- 当前CPU的时钟频率 单位:Hz
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_INIT(unsigned int ulsysclk);
-
- /*
- 功 能: 精确的微妙级延时
- 参 数: nCount -- 需要延时的时间 单位:微妙(us)
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_DELAY_US(unsigned int ulTime);
- /*
- 功 能: 计时器记录时间的动作
- 参 数: iNo -- 计时器的编号 范围从0到(DWT_TIME_COUNTER_NUM-1)
- flag -- 计时器的动作 DWT_TIME_COUNTER_BEGIN:计时开始
- DWT_TIME_COUNTER_END :计时结束
- 返 回 值: 无
- 注意 事 项:
- 所在文件名:
- */
- void DWT_TIMECOUNTER(int iNo,int flag);
- #ifdef __cplusplus
- }
- #endif
- #endif
复制代码
至此,简单、好用、精准又不占用CPU其它外设资源的延时函数就实现了。
(代码是重新整理过的,原文到这里结束)
3.应用
有时候我们需要在程序中实现精确的延时,但我看到很多电工都是用这种形式:
- void SimpleDelay(unsigned long ulCycle)
- {
- for (;ulCycle>0;ulCycle--)
- {}
- }
复制代码
这明显不现实,效果也奇差啊,怎么办?
系统定时中断!但如果我要时间短一些,微妙级别的,恐怕力有不逮。
也有人说,可以算。但未免麻烦。
定时器Timer!也是一种办法,但如果已经有其他用途了呢?资源紧张呢?
本文提供的方法即不占用资源,精度也很高,因为是CPU频率级别的,精度应该是在ns (纳秒)间。
所以完美符合电工的精确延时需求。
除了精确延时外,上面的代码也提供了一个计时器,用来干嘛的呢?
比如说,我想测试某一个函数的耗时有多少,可以用DWT直接记录下来,调试时可以直接查看,或打印出来。
这样就可以测试功能模块的效率性能啦。
好吧,好久不发帖。就说这么多,先去喝口水。。。
不知道发在【ARM】版块合不合适?不合适请管理员告诉我啊,表封我ID。。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|