FreeRTOS 調試方法(printf---打印任務執行情況)


以下轉載自安富萊電子: http://forum.armfly.com/forum.php

摘自:https://www.cnblogs.com/yangguang-it/p/7149048.html

本章節為大家介紹 FreeRTOS 的調試方法,這里的調試方法主要是教會大家如何獲取任務的執行情況,通過獲取的任務信息,可以進一步的配置和優化工程,這種方法非常實用,建議初學者必須掌握。 
串口打印調試說明
很多時候,我們需要了解任務的執行狀態,任務棧的使用情況以及各個任務的 CPU 使用率,這時就
需要用到官方提供的兩個函數 vTaskList  vTaskGetRunTimeStats。用戶就可以通過這兩個函數獲得任
務的執行情況。
獲取了任務執行情況后,可以通過串口將其打印出來,當然,也可以通過任何其它方式將其顯示出來。
本教程配套的例子統一采用串口打印的方式顯示任務的執行情況。另外有一點要特別注意,這種調試方式
僅限測試目的,實際項目中不要使用,這種測試方式比較影響系統實時性
為了獲取 FreeRTOS 的任務信息,需要創建一個定時器,這個定時器的時間基准精度要高於系統時鍾
節拍,這樣得到的任務信息才准確。這里提供的函數僅用於測試目的,切不可將其用於實際項目,原因有兩點:

1. FreeRTOS 的系統內核沒有對總的計數時間做溢出保護。
2. 定時器中斷是 50us 進入一次,比較影響系統性能。
這里使用的是 32 位變量來保存 50us 一次的計數值,最大支持計數時間:2^32 * 50us / 3600s =
59.6 分鍾。 運行時間超過了 59.6 分鍾將不准確。
具體在 FreeRTOS 的工程中如何做才可以實現任務信息獲取呢? 
使能相關宏定義
需要在 FreeRTOSConfig.h 文件中使能如下宏定義:

復制代碼
/***freertosconfig.h***/

extern volatile uint32_t ulHighFrequencyTimerTicks ;
/* Run time and task stats gathering related definitions. */
#define configUSE_TRACE_FACILITY    1
#define configGENERATE_RUN_TIME_STATS                1
#define configUSE_STATS_FORMATTING_FUNCTIONS         1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul) 
#define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
復制代碼

之前的博客:

使能了宏定義之后,必要完成portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()和portGET_RUN_TIME_COUNTER_VALUE()的定義,那么我們究竟應該如何定義並使用它們呢?

freertos官方網站給了提示(點擊這里查看更多):

本次實驗采用上面的做法,下面還有一個例子,但是沒有上面好用:

現在我使用第一種方式,STM32F429,TIM6基本定時器,產生一個50us周期的中斷,進一次中斷,計數器的值加1:

由於在進入

/* 第三步:啟動FreeRTOS,開始多任務調度,啟動成功則不返回 */
vTaskStartScheduler();(在這個函數中調用了portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() )

這個函數之前,定時器計數已經執行計數功能很多次了,為了保證我們的基石相對准確,我們在啟動freertos的API函數中將其置零,這也就是為什么那個宏#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 要這樣操作這個計數器ulHighFrequencyTimerTicks=0UL;這樣從任務開啟時,計數器從零開始計數,而portGET_RUN_TIME_COUNTER_VALUE()是為了得到最后的計數值,可以是一個函數,返回之前用戶自定義的計數器的值,也可以直接把那個計數器的值進行宏替換,在uxTaskGetSystemState()函數調用形式如下:

可以知道,我們只是需要這個計數器的值作為右值最后打印顯示。(一個比較好的參考,點擊這里)-(stack overflow參考)-(博客參考)。

有了前面的鋪墊之后,再來說說我們的驗證試驗:

功能描述:按下K1按鍵,打印出任務信息,函數如下:

