【STM32H7教程】第8章 STM32H7的終極調試組件Event Recorder


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第8章   STM32H7的終極調試組件Event Recorder

本章節為大家介紹終極調試方案Event Recoder,之所以叫終極解決方案,是因為所有Link通吃,支持時間測量,功耗測量,printf打印,RTX5及其所有中間件調試信息展示。

8.1 重要提示(必讀)

8.2 Event Recorder簡介

8.3 創建工程模板和注意事項

8.4 Event Recorder事件記錄的實現

8.5 Event Recoder 實現printf重定向

8.6 Event Statistics 時間測量功能的實現

8.7 Event Statistics 功耗測量功能的實現

8.8 Event Recoder對RTX5及其所有中間件的支持

8.9 JLINK配置說明

8.10 STLINK配置說明

8.11 CMSIS-DAP配置說明

8.12 ULINK配置說明

8.13 配套例子

8.14 總結

 

 

8.1   重要提示(必讀

  1.   只要是MDK支持的調試下載器,基本都支持Event Recorder,本教程測試了JLINK,STLINK和CMSIS-DAP。
  2.   務必使用MDK5.25及其以上版本。
  3.   使用ARM_Compiler 軟件包V1.4.0及其以上版本。詳情看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87175
  4.   CMSIS軟件包 要是使用V5.3.0及其以上版本,詳情本教程8.3小節末尾的說明。
  5.   如果大家的MDK5.X應用不是很熟練的話,可以看論壇網友翻譯的MDK5.X入門手冊:http://www.armbbs.cn/forum.php?mod=viewthread&tid=31288。如果覺得看手冊上手慢的話,可以直接看KEIL官方做的MDK入門系列視頻,帶中文字幕:http://www.armbbs.cn/forum.php?mod=viewthread&tid=82667
  6.   為了實現Event Recorder組件的最高性能,最好將下載器的時鍾速度設置到所支持的最大值,另外,根據需要加大EventRecorderConf.h文件中的緩沖大小,默認可以緩沖64個消息(動態更新的FIFO空間)。
  7.   此調試組件不需要用到SWO引腳,使用標准的下載接口即可。以我們的開發板為例,用到VCC,GND,SWDIO,SWCLK和NRST。大家使用三線JLINK-OB也是沒問題的,僅需用到GND,SWDIO和SWCLK。

8.2   Event Recorder簡介

前面的專題教程中為大家講解了使用SEGGER的RTT功能來替代串口打印,比較方便。只是這種方法限制用戶必須使用JLINK才可以。而使用Event Recorder的話,無此限制,各種LINK通吃。只要是MDK支持的即可。

Event Recorder是MDK在5.22版本的時增加的功能,到了5.25版本后,這個功能就更加完善了,增加了時間測量和功耗測量的功能。

此調試組件不需要用到SWO引腳,使用標准的下載接口即可。以我們的開發板為例,用到VCC,GND,SWDIO,SWCLK和NRST。大家使用三線JLINK-OB也是沒問題的,僅需用到GND,SWDIO和SWCLK。

 

  •   JTAG接口和SWD接口區別

下圖分別是20pin的標准JTAG引腳和SWD( Serial Wire Debug)引腳,一般SWD接口僅需要Vref,SWDIO,SWCLK,RESET和GND五個引腳即可,SWO(Serial Wire Output)引腳是可選的。有了SWO引腳才可以實現數據從芯片到電腦端的數據發送。

  •   詞條 SWV(Serial Wire Viewer)

SWV是由儀器化跟蹤宏單元ITM(Instrumentation Trace Macrocell)和SWO構成的。SWV實現了一種從MCU內部獲取信息的低成本方案,SWO接口支持輸出兩種格式的跟蹤數據,但是任意時刻只能使用一種。兩種格式的數據編碼分別是UART(串行)和Manchester(曼徹斯特)。當前JLINK僅支持UART編碼,SWO引腳可以根據不同的信息發送不同的數據包。當前M3/M4可以通過SWO引腳輸出以下三種信息:

  1. ITM支持printf函數的debug調用(工程需要做一下接口重定向即可)。ITM有32個通道,如果使用MDK的話,通道0用於輸出調試字符或者實現printf函數,通道31用於Event Viewer,這就是為什么實現Event Viewer需要配置SWV的原因。
  2. 數據觀察點和跟蹤DWT(Data Watchpoint and Trace)可用於變量的實時監測和PC程序計數器采樣。
  3. ITM 還附帶了一個時間戳的功能:當一個新的跟蹤數據包進入了ITM的FIFO 時,ITM 就會把一個差分的時間戳數據包插入到跟蹤數據流中。跟蹤捕獲設備在得到了這些時間戳后,就可以找出各跟蹤數據之間的時間相關信息。另外,在時間戳計數器溢出時也會發送時間戳數據包。

8.2.1        Event Recorder的特色

Event Recorder的特色主要有以下幾點:

  1. 提升應用程序動態執行期間的檢測能力。
  2. 支持的事件類型濾除機制,比如運行錯誤、API調用、內部操作和操作信息的區分。
  3. 可以在任務中、RTOS內核中和中斷服務程序中任意調用。
  4. 對於帶ITM功能的Cortex-M3/M4/M7/M33內核芯片,執行記錄期間,全程無需開關中斷操作。對於不帶ITM功能的Cortex-M0/M0+/M23,是需要開關中斷的。
  5. 支持printf重定向。
  6. 各種link通吃,支持SWD接口或者JTAG接口方式的JLINK、STLINK、ULINK和CMSIS-DAP。
  7. 對於帶DWT時鍾周期計數器功能的Cortex-M3/M4/M7/M33內核芯片,創建時間戳時,可以有效降低系統負擔,無需專用定時器來實現。
  8. Event Recorder執行時間具有時間確定性,即執行的時間是確定的,而且執行速度超快,因此,實際產品中的代碼依然可以帶有這部分,無需創建debug和release兩種版本。
  9. RTX5及其所有中間件都支持Event Recorder調試。

8.2.2        Event Recorder是如何工作的

首先來看下面這張圖:

 

在截圖的左下角有個Memory內存區,在這個內存區里面有一個緩沖Event Buffer,其實就是一個大數組。MDK通過訪問這個數組實現消息的圖形化展示。為了正確的圖形化展示,數組緩沖里面的數據就得有一定的數據格式。而這個數據格式就是通過左側截圖里面的Event Recorder和Event Filter來實現的。Event Recorder的API實現數據記錄和整理,Event Filter的API實現數據的篩選,從而可以選擇哪些數據可以在MDK的Event Recorder調試組件里面展示出來。

這就是Event Recorder的基本工作流程。

8.2.3  Event Statistics時間測量功能

Event Statistics提供的時間測量功能簡單易用,在測試代碼前后加上測量函數即可:

 

在本章教程程的8.6小節為大家詳細進行了講解。通過這個時間測量功能,用戶可以方便測試代碼的執行時間,從而根據需要,進行合理的優化,提高代碼執行效率。

8.2.4  Event Statistics功耗測量功能

Event Statistics提供的功耗測量功能,當前只有KEIL的ULINKplus支持此功能,由於ULINKplus價格不便宜,一套5000多,大家作為了解即可,實際效果如下:

 

8.2.5  Event Recorder的實現原理

每條Event Recorder消息是由16字節的數據組成,32位的ID,32位的時間戳,兩個32位的數據,共計16個字節。其中32位ID最重要,格式如下:

Level指定消息分類,主要用於消息篩選:

Component number指定事件消息所屬的軟件組件,也可用於過濾:

看了下Event Recorder的源碼,每條消息大體是一樣的:

typedef struct {

  uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)

  uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)

  uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)

  uint32_t info;                // Record Information

                                //  [ 7.. 0]: Message ID (8-bit)

                                //  [15.. 8]: Component ID (8-bit)

                                //  [18..16]: Data Length (1..8) / Event Context

                                //      [19]: IRQ Flag

                                //  [23..20]: Sequence Number

                                //      [24]: First Record

                                //      [25]: Last Record

                                //      [26]: Locked Record

                                //      [27]: Valid Record

                                //      [28]: Timestamp MSB

                                //      [29]: Value 1 MSB

                                //      [30]: Value 2 MSB

                                //      [31]: Toggle bit

} EventRecord_t;

