在MCU on Eclipse网站上看到Erich Styger在8月2日发的博文,一篇关于在Amazon FreeRTOS V10中使用运行时统计信息的文章,本人觉得很有启发,特将其翻译过来以备参考。原文网址:https://mcuoneclipse.com/2018/08/02/tutorial-using-runtime-statistics-with-amazon-freertos-v10/
FreeRTOS包含一个很好的功能,可以向我提供有关每个任务在系统上运行的时间的信息:
FreeRTOS运行时信息
本教程解释了FreeRTOS运行时统计功能以及如何打开和使用它。
?软件和工具
在本文中,我使用以下内容:
- MCUXpresso IDE 10.2.1
- FRDM-K64F板
- 来自MCUXpresso SDK 2.4.0的Amazon FreeRTOS V10.0.1
但是当然可以使用任何其他工具/ IDE或FreeRTOS版本(FreeRTOS至少应该是9.0.0或更高版本)。
使用以下步骤,还可以使用FreeRTOS任务运行时信息收集来更新现有项目。
?怎么运行的
FreeRTOS使用用户/应用程序特定的计时器来测量任务执行时间。为此,RTOS中的每个任务描述符都有一个累积计数器,用于添加为该任务花费的计时器滴答。当任务获得CPU时间时,当前计时器滴答计数被记忆,当RTOS切换出该任务时,则记忆当前计时器滴答计数。然后将对应于任务执行时间的增量时间添加到任务执行时间计数器。
我需要配置FreeRTOS,并将以下宏设置为1以执行运行时分析:
1 #define configGENERATE_RUN_TIME_STATS 1
另外,需要提供以下两个宏:
1 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 2 portGET_RUN_TIME_COUNTER_VALUE()
RTOS使用它来配置运行时计数器计时器并获取计时器值。
运行时计数器在每个任务描述符中存储为32位值,这意味着对于每个任务,我对系统的RAM要求将增加4个字节:
FreeRTOS TCB中的ulRunTimeCounter
假设计数器周期为10 kHz(0.1 ms),这意味着变量将在大约5天后溢出。
此外,RTOS在task.c中维护额外的全局变量:
1 #if ( configGENERATE_RUN_TIME_STATS == 1 ) 2 3 PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ 4 5 PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ 6 7 #endif
第一个变量用于记住任务切换的时间,第二个变量是系统的总运行时间。这是在任务切换时发生的事情(内核函数vTaskSwitchContext):
1 /* Add the amount of time the task has been running to theaccumulated time so far. The time the task started running wasstored in ulTaskSwitchedInTime. Note that there is no overflowprotection here so count values are only valid until the timeroverflows. The guard against negative values is to protectagainst suspect run time stat counter implementations - whichare provided by the application, not the kernel. */ 2 3 if( ulTotalRunTime > ulTaskSwitchedInTime ) 4 5 { 6 7 pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); 8 9 } 10 11 else 12 13 { 14 15 mtCOVERAGE_TEST_MARKER(); 16 17 } 18 19 ulTaskSwitchedInTime = ulTotalRunTime;
通常,周期性定时器中断用于计算执行时间,并且定时器频率应该是嘀嗒中断频率的大约10倍(比如说“Hello”到“奈奎斯特 - 香农”采样定理)。这意味着如果我的滴答中断是1 kHz,我的运行时分析定时器频率应该是10 kHz。
运行时统计信息通常带有两个数字:
- 绝对(时间)数字
- 百分比
下面是一个文本任务列表,其中包含右侧的运行时信息:
TCB Static Handle Name State Prio Stack Beg Stack End Size Stack Top Unused Runtime
1 no (0) 0x20000450 Shell Running (1,1) 0x20000440 0x20000060 1000 B 0x200001EC ( 600 B) 392 B 0x00000000 ( <1%)
7 no (0) 0x20001E68 IDLE Ready (0,0) 0x20001E58 0x20001CD0 400 B 0x20001DFC ( 96 B) 312 B 0x00007C35 ( 91%)
2 no (0) 0x20000740 Refl Blocked (4,4) 0x20000730 0x20000510 552 B 0x200006BC ( 120 B) 384 B 0x00000C6E ( 9%)
6 no (0) 0x20001C68 Main Blocked (1,1) 0x20001C58 0x20001A08 600 B 0x20001BDC ( 128 B) 356 B 0x00000000 ( <1%)
3 no (0) 0x20001378 Radio Blocked (3,3) 0x20001368 0x20000F88 1000 B 0x200012F4 ( 120 B) 680 B 0x00000001 ( <1%)
4 no (0) 0x20001658 Sumo Blocked (2,2) 0x20001648 0x20001458 504 B 0x200015C4 ( 136 B) 360 B 0x00000000 ( <1%)
5 no (0) 0x20001948 Drive Blocked (3,3) 0x20001938 0x20001748 504 B 0x200018B4 ( 136 B) 264 B 0x00000000 ( <1%)
绝对数字是运行时间计时器滴答数(TCB中的ulRunTimeCounter)以及此计数器相对于总运行时间的百分比(task.c中的ulTotalRunTime)。
对于IDLE任务,它显示了这一点:
TCB Static Handle Name State Prio Stack Beg Stack End Size Stack Top Unused Runtime
7 no (0) 0x20001E68 IDLE Ready (0,0) 0x20001E58 0x20001CD0 400 B 0x20001DFC ( 96 B) 312 B 0x00007C35 ( 91%)
0x7C35是定时器计数器(在本例中使用0.1 ms定时器,因此它意味着IDLE任务运行约3秒(0x7C35 / 10 => 3179 ms)并使用91%的运行时间。
问题可能是:中断花费的时间是多少?答案是RTOS不知道中断,它只知道任务使用了多少运行时间计时器。或者换句话说:FreeRTOS运行时计数器显示的运行时* includes*中断的时间。
?教程:使用FreeRTOS进行运行时分析
在下一节中,我将展示如何使用FreeRTOS启用运行时分析。基本步骤是:
- 创建一个新项目(如果尚未存在)
- 更新FreeRTOSConfig.h
- 初始化和配置计时器
- 添加钩子/回调到应用程序
1、创建项目
创建一个基于你的目标板的项目:
项目创建的目标板
确保包含FreeRTOS:
项目的FreeRTOS选项
2、添加FreeRTOS任务
接下来添加一个任务,例如:
1 #include "FreeRTOS.h" 2 3 #include "task.h" 4 5 6 7 static void MyTask(void *pvParameters) { 8 9 for(;;) { 10 11 vTaskDelay(pdMS_TO_TICKS(100)); 12 13 } 14 15 }
在main()内部,创建一个任务并启动调度程序:
1 /* create task */ 2 3 if (xTaskCreate(MyTask, "MyTask", 500/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+1, NULL) != pdPASS) { 4 5 for(;;){} /* error */ 6 7 } 8 9 vTaskStartScheduler(); /* start RTOS */ 10 11 for(;;) { 12 13 /* should not get here */ 14 15 }
添加FreeRTOS任务
构建和调试该项目,只是为了确保一切正常。
调试FreeRTOS任务
要在Debug视图中显示FreeRTOS线程,请参阅https://mcuoneclipse.com/2018/06/29/show-freertos-threads-in-eclipse-debug-view-with-segger-j-link-and-nxp-s32-design-studio/
但是FreeRTOS任务列表(使用Menu FreeRTOS>任务列表来显示该视图)不显示任何运行时信息:
FreeRTOS任务列表,没有运行时信息
这是我们将在接下来的步骤中添加的内容。
3、跟踪和运行时统计信息
在FreeRTOSConfig.h中,确保将以下定义设置为1(打开):
1 #define configGENERATE_RUN_TIME_STATS 1 /* 1: generate runtime statistics; 0: no runtime statistics */ 2 3 #define configUSE_TRACE_FACILITY 1 /* 1: include additional structure members and functions to assist with execution visualization and tracing, 0: no runtime stats/trace */
启用运行时统计信息和跟踪功能
该configUSE_TRACE_FACILITY需要使用RTOS有在任务描述当前存储的运行时间计数器的附加数据元素,在configGENERATE_RUN_TIME_STATS自动关上,以创纪录的任务执行时间的功能。
4、配置定时器
接下来,我们必须设置一个计时器来测量任务执行时间。该计时器的运行速度至少比RTOS Tick计时器快10倍。
在我们的示例中,滴答率为1 kHz:
1 #define configTICK_RATE_HZ ((TickType_t)1000)
这意味着我们的运行时间应至少以10 kHz运行。
要配置这样的计时器,我可以使用MCUXpresso配置外设工具:
外围设备工具
在外设工具中,我们选择了FTM0定时器(我们也可以使用任何其他定时器)。
添加FTM0
定时器配置为10 kHz:
定时器输出频率
我们将使用定时器中断来增加一个计数器,所以不要忘记打开中断:
定时器溢出中断使能
然后单击按钮以更新项目源:
更新项目
切换回开发人员视角。
5、定时器ISR
接下来,我们将定时器中断代码添加到应用程序:
1 #include "fsl_ftm.h" 2 3 static uint32_t RTOS_RunTimeCounter; /* runtime counter, used for configGENERATE_RUNTIME_STATS */ 4 5 6 7 void FTM0_IRQHandler(void) { 8 9 /* Clear interrupt flag.*/ 10 11 FTM_ClearStatusFlags(FTM0, kFTM_TimeOverflowFlag); 12 13 RTOS_RunTimeCounter++; /* increment runtime counter */ 14 15 }
6、添加定时器驱动
该项目尚未编译,因为必要的驱动程序尚未成为项目的一部分。要添加它们,请使用“管理SDK组件”按钮:
管理SDK组件按钮
然后检查ftm驱动程序并按OK,将额外的驱动程序源添加到项目中。
FTM驱动程序
7、向FreeRTOS添加定时器:用于运行时统计的FreeRTOS定时器宏
将以下行添加到FreeRTOSConfig.h:
1 extern void RTOS_AppConfigureTimerForRuntimeStats(void); 2 3 #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() RTOS_AppConfigureTimerForRuntimeStats() 4 5 extern uint32_t RTOS_AppGetRuntimeCounterValueFromISR(void); 6 7 #define portGET_RUN_TIME_COUNTER_VALUE() RTOS_AppGetRuntimeCounterValueFromISR()
这告诉FreeRTOS它将用于初始化定时器的功能以及获取定时器值的功能。
向FreeRTOSConfig.h添加了运行时计数器设置
8、FreeRTOS Callback for Timer
现在我们需要添加我们配置FreeRTOS使用的两个回调。
1 void RTOS_AppConfigureTimerForRuntimeStats(void) { 2 3 RTOS_RunTimeCounter = 0; 4 5 EnableIRQ(FTM0_IRQn); 6 7 } 8 9 10 11 uint32_t RTOS_AppGetRuntimeCounterValueFromISR(void) { 12 13 return RTOS_RunTimeCounter; 14 15 }
9、正在运行...。
构建和调试您的应用程序。如果您现在停止应用程序并检查任务列表,它现在显示运行时信息:
显示运行时信息
10、没有Eclipse?没问题!
上面我使用了FreeRTOS的Eclipse Task List视图,这是NXP为他们的基于Eclipse的IDE(MCUXpresso IDE,S32DS for ARM和Kinetis Design Studio)所做的事情。但是可以直接从应用程序显示该信息,例如在终端LCD显示器上。McuOnEclipse上的FreeRTOS 包含一个使用它的shell /终端接口。
控制台上的任务运行时信息
下面的代码片段显示了如何为每个任务打印信息:
1 #if configGENERATE_RUN_TIME_STATS 2 3 ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE(); /* get total time passed in system */ 4 5 ulTotalTime /= 100UL; /* For percentage calculations. */ 6 7 #endif 8 9 ... 10 11 #if configGENERATE_RUN_TIME_STATS && configUSE_TRACE_FACILITY 12 13 /* runtime */ 14 15 UTIL1_strcpy(tmpBuf, sizeof(tmpBuf), (unsigned char*)"0x"); 16 17 UTIL1_strcatNum32Hex(tmpBuf, sizeof(tmpBuf), taskStatus.ulRunTimeCounter); 18 19 if (ulTotalTime>0) { /* to avoid division by zero */ 20 21 /* What percentage of the total run time has the task used? 22 23 This will always be rounded down to the nearest integer. 24 25 ulTotalRunTime has already been divided by 100. */ 26 27 ulStatsAsPercentage = taskStatus.ulRunTimeCounter/ulTotalTime; 28 29 if (ulStatsAsPercentage>0) { 30 31 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)" ("); 32 33 UTIL1_strcatNum16uFormatted(tmpBuf, sizeof(tmpBuf), ulStatsAsPercentage, ‘ ‘, 3); 34 35 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)"%)"); 36 37 } else { 38 39 /* If the percentage is zero here then the task has consumed less than 1% of the total run time. */ 40 41 UTIL1_strcat(tmpBuf, sizeof(tmpBuf), (unsigned char*)" ( <1%)"); 42 43 } 44 45 } 46 47 buf[0] = ‘\0‘; 48 49 UTIL1_strcatPad(buf, sizeof(buf), tmpBuf, ‘ ‘, PAD_STAT_TASK_RUNTIME); 50 51 CLS1_SendStr(buf, io->stdOut); 52 53 #endif 54 55 CLS1_SendStr((unsigned char*)"\r\n", io->stdOut);
?概要
FreeRTOS运行时统计是一个非常有用的功能:它显示每个任务使用了多少时间,包括其中断时间。我需要的只是设置一些FreeRTOS配置宏并设置周期性定时器中断。当然,这并不是免费提供额外的定时器中断以及该功能所需的RAM和FLASH,但如果需要,它可以很容易地关闭以供最终版本使用。
?链接
- GitHub上的示例项目:https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K64F/FRDM-K64F_SDK_FreeRTOS
- 使用FreeRTOS进行性能和运行时分析
- MCUXpresso IDE网页:http://www.nxp.com/mcuxpresso/ide
- MCUXpresso IDE社区:http://www.nxp.com/mcuxpresso/ide/forum
- 在Eclipse中更好的FreeRTOS调试
- McuOnEclipse库项目:https://github.com/ErichStyger/McuOnEclipseLibrary/tree/master/lib/FreeRTOS/Source
- ARM Cortex-M循环计数器:https://mcuoneclipse.com/2017/01/30/cycle-counting-on-arm-cortex-m-with-dwt/
- 更好的FreeRTOS调试:https://mcuoneclipse.com/2017/03/18/better-freertos-debugging-in-eclipse/
- FreeRTOS RuntimeStats:https://www.freertos.org/rtos-run-time-stats.html
欢迎关注:
原文地址:https://www.cnblogs.com/foxclever/p/9459691.html