FreeRTOS-钩子函数

目录

钩子函数

Tick滴答钩子

空闲钩子

栈溢出钩子

守护进程钩子

钩子应用场景:获取CPU占用率

utils_cpu.h文件

utils_cpu.c文件

FreeRTOSConfig.h配置文件部分内容

实现原理


钩子函数

Tick滴答钩子

/* FreeRTOSConfig.h */
#define configUSE_TICK_HOOK                      1/* cmsis_os2.h */
/**Dummy implementation of the callback function vApplicationTickHook().
*/
#if (configUSE_TICK_HOOK == 1)__WEAK void vApplicationTickHook (void){}
#endif

vApplicationTickHook()函数在中断服务程序中执行,所以这个函数必须短而有效率,不能加延时,不能大量使用堆栈,也不能调用“FromISR”结尾的API函数。

vApplicationTickHook()函数的运行周期由configTICK_RATE_HZ决定,一般都设置为1ms

/* FreeRTOSConfig.h */
#define configTICK_RATE_HZ                       ((TickType_t)1000)

T = 1/f = 1/1000 = 0.001s = 1ms。 

空闲钩子

/* FreeRTOSConfig.h */
#define configUSE_IDLE_HOOK                      1/* cmsis_os2.h */
/**Dummy implementation of the callback function vApplicationIdleHook().
*/
#if (configUSE_IDLE_HOOK == 1)
__WEAK void vApplicationIdleHook (void){}
#endif

运行周期:没有其它任务时,一直被调用,调用周期非常短。

如果没有其它任务优先级和空闲任务相同,那空闲任务钩子函数里不能阻塞或挂起自身。因为FreeRTOS任何时候都需要有一个任务在运行,否则可能会造成没有任务能够进入运行态。

如果其它任务占用的实际比较少,空闲钩子函数将占用大量的系统时间片资源,则用户可以将一些功能(对时序无要求)在该函数内实现。

栈溢出钩子

/* FreeRTOSConfig.h */
#define configCHECK_FOR_STACK_OVERFLOW                      1    // 或
#define configCHECK_FOR_STACK_OVERFLOW                      2/**Dummy implementation of the callback function vApplicationStackOverflowHook().
*/
#if (configCHECK_FOR_STACK_OVERFLOW > 0)
__WEAK void vApplicationStackOverflowHook (TaskHandle_t xTask, signed char *pcTaskName) {(void)xTask;(void)pcTaskName;
}
#endif

运行周期:任务栈溢出时

并非所有栈溢出都会触发栈溢出钩子函数。栈溢出可能会把系统栈溢出检测代码的数据也给破坏掉了,而导致栈溢出检测失效,这时栈溢出钩子函数也就无法调用了。

参数可以知道哪个任务的栈溢出。

守护进程钩子

/* FreeRTOSConfig.h */
#define configUSE_DAEMON_TASK_STARTUP_HOOK                      1
#define configUSE_TIMER                                         1    // 钩子函数需要Timer任务下实现/* cmsis_os2.h */
/**Dummy implementation of the callback function vApplicationDaemonTaskStartupHook().
*/
#if (configUSE_DAEMON_TASK_STARTUP_HOOK == 1)
__WEAK void vApplicationDaemonTaskStartupHook (void){}
#endif

钩子应用场景:获取CPU占用率

可以统计在一定周期时间内系统执行空闲任务的tick数,就可以获取到CPU空闲率(如1s内空闲任务运行了0.9s,则CPU占用率为10%)。

utils_cpu.h文件

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __UTILS_CPU_H
#define __UTILS_CPU_H#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"/* Private includes ----------------------------------------------------------*//* Exported types ------------------------------------------------------------*//* Exported constants --------------------------------------------------------*//* Exported macro ------------------------------------------------------------*/
#define CALCULATION_PERIOD    1000/* Exported functions prototypes ---------------------------------------------*/
uint16_t osGetCPUUsage (void);/* Private defines -----------------------------------------------------------*/#ifdef __cplusplus
}
#endif#endif /* __MAIN_H *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