其中參數成員info最重要,也就是前面說的32位ID,這里的說明與前面的說明稍有不同。這里是經過處理后,實際存儲到Event Recorder緩沖里面的數據。

對於Event Recorder,大家了解了這些知識點基本就夠用了。

8.3   創建工程模板和注意事項

Event Recorder工程的創建比較簡單,這里分步為大家做個介紹。

  第1步:准備好一個使用MDK5.25或以上版本創建的工程模板。

 

  第2步:安裝ARM_Compiler V1.4.0或以上版本(如果有最新版,直接安裝最新的),詳情見帖子:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=87175 。

  第3步:打開MDK5.25或以上版本創建的RTE環境。

 

  第4步:通過RTE環境,為工程添加Event Recorder功能。

 

  第5步:為了實現printf重定向,我們需要將STDOUT的輸出方式改為Event Recorder,即選項里面的EVR。

 

  第6步:打開通過RTE環境為工程添加的文件EventRecorderConf.h,配置如下:

 

這里主要設置方框里面的兩個參數。

Number of Records:表示Event Recorder緩沖可以記錄的消息條數。

Time Stamp Source:表示時間戳來源,有如下四種可以選擇,我們這里使用DWT時鍾周期計數器。

 

由於選擇的是DWT,因此EventRecorderCong.h文件中的Systick Configuration配置就不用管了。