復制代碼
static void vTaskWork(void *pvParameters)
{

    uint8_t pcWriteBuffer[500];

    while(1)
    {
        
        
        if (key1_flag==1)
        {
                    key1_flag=0;
                /* K1鍵按下 打印任務執行情況 */
                         
                    printf("=======================================================\r\n");
                    printf("任務名           任務狀態   優先級      剩余棧   任務序號\r\n");
                    vTaskList((char *)&pcWriteBuffer);
                    printf("%s\r\n", pcWriteBuffer);
                
                    printf("\r\n任務名            運行計數              使用率\r\n");
                    vTaskGetRunTimeStats((char *)&pcWriteBuffer);
                    printf("%s\r\n", pcWriteBuffer);
                
                /* 其他的鍵值不處理 */
    
            
        }
        
        vTaskDelay(20);
    }
}
復制代碼

任務一,執行打印,任務二,LED閃爍,任務三,蜂鳴器

 

復制代碼
static void AppTaskCreate(void)
{

        xTaskCreate(vTaskWork,       /* 任務函數  */
                 "vTaskWork",         /* 任務名    */
                 512,                   /* 任務棧大小,單位word,也就是4字節 */
                 NULL,                  /* 任務參數  */
                 1,                     /* 任務優先級*/
                 &xHandleTaskWork );  /* 任務句柄  */
    
    
    xTaskCreate( vTaskLed1,            /* 任務函數  */
                 "vTaskLed1",          /* 任務名    */
                 512,                 /* 任務棧大小,單位word,也就是4字節 */
                 NULL,                /* 任務參數  */
                 2,                   /* 任務優先級*/
                 &xHandleTaskLED1); /* 任務句柄  */
    
    xTaskCreate( vTaskBeep,             /* 任務函數  */
                 "vTaskBeep",           /* 任務名    */
                 512,                     /* 任務棧大小,單位word,也就是4字節 */
                 NULL,                   /* 任務參數  */
                 3,                       /* 任務優先級*/
                 &xHandleTaskBeep );  /* 任務句柄  */
    
    
}
復制代碼
復制代碼
void vTaskLed1(void *pvParameters)
{
    /* 任務都是一個無限,不能返回 */
    while(1)
    {
        LED3_ON;
    /* 阻塞延時,單位ms */        
        vTaskDelay( 500 );
        LED3_OFF;    
        vTaskDelay( 500 );
    }    
}
復制代碼
復制代碼
void vTaskBeep(void *pvParameters)
{
    /* 任務都是一個無限循環,不能返回 */
    while(1)
    {
        BEEP_ON;
    /* 阻塞延時,單位ms */        
        vTaskDelay( 20 );
        BEEP_OFF;    
        vTaskDelay( 500 );
    }    
}
復制代碼

基本硬件初始化:

復制代碼
static void BSP_Init(void)
{
    /*
     * STM32中斷優先級分組為4,即4bit都用來表示搶占優先級,范圍為:0~15
     * 優先級分組只需要分組一次即可,以后如果有其他的任務需要用到中斷,
     * 都統一用這個優先級分組,千萬不要再分組,切忌。
     */
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
    
    /* LED 初始化 */
    LED_GPIO_Config();
    /*串口初始化*/
    Debug_USART_Config();
    /*按鍵初始化*/
    EXTI_Key_Config();
    /*定時器6初始化*/
    TIMx_Configuration();
  /* 蜂鳴器初始化 */
    Beep_GPIO_Config();    
}
復制代碼

關於裸機部分的外設初始化配置不再贅述。這樣,下載程序之后,LED燈閃爍並且蜂鳴器鳴叫,在按下k1按鍵時,串口輸出如下:

 

 有了這個可以知道,我們512字的任務棧空間實在有點浪費,最后的剩余棧單位也是字。

最后,關於vTaskList((char *)&pcWriteBuffer);vTaskGetRunTimeStats((char *)&pcWriteBuffer);這個緩沖區的大小,官網說的,一個任務,大約40個字節足夠,我們取50字節,所以定義的緩沖區大小uint8_t pcWriteBuffer[500];可以足夠打印10個任務狀態。


免責聲明!

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



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