1前言上一篇在《STM32延时函数的四种方法》中使用了定时器延时,在《如何测量代码运行时间》中提到了使用定时器来计算代码的运行时间。文中提到的这种方式的明显缺点是需要占用一个定时器,而某些MCU在特定应用场景下定时器外设资源非常紧缺。留言区有个大佬提到可以用DWT,于是研究了一下。2DWT在Cortex-M中有一个外设叫DWT(DataWatchpointandTrace),用于系统调试和跟踪。DWT的中文名称应该是:datawatchpointtrigger。在STM32用户手册Debugsupport(DBG)的第32章中有以下框图。显然,DWT属于DBG的功能。从上图的标题我们可以看出DWT属于CortexM3内核。理论上所有M3内核的MCU都支持,下面会说明。这里我称它为“隐藏定时器”,因为它可以代替定时器外设实现上面提到的代码的延时功能和测量运行时间的功能。DWT不能替代定时器的其他功能。DWT之所以能实现延时功能,是因为它有一个32位计数器CYCCNT,是一个向上计数的计数器。当它溢出时,它会自动清除并重新开始计数。它的频率就是核心的主频。简单来说,当核心时钟跳变时,CYCCNT计数器加1。可见DWT计数器的精度与系统的主频有关。我们常用的STM32F103的主频一般为72Mhz,STM32F207的主频一般为120Mhz,STM32H7的主频一般为400Mhz。以主频最低72Mhz的STM32F103为例,精度为1/72M=14ns,足以满足大部分延时功能的需要。同一个程序的运行时间在微秒级别,足以衡量代码需求的运行时间。3DWT配置在使用DWT前首先选择使能DBG系统跟踪,控制使能位在DEMCR寄存器的bit24。注意,在STM32用户手册中找不到该寄存器的详细说明。需要在CortexM3内核手册中找到,在《Cortex-M3权威指南》一书中也可以找到。在启用CYCCNT计数器之前,必须先将其清零。下图是从ARM的官方手册《Cortex-M3Technical Reference Manual》找到的。使能CYCCNT计数器,其控制位为DWT控制寄存器的第一位,写1使能,则使能CYCCNT计数器,否则CYCCNT计数器不工作。总结一下:首先使能DWT外设,由内核调试寄存器DEM_CR的第24位控制,写1使能。b.在启用CYCCNT寄存器之前,将其清零。c.使能CYCCNT寄存器,由DWT_CTRL的bit0控制,写1使能。代码如下//注册基地址#defineDWT_CR*(uint32_t*)0xE0001000#defineDWT_CYCCNT*(uint32_t*)0xE0001004#defineDEM_CR*(uint32_t*)0xE000EDFC//定义使能位#defineDEMNT_CR_TRCENA(1<<24)#ENCR_CYD<<0)//DWTinitvoidDWT_init(void){DEM_CR|=(uint32_t)DEM_CR_TRCENA;DWT_CYCCNT=(uint32_t)0u;DWT_CR|=(uint32_t)DWT_CR_CYCCNTENA;}//getDWTcountuint32_tDWT_TS_GET(CYCCNTYNA;}4上面的代码我们知道我们得到了一个32位的向上累加计数器,溢出会自动清零累加,频率为系统的主频。那么我们简单封装一下就可以实现延时功能。以下代码是在STM32F207上测试的at120Mhz.//使用DWT延迟time_ms毫秒voidDWT_Delay_Ms(uint32_ttime_ms){uint32_told_counter,current_counter;uint32_tdelay_ms;old_counter=DWT_TS_GET();current_counter=DWT_TS_GET();delay_ms=0;while(delay_ms