==========================

通過上面的6步就完成了Event Recorder功能的添加,效果如下:

 

添加完成后,還有非常重要的兩點要特別注意:

  •   第1點:一定要使用當前最新的CMSIS軟件包,當前是V5.4.0(隨着時間的推移,如果升級了新版本,直接使用新版即可)。大家可以從這里下載:

http://www.keil.com/dd2/pack/

 

下載並導入到MDK后,需要大家更新自己現有工程CMSIS文件里面的頭文件,可以直接將CMSIS文件夾中Include文件里面的所有文件全部刪掉,替換為MDK安裝目錄如下路徑里面的所有頭文件:

ARM\PACK\ARM\CMSIS\5.4.0\CMSIS\Include。保證頭文件都是最新的5.4.0版本。

  •  第2點:由於使能了printf重定向,大家的工程里面一定不要再做重定向了,比如fpuc,fgetc。另外當前選擇了微庫MicroLib:

 

注意這兩點后,就可以使用Event Recorder的功能了。

8.4   Event Recorder事件記錄的實現

Event Recorder的使用也比較省事,這里也分步為大家進行說明:

  第1步:初始化,僅需添加如下兩行代碼即可。

/* 初始化EventRecorder並開啟 */

EventRecorderInitialize(EventRecordAll, 1U);

EventRecorderStart();

  第2步:調用Event Recorder的API就可以使用了,主要有以下三個API:

EventRecord2:可以發送兩個32位數據。

EventRecord4:可以發送四個32位數據。

EventRecordData:可以發送字符串。

顯然這三個函數沒有printf使用方便,所以對於這三個函數,大家做個簡單的了解即可。教程配套例子里面有調用到這三個函數,可以操作熟悉下。這三個API的說明是在對應的help文檔中,即MDK安裝目錄路徑:/ARM/PACK/Keil/ARM_Compiler/1.6.0/Doc/General/html/index.html。

 

  第3步:進入調試狀態,選上周期更新:

 

點擊全速運行:

 

然后將Event Recorder調試組件展示出來:

 

效果如下:

 

另外,這里有個知識點需要大家了解下,如果程序里面也調用了Event Statistics時間測量函數,那么也會在這個界面里面展示消息的,如何才能僅展示大家想看的功能呢?這就需要用到Event Recorder支持的篩選功能。使用這個功能需要大家先暫停全速運行,然后點擊下面這個選項:

 

彈出的界面里面可以設置哪些選項顯示,哪些選項不顯示(勾上表示顯示),我們這里取消Event Statistics的顯示,設置完畢后記得點擊OK按鈕。

 

這就不展示Event Statistics的內容了。再次啟動全速運行前,下面這個選項的對勾別忘了勾上。

 

8.5   Event Recorder實現printf重定向

實現printf輸出需要用到MDK調試組件中的Debug(printf) Viewer,輸出效果就跟大家使用串口調試軟件一樣,可以輸出中文和英文。

MDK的printf調試組件使用方法跟本章8.4小節中的說明一樣,點擊調試,選中周期運行,然后顯示Debug(printf) Viewer調試組件:

 

效果如下:

 

另外,還有一個知識點需要給大家做個補充,使用SWD接口的SWO引腳也是可以做串口打印的,並且也是通過這個調試組件Debug(printf) Viewer進行輸出。只是這種方式的性能沒有Event Viewer強,而且要多占用一個SWO引腳。

 

關於SWO輸出方式可以看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=526

8.6   Event Statistics 時間測量功能的實現

時間測量功能簡單易用,僅需一個起始函數,一個停止函數即可。當前支持4組,每組支持16路測量,也就是可以同時測量64路。

時間測量的API函數支持多任務和中斷里面隨意調用。

