STM32“隱藏的定時器”-DWT

語言: CN / TW / HK

1、前言

在之前的文章在《STM32延時函式的四種方法》使用定時器延時,在《如何測量程式碼執行時間》中提到使用定時器外設計算程式碼執行時間。文中提到這種方法的明顯缺點就是需要佔用一個定時器,一些MCU在特定應用場景下定時器外設資源是十分稀缺的。在留言區有位大佬提到可以使用DWT,我就研究了一番。

 

 02、DWT

在Cortex-M裡面有一個外設叫DWT(DataWatchpoint andTrace),是用於系統除錯及跟蹤,DWT的中文名字應該是:資料觀察點觸發。在STM32使用者手冊的第32章節Debugsupport (DBG)有如下框圖。

 

明顯DWT屬於DBG部分的功能,從上圖的標題可以看出DWT屬於CortexM3核心的,理論上M3核心的MCU都支援的,這個下文會說明。在這裡我將其稱之為“隱藏的定時器”,因為他可以代替定時器外設實現上文提到延時功能和測量程式碼執行時間的功能,DWT不能代替定時器的其他功能。

之所以DWT可以實現延時功能,因為它有一個32的計數器CYCCNT,這是一個向上計數的計數器,當它溢位時會自動清零並重新開始向上計數,它的頻率就是核心的主頻。簡單點說,就是核心時鐘跳動一下,CYCCNT計數器就加1。

很明顯DWT計數器的精度和系統主頻有關,我們常用的STM32F103主頻一般為72Mhz,STM32F207一般為120Mhz,STM32H7主頻一般為400Mhz。以為主頻最低為72Mhz的STM32F103為例,精度是1/72M= 14ns,這個精度足以滿足大部分延時函式的需求,同樣程式的執行時間都是微秒級別的,遠遠滿足測量程式碼執行時間的要求。

03、DWT的配置

首選使用DWT前必須使能DBG的系統跟蹤,控制使能位在DEMCR暫存器的bit24。注意該暫存器詳細說明在STM32的使用者手冊上查不到,需要在CortexM3核心手冊查到,在《Cortex-M3權威指南》書中也可以查到。

 

在使能CYCCNT計數器前,必須先將其清零。下圖是從ARM的官方手冊《Cortex-M3Technical Reference Manual》中查到的。

 

 使能CYCCNT計數器,其控制位是DWT控制暫存器的第一位,寫1使能,則啟用CYCCNT計數器,否則CYCCNT計數器將不會工作。

 

 總結一下:

a.先使能DWT外設,由核心除錯暫存器DEM_CR的位24控制,寫1使能。

b.使能CYCCNT暫存器之前,先清0。

c.使能CYCCNT暫存器,由DWT_CTRL的位0控制,寫1使能。

程式碼如下

//暫存器基地址
#define    DWT_CR    *(uint32_t*)0xE0001000
#define    DWT_CYCCNT    *(uint32_t*)0xE0001004
#define    DEM_CR    *(uint32_t*)0xE000EDFC
 
//定義需使能位
#define    DEM_CR_TRCENA    (1<<24)
#define    DWT_CR_CYCCNTENA    (1<<0)
 
//DWT init
void DWT_init(void)
{
  DEM_CR |= (uint32_t)DEM_CR_TRCENA;
  DWT_CYCCNT = (uint32_t)0u;
  DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
}
//get DWT count
uint32_t DWT_TS_GET(void)
{
  return((uint32_t)DWT_CYCCNT);
}

04、程式碼

從上文我們得知,我們已經獲得了一個32位向上累加的計數器,溢位會自動清零並累加,頻率是系統主頻。那麼我們簡單封裝下,就可以實現延時函式。以下程式碼在120Mhz的STM32F207測試。

//使用DWT延時time_ms毫秒
void DWT_Delay_Ms(uint32_t time_ms)
{
  uint32_t old_counter,current_counter;
  uint32_t delay_ms;
  
  old_counter = DWT_TS_GET();
  current_counter = DWT_TS_GET();
  delay_ms = 0;
  while(delay_ms<time_ms)
  {
    current_counter = DWT_TS_GET();
    if(current_counter > old_counter)
      delay_ms = (current_counter - old_counter)/(SystemCoreClock/1000);
    else
      delay_ms = (current_counter + 0XFFFFFFFF - old_counter)/(SystemCoreClock/1000);
  }
}

使用之前的文章《如何測量程式碼執行時間》測量延時函式是否準確。

DWT_Delay_Ms(100);//延時100ms
time_ms=Time_Difference_ms();

如下圖,延時函式精確延時,沒有問題

實現測量程式碼執行時長的函式介面

//使用DWT測量函式執行時間
float DTW_Time_Difference_ms(void)
{
  static uint32_t old_counter;
  uint32_t counter,couter_current;
  couter_current = DWT_TS_GET();
  if(couter_current > old_counter)
    counter = couter_current - old_counter;
  else
    counter = couter_current + 0XFFFFFFFF - old_counter;
  old_counter = couter_current;
  return (counter / (SystemCoreClock/1000));
}

使用之前的文章《STM32延時函式的四種方法》精確延時,然後使用DWT測量延時時間。

elay_ms(300);//延時300ms
time_ms=DTW_Time_Difference_ms();

如下圖,可以精確測量程式碼執行時間,沒有問題。

 

 

 05、後記

本文使用DWT代替了定時器部分功能,它的優缺點如下:

1、優點是:方便移植,經過測試在M3、M4、M7核心的MCU上都可以使用。

2、缺點是:和定時器一樣,都有一個延時的最大時間,測量程式碼執行時間的最大值。

如果專案使用MCU有空閒的定時器,且不考慮換MCU的話,我個人建議還是使用通用的定時器外設,不要使用DWT,雖然DWT方便移植,但通用定時器外設簡單易懂,對於沒有了解過這部分知識的小白,看到DWT的延時函式,還需要學習下。

Keil和IAR的工程檔案下載地址:

PCB和工程程式碼開源地址:

https://github.com/strongercjd/STM32F207VCT6

 

點選檢視本文所在的專輯,STM32F207教程