utils_cpu.c文件

/* Includes ------------------------------------------------------------------*/
#include "utils_cpu.h"
#include "main.h"
#include "cmsis_os.h" /* _FS_REENTRANT set to 1 and CMSIS API chosen *//* Private includes ----------------------------------------------------------*/
#include "FreeRTOS.h"                   // ARM.FreeRTOS::RTOS:Core
#include "task.h"                       // ARM.FreeRTOS::RTOS:Core/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*//* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/
xTaskHandle    xIdleHandle = NULL;
__IO uint32_t  osCPU_Usage = 0; 
uint32_t       osCPU_IdleStartTime = 0; 
uint32_t       osCPU_IdleSpentTime = 0; 
uint32_t       osCPU_TotalIdleTime = 0; /* Private function prototypes -----------------------------------------------*//* Private user code ---------------------------------------------------------*/
/*** @brief  Application Idle Hook* @param  None * @retval None*/
void vApplicationIdleHook(void) 
{if( xIdleHandle == NULL ){/* Store the handle to the idle task. */xIdleHandle = xTaskGetCurrentTaskHandle();}
}/*** @brief  Application Idle Hook* @param  None * @retval None*/
void vApplicationTickHook (void)
{static int tick = 0;if(tick ++ > CALCULATION_PERIOD){tick = 0;if(osCPU_TotalIdleTime > 1000){osCPU_TotalIdleTime = 1000;}osCPU_Usage = (100 - (osCPU_TotalIdleTime * 100) / CALCULATION_PERIOD);osCPU_TotalIdleTime = 0;}
}/*** @brief  Start Idle monitor* @param  None * @retval None*/
void StartIdleMonitor (void)
{if( xTaskGetCurrentTaskHandle() == xIdleHandle ) {osCPU_IdleStartTime = xTaskGetTickCountFromISR();}
}/*** @brief  Stop Idle monitor* @param  None * @retval None*/
void EndIdleMonitor (void)
{if( xTaskGetCurrentTaskHandle() == xIdleHandle ){/* Store the handle to the idle task. */osCPU_IdleSpentTime = xTaskGetTickCountFromISR() - osCPU_IdleStartTime;osCPU_TotalIdleTime += osCPU_IdleSpentTime; }
}/*** @brief  Stop Idle monitor* @param  None * @retval None*/
uint16_t osGetCPUUsage (void)
{return (uint16_t)osCPU_Usage;
}

FreeRTOSConfig.h配置文件部分内容

/* FreeRTOSConfig.h */
#define configUSE_IDLE_HOOK                      1
#define configUSE_TICK_HOOK                      1#define traceTASK_SWITCHED_IN()  extern void StartIdleMonitor(void); \StartIdleMonitor()
#define traceTASK_SWITCHED_OUT() extern void EndIdleMonitor(void); \EndIdleMonitor()

实现原理

在第一次进入空闲钩子函数时获取空闲任务的句柄到xIdleHandle,之后进入该钩子函数时忽略操作。

Tick滴答钩子函数运行周期由configTICK_RATE_HZ决定,假设设置为1ms。则每秒进一次Tick滴答钩子函数的条件判断。        

traceTASK_SWITCHED_IN()函数是每次切入到一个任务时执行,traceTASK_SWITCHED_OUT()函数是每次从一个任务中切出时执行。所以通过判断当前是不是空闲任务的进入和退出,并记录空闲任务的开始和结束时间。

traceTASK_SWITCHED_IN()函数中获取时间计数器值,traceTASK_SWITCHED_OUT()函数中获取空闲任务一次进入和退出的计数差值,即运行一次空闲任务的计数值,并进行累加。累加值在Tick滴答钩子函数中有使用到。每秒都会进一次Tick滴答钩子函数的条件判断,每次清零累加值。累加值最大1000,CPU占用率 = 1 - 空闲计数 / 1000,1000为1s计数值。代码中osCPU_Usage为CPU占用率(去掉%)。