钩子函数
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函数是每次从一个任务中切出时执行。所以通过判断当前是不是空闲任务的进入和退出,并记录空闲任务的开始和结束时间。