52. DWT—内核定时器¶
本章参考资料《Cortex-M3内核编程手册》。
52.1. DWT简介¶
在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace),是用于系统调试及跟踪,它有一个32位的寄存器叫CYCCNT, 它是一个向上的计数器,记录的是内核时钟运行的个数,内核时钟跳动一次,该计数器就加1,精度非常高,决定内核的频率是多少, 例如本教程是基于F103系列,内核时钟是72M,那精度就是1/72M = 14ns,而程序的运行时间都是微秒级别的,所以14ns的精度是远远够的。 最长能记录的时间为:60s=2的32次方/72000000(假设内核频率为72M,内核跳一次的时间大概为1/72M=14ns), 而如果是H7这种400M主频的芯片,那它的计时精度高达2.5ns(1/400000000 = 2.5),而如果是 i.MX RT1052这种高速的处理器, 最长能记录的时间为: 8.13s=2的32次方/528000000 (假设内核频率为528M,内核跳一次的时间大概为1/528M=1.9ns) 。当CYCCNT溢出之后, 会清0重新开始向上计数。系统处理框图见图 系统处理框图。
要实现延时的功能,总共涉及到三个寄存器:DEMCR、DWT_CTRL、DWT_CYCCNT,分别用于开启DWT功能、开启CYCCNT及获得系统时钟计数值。
52.2. DWT相关寄存器¶
52.2.1. DEMCR¶
想要使能DWT外设,需要由另外的内核调试寄存器DEMCR的位24控制,写1使能。DEMCR的地址是:0xE000 EDFC。 关于该寄存器的详情见图 DEMCR的位24写1使能。
52.2.2. DWT_CYCCNT¶
使能DWT_CYCCNT寄存器之前,先清0。其基地址是0xE0001004,复位默认值是0,可读写类型。所以往0xE0001004这个地址写就将DWT_CYCCNT清0了。
52.3. 相关实现函数¶
初始化过程见 代码清单:DWT-1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /**
* @brief 初始化时间戳
* @param 无
* @retval 无
* @note 使用延时函数前,必须调用本函数
*/
void CPU_TS_TmrInit(void)
{
/* 使能DWT外设 */
DEM_CR |= (uint32_t)DEM_CR_TRCENA;
/* DWT CYCCNT寄存器计数清0 */
DWT_CYCCNT = (uint32_t)0u;
/* 使能Cortex-M DWT CYCCNT寄存器 */
DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
}
|
延时函数实现见 代码清单:DWT-2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /**
* @brief 采用CPU的内部计数实现精确延时,32位计数器
* @param us : 延迟长度,单位1 us
* @retval 无
* @note 使用本函数前必须先调用CPU_TS_TmrInit函数使能计数器,
或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
最大延时值为60秒,即60s=2的32次方/72000000
*/
void CPU_TS_Tmr_Delay_US(__IO uint32_t us)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
/* 在函数内部初始化时间戳寄存器, */
#if (CPU_TS_INIT_IN_DELAY_FUNCTION)
/* 初始化时间戳并清零 */
CPU_TS_TmrInit();
#endif
ticks = us * (GET_CPU_ClkFreq() / 1000000); /* 需要的节拍数 */
tcnt = 0;
told = (uint32_t)CPU_TS_TmrRd(); /* 刚进入时的计数器值 */
while (1) {
tnow = (uint32_t)CPU_TS_TmrRd();
if (tnow != told) {
/* 32位计数器是递增计数器 */
if (tnow > told) {
tcnt += tnow - told;
}
/* 重新装载 */
else {
tcnt += UINT32_MAX - told + tnow;
}
told = tnow;
/*时间超过/等于要延迟的时间,则退出 */
if (tcnt >= ticks)break;
}
}
}
|
在调用该函数前需先调用CPU_TS_TmrInit();函数初始化计数器。或者在core_delay.h文件中使能宏CPU_TS_INIT_IN_DELAY_FUNCTION。
本函数包含一个输入参数us,即就是要延时的微秒数。
52.4. 实验:DWT控制流水灯¶
52.4.1. 硬件设计¶
DWT属于单片机内部的内核中的外设,不需要额外的硬件电路,剩下的只需一个LED灯即可。
52.4.2. 软件设计¶
主函数
/**
* @brief 采用CPU的内部计数实现精确延时,32位计数器
* @param us : 延迟长度,单位1 us
* @retval 无
* @note 使用本函数前必须先调用CPU_TS_TmrInit函数使能计数器,
或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
最大延时值为60秒,即60s=2的32次方/72000000
*/
void CPU_TS_Tmr_Delay_US(__IO uint32_t us)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
/* 在函数内部初始化时间戳寄存器, */
#if (CPU_TS_INIT_IN_DELAY_FUNCTION)
/* 初始化时间戳并清零 */
CPU_TS_TmrInit();
#endif
ticks = us * (GET_CPU_ClkFreq() / 1000000); /* 需要的节拍数 */
tcnt = 0;
told = (uint32_t)CPU_TS_TmrRd(); /* 刚进入时的计数器值 */
while (1) {
tnow = (uint32_t)CPU_TS_TmrRd();
if (tnow != told) {
/* 32位计数器是递增计数器 */
if (tnow > told) {
tcnt += tnow - told;
}
/* 重新装载 */
else {
tcnt += UINT32_MAX - told + tnow;
}
told = tnow;
/*时间超过/等于要延迟的时间,则退出 */
if (tcnt >= ticks)break;
}
}
}
主函数中初始化了LED和DWT,然后在一个while循环中以0.1s的频率让LED闪烁。