FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試
前言
用RTOS編程,為每個任務(wù)分配多大的堆棧空間就成了一項技術(shù)活:分配多了浪費系統(tǒng)資源,分配少了又恐怕會發(fā)生堆棧溢出。由于中斷和搶占式調(diào)度器的存在,我們要估算出一個任務(wù)需要多少堆棧是非常困難的,今天我們就介紹一種方法,來獲取每個任務(wù)的剩余堆??臻g。本文以NXP LPC177x_8x系列微控制器為例。
我們將這個功能做成一個命令,添加到FreeRTOS使用任務(wù)通知實現(xiàn)命令行解釋器一文介紹的命令解釋列表中。當(dāng)程序運行一段時間后,我們在SecureCRT軟件中輸入命令“task”后回車,能看到如圖1-1所示的任務(wù)信息。這里只有兩個任務(wù),其中堆棧一列中的數(shù)字,代表對應(yīng)任務(wù)剩余的堆??臻g,單位是StackType_t類型,這個類型在移植層定義,一般定義為4字節(jié)。
圖1-1:任務(wù)信息
1.使能可視化追蹤和運行時間統(tǒng)計功能
如圖1-1所示,要實現(xiàn)堆棧使用量信息以及CPU使用率信息,必須將FreeRTOSConfig.h文件中的兩個宏設(shè)置為1:
#define configUSE_TRACE_FACILITY 1 #define configGENERATE_RUN_TIME_STATS 1
第一個宏用來使能可視化追蹤功能,第二個宏用來使能運行時間統(tǒng)計功能。如果第二個宏設(shè)置為1,則下面兩個宏必須被定義:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():
用戶程序需要提供一個基準(zhǔn)時鐘函數(shù),函數(shù)完成初始化基準(zhǔn)時鐘功能,這個函數(shù)要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上。
這是因為運行時間統(tǒng)計需要一個比系統(tǒng)節(jié)拍中斷頻率還要高分辨率的基準(zhǔn)定時器,否則,統(tǒng)計可能不精確。
基準(zhǔn)定時器中斷頻率要比統(tǒng)節(jié)拍中斷快10~100倍?;鶞?zhǔn)定時器中斷頻率越快,統(tǒng)計越精準(zhǔn),但能統(tǒng)計的運行時間也越短(比如,基準(zhǔn)定時器10ms中斷一次,8位無符號整形變量可以計到2.55秒,但如果是1秒中斷一次,8位無符號整形變量可以統(tǒng)計到255秒)。
portGET_RUN_TIME_COUNTER_VALUE():
用戶程序需要提供一個返回基準(zhǔn)時鐘當(dāng)前“時間”的函數(shù),這個函數(shù)要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。
我們使用定時器1來產(chǎn)生基準(zhǔn)時鐘,定時器1初始化函數(shù)為:
/** * 初始化計時定時器1,用于OS任務(wù)運行時間統(tǒng)計 */ void init_timer1_for_runtime_state(void) { TIM_TIMERCFG_Type Timer0CfgType; Timer0CfgType.PrescaleOption=TIM_PRESCALE_USVAL; //預(yù)分頻的單位是微秒 Timer0CfgType.PrescaleValue=500; //預(yù)分頻后為500微秒, TIM_Init(LPC_TIM1,TIM_TIMER_MODE,&Timer0CfgType); LPC_TIM1->TCR=0x01; }
定時器1被配置成每隔500微秒,TC寄存器值增一。我們將定時器1的 TC寄存器值作為基準(zhǔn)時鐘當(dāng)前時間。當(dāng)TC寄存器值溢出時,大概要經(jīng)過24.8天,這對于我們這個應(yīng)用是足夠的。
在FreeRTOSConfig.h中,定義初始化基準(zhǔn)定時器宏和獲取當(dāng)前時間宏:
extern void init_timer1_for_runtime_state(void); #define TIMER1_TC ( * ( ( volatile uint32_t * )0x40008008 ) ) #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() init_timer1_for_runtime_state() #define portGET_RUN_TIME_COUNTER_VALUE() TIMER1_TC
2.獲取任務(wù)信息并格式化
獲取每個任務(wù)的狀態(tài)信息使用的是API函數(shù)uxTaskGetSystemState(),該函數(shù)定義為:
UBaseType_tuxTaskGetSystemState( TaskStatus_t * constpxTaskStatusArray, const UBaseType_tuxArraySize, unsigned long * constpulTotalRunTime );
函數(shù)uxTaskGetSystemState()向TaskStatus_t結(jié)構(gòu)體填充相關(guān)信息,系統(tǒng)中每一個任務(wù)的信息都可以填充到TaskStatus_t結(jié)構(gòu)體數(shù)組中,數(shù)組大小由uxArraySize指定。結(jié)構(gòu)體TaskStatus_t定義如下:
typedef struct xTASK_STATUS { /* 任務(wù)句柄*/ TaskHandle_t xHandle; /* 指針,指向任務(wù)名*/ const signed char *pcTaskName; /*任務(wù)ID,是一個獨一無二的數(shù)字*/ UBaseType_t xTaskNumber; /*填充結(jié)構(gòu)體時,任務(wù)當(dāng)前的狀態(tài)(運行、就緒、掛起等等)*/ eTaskState eCurrentState; /*填充結(jié)構(gòu)體時,任務(wù)運行(或繼承)的優(yōu)先級。*/ UBaseType_t uxCurrentPriority; /* 當(dāng)任務(wù)因繼承而改變優(yōu)先級時,該變量保存任務(wù)最初的優(yōu)先級。僅當(dāng)configUSE_MUTEXES定義為1有效。*/ UBaseType_t uxBasePriority; /* 分配給任務(wù)的總運行時間。僅當(dāng)宏configGENERATE_RUN_TIME_STATS為1時有效。*/ unsigned long ulRunTimeCounter; /* 從任務(wù)創(chuàng)建起,堆棧剩余的最小數(shù)量,這個值越接近0,堆棧溢出的可能越大。 */ unsigned short usStackHighWaterMark; }TaskStatus_t;
注意,這個函數(shù)僅用來調(diào)試用,調(diào)用此函數(shù)會掛起所有任務(wù),直到函數(shù)結(jié)束后才恢復(fù)掛起的任務(wù),因此任務(wù)可能被掛起很長時間。在文件FreeRTOSConfig.h中,宏configUSE_TRACE_FACILITY必須設(shè)置為1,此函數(shù)才有效。
由于我們不使用動態(tài)內(nèi)存分配策略,所以實現(xiàn)定義了最大任務(wù)個數(shù)并預(yù)先分配好了存儲任務(wù)狀態(tài)信息的數(shù)組:
#defineMAX_TASK_NUM 5 TaskStatus_tpxTaskStatusArray[MAX_TASK_NUM];
正確調(diào)用函數(shù)uxTaskGetSystemState()后,任務(wù)的信息會被放在TaskStatus_t結(jié)構(gòu)體中,我們需要將這些信息格式化為容易閱讀的形式,并共通過串口打印到屏幕。完成這些功能的函數(shù)叫做get_task_state(),代碼如下所示:
/*獲取OS任務(wù)信息*/ voidget_task_state(int32_t argc,void *cmd_arg) { const chartask_state[]={'r','R','B','S','D'}; volatile UBaseType_t uxArraySize, x; uint32_t ulTotalRunTime,ulStatsAsPercentage; /* 獲取任務(wù)總數(shù)目 */ uxArraySize = uxTaskGetNumberOfTasks(); if(uxArraySize>MAX_TASK_NUM) { MY_DEBUGF(CMD_LINE_DEBUG,("當(dāng)前任務(wù)數(shù)量過多!\n")); } /*獲取每個任務(wù)的狀態(tài)信息 */ uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime ); #if (configGENERATE_RUN_TIME_STATS==1) MY_DEBUGF(CMD_LINE_DEBUG,("任務(wù)名 狀態(tài) ID 優(yōu)先級 堆棧 CPU使用率\n")); /* 避免除零錯誤 */ if( ulTotalRunTime > 0 ) { /* 將獲得的每一個任務(wù)狀態(tài)信息部分的轉(zhuǎn)化為程序員容易識別的字符串格式 */ for( x = 0; x < uxArraySize; x++ ) { char tmp[128]; /* 計算任務(wù)運行時間與總運行時間的百分比。*/ ulStatsAsPercentage =(uint64_t)(pxTaskStatusArray[ x ].ulRunTimeCounter)*100 / ulTotalRunTime; if( ulStatsAsPercentage > 0UL ) { sprintf(tmp,"%-12s%-6c%-6d%-8d%-8d%d%%",pxTaskStatusArray[ x].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState], pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority, pxTaskStatusArray[ x ].usStackHighWaterMark,ulStatsAsPercentage); } else { /* 任務(wù)運行時間不足總運行時間的1%*/ sprintf(tmp,"%-12s%-6c%-6d%-8d%-8dt<1%%",pxTaskStatusArray[x ].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState], pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority, pxTaskStatusArray[ x ].usStackHighWaterMark); } MY_DEBUGF(CMD_LINE_DEBUG,("%s\n",tmp)); } } MY_DEBUGF(CMD_LINE_DEBUG,("任務(wù)狀態(tài): r-運行 R-就緒 B-阻塞 S-掛起 D-刪除\n")); #endif //#if (configGENERATE_RUN_TIME_STATS==1) }
3.添加到命令解釋列表
在FreeRTOS使用任務(wù)通知實現(xiàn)命令行解釋器一文我們講過了命令表,這里只需要將get_task_state()函數(shù)添加到命令列表中,命令設(shè)置為”task”,代碼如下所示:
/*命令表*/ const cmd_list_structcmd_list[]={ /* 命令 參數(shù)數(shù)目 處理函數(shù) 幫助信息 */ {"?", 0, handle_help, "? -打印幫助信息"}, {"reset", 0, handle_reset, "reset -重啟控制器"}, {"arg", 8, handle_arg, "arg<arg1> <arg2> ... -測試用,打印輸入的參數(shù)"}, {"hello", 0, printf_hello, "hello -打印HelloWorld!"}, {"task", 0, get_task_state, "task -獲取任務(wù)信息"}, };
以上就是FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試的詳細(xì)內(nèi)容,更多關(guān)于FreeRTOS可視化追蹤調(diào)試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
freertos實時操作系統(tǒng)空閑任務(wù)阻塞延時示例解析
這篇文章主要為大家介紹了freertos實時操作系統(tǒng)的空閑任務(wù)及阻塞延時示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)的任務(wù)創(chuàng)建與任務(wù)切換
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)的任務(wù)創(chuàng)建與任務(wù)切換,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)內(nèi)核配置說明
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)內(nèi)核配置及說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)Cortex-M內(nèi)核使用注意事項
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)Cortex-M內(nèi)核使用注意事項,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)結(jié)構(gòu)示例
這篇文章主要介紹了FreeRTOS實時操作系統(tǒng)結(jié)構(gòu)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04FreeRTOS進(jìn)階之任務(wù)創(chuàng)建完全解析
這篇文章主要為大家介紹了FreeRTOS進(jìn)階之任務(wù)創(chuàng)建完全解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04