1、  測量起始函數:EventStartG (slot) 或者EventStartGv (slot, val1, val2)

  •  函數中的字母G是表示分組A,B,C,D,即實際調用函數為EventStartA,EventStartB,EventStartC和EventStartD。
  •   函數的第一個形參slot的范圍是0-15,也就是每個分組可以測試16路。
  •   函數后面的兩個形象val1和val2是32位變量,用戶可以用這兩個形參來傳遞變量數值給Event Statistics調試組件里面,方便圖形化展示。簡單的說,這兩個變量僅僅起到一個傳遞變量數值的作用。

2、  測量停止函數:EventStopG (slot) 或者  EventStopGv (slot, val1, val2)

  •   函數中的字母G是表示分組A,B,C,D,即實際調用函數為EventStopA,EventStopB,EventStopC和EventStopD。
  •   函數的第一個形參slot的范圍是0-15,也就是每個分組可以測試16路。
  •  函數后面的兩個形象val1和val2是32位變量,用戶可以用這兩個形參來傳遞變量數值給Event Statistics調試組件里面,方便圖形化展示。簡單的說,這兩個變量僅僅起到一個傳遞變量數值的作用。

 

這里也分步為大家說明Event Statistics時間測量功能的使用方法。

  第1步:初始化,僅需添加如下兩行代碼即可。

/* 初始化EventRecorder並開啟 */

EventRecorderInitialize(EventRecordAll, 1U);

EventRecorderStart();

  第2步:在要測量的代碼前后加上起始和結束時間。

EventStartA(0);

測量的代碼部分

EventStopA(0);

這里是用分組A的測量通道0。

  第3步:跟本章8.4小節講解的一樣,點擊調試,選擇周期更新選項,然后全速運行。

  第4步:全速運行后,顯示Event Statistics調試組件。

 

比如我這里簡單的測試了一個5ms的延遲函數,效果如下(測量時間是動態更新的):

 

另外要注意一點,微秒的時間單位us可能無法正常顯示,這個是沒有關系的:

 

8.7   Event Statistics 功耗測量功能的實現

當前僅KEIL自家的ULINKplus支持功耗測量功能,這款下載器不便宜,一套5000多,大家有個了解即可,我們這里就不做講解了。

8.8   Event Recorder對RTX5及其所有中間件的支持

后面做RTX5及其所有中間件的教程時會為大家做講解,這里讓大家看下效果:

  •   RTX5組件和使用Event Recoder的效果:

 

 

  •   網絡調試組件效果展示:

 

 

  •   文件系統和USB協議棧的效果展示:

 

8.9   JLINK配置說明

為了幫助大家更好的使用JLINK,這里將JLINK配置中關鍵的幾個地方做個說明。

  •   下面這個地方最重要一定要正確設置當前系統工作的主頻,如果不正確,會導致Event Statistics的時間統計不正確對於H7,Core部分要填400MHz)。

 

注:如果大家調試狀態彈出SWD配置時鍾超出范圍的問題,可以考慮將上面截圖中的Enable選項的對勾取消掉即可,但內核時鍾一定要修改為芯片的主頻。

另外,進入調試狀態后,右下角的時間是否正常更新都沒有關系:

 

  •   其它選項配置如下(只要大家的工程能夠正常調試,配置就是沒問題的):

 

 

 

 

8.10 STLINK配置說明

為了幫助大家更好的使用STLINK,這里將STLINK配置中關鍵的幾個地方做個說明。

  •   下面這個地方最重要一定要正確設置當前系統工作的主頻,如果不正確,會導致Event Statistics的時間統計是不正確的對於H7,Core部分要填400MHz)。

 

另外注意,進入調試狀態后,右下角的時間是否正常更新都沒有關系:

 

  • 其它選項配置如下(只要大家的工程能夠正常調試,配置就是沒問題的)

 

 

8.11 CMSIS-DAP配置說明

為了幫助大家更好的使用CMSIS-DAP,這里將CMSIS-DAP配置中關鍵的幾個地方做個說明。

  •   下面這個地方最重要一定要正確設置當前系統工作的主頻,如果不正確,會導致Event Statistics的時間統計不正確對於H7,Core部分要填400MHz)。

 

另外注意,進入調試狀態后,右下角的時間是否正常更新都沒有關系:

 

  •   其它選項配置如下(只要大家的工程能夠正常調試,配置就是沒問題的)

 

 

 

 

 

8.12 ULINK配置說明

由於手頭沒有ULINK,這里就不做講解了。如果大家需要相關配置,按照前面小節三款LINK的配置照葫蘆畫瓢搞一下即可,或者在MDK安裝目錄的路徑ARM\Hlp下有對應的文檔說明:

 

8.13 配套例子

本章節教程配套了如下例程,僅MDK版本。

  •   V7-008_終極調試組件EventRecoder的使用

 

具體代碼實現也比較簡單,以V6開發板為例,定義一個TIM6的中斷,中斷頻率是500Hz,通過Event Statistics測量中斷的執行頻率。代碼如下:

#include "bsp.h"

#include "EventRecorder.h"

 

 

 

 

/* 定時器頻率,500Hz */

#define  timerINTERRUPT_FREQUENCY    500

 

/* 中斷優先級 */

#define  timerHIGHEST_PRIORITY       10

 

    

/*

*********************************************************************************************************

*    函 數 名: vEventRecorderTest

*    功能說明: 創建定時器

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

void vEventRecorderTest(void)

{

     bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);

     EventStartB(0);   

}

 

/*

*********************************************************************************************************

*    函 數 名: TIM6_DAC_IRQHandler

*    功能說明: TIM6中斷服務程序。

*    形    參: 無

*    返 回 值: 無

*********************************************************************************************************

*/

void TIM6_DAC_IRQHandler( void )

{

     if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)

     {

         EventStopB(0);    

         EventStartB(0);   

        

         /* 清除更新標志 */

         TIM6->SR = ~ TIM_FLAG_UPDATE;

     }

}

效果如下,測量的平均頻率是1.98ms,與我們設計的500Hz基本符合:

 

應用程序的設計如下:

#include "bsp.h"            /* 底層硬件驅動 */

#include "EventRecorder.h"

 

 

/*

*********************************************************************************************************

*                                              函數和變量

*********************************************************************************************************

*/

extern void vEventRecorderTest(void);

uint8_t s_ucBuf[10] = "armfly";

 

 

/*

*********************************************************************************************************

*    函 數 名: main

*    功能說明: c程序入口

*    形    參:無

*    返 回 值: 錯誤代碼(無需處理)

*********************************************************************************************************

*/

int main(void)

{

     uint8_t ucKeyCode;     /* 按鍵代碼 */

     uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;

 

 

     /* 初始化EventRecorder並開啟 */

     EventRecorderInitialize(EventRecordAll, 1U);

     EventRecorderStart();

    

     bsp_Init();        /* 硬件初始化 */

 

     bsp_StartAutoTimer(0, 200); /* 啟動1個200ms的自動重裝的定時器 */

 

     /* 測量中斷周期 */

     vEventRecorderTest();

    

     /* 進入主程序循環體 */

     while (1)

     {

         bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */

 

         /* 判斷定時器超時時間 */

         if (bsp_CheckTimer(0))

         {

              EventStartA(0);  

              EventStopA(0);

             

              EventStartA(1);

              bsp_DelayMS(5);

              EventStopA(1);

             

              EventStartA(2);

              bsp_DelayMS(30);

              EventStopA(2);

             

              t0++;

              EventStartAv(3, t0, t0);

              bsp_DelayMS(30);

              EventStopAv(3, t0, t0);

         }

 

         /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */

         ucKeyCode = bsp_GetKey();   /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   case KEY_DOWN_K1:           /* K1鍵按下 */

                       t1 += 1;

                       t2 += 2;

                        EventRecord2(1+EventLevelAPI, t1, t2);

                       t3 += 3;

                       t4 += 4;

                       EventRecord4(2+EventLevelOp, t1, t2, t3, t4);

                       EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));

                       break;

 

                   case KEY_DOWN_K2:           /* K2鍵按下 */

                       printf("K2按鍵按下\r\n");

                       break;

 

                   case KEY_DOWN_K3:           /* K3鍵按下 */

                        printf("K3按鍵按下\r\n");

                       break;

 

                   default:

                        /* 其它的鍵值不處理 */

                       break;

              }

         }

     }

}

應用程序里面主要實現了三個功能:

  1、利用測量分組A實現4路時間的測量(第1路什么也沒有測量,可以用來表示這兩個函數本身執行占用的時間)。每100ms測量一次時間,效果如下:

 

  2、利用函數EventRecord2,EventRecord4和EventRecordData發送消息事件。按下按鍵K1進行更新,效果如下:

 

  3、基於Event Recorder的printf重定向。按下按鍵K2或者K3會打印消息,效果如下:

 

8.14 總結

Event Recoder還是非常實用的,建議大家多使用幾次,熟練掌握。基本用上幾次就上癮,離不開了,的確是工程調試的利器。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM