FreeRTOS-空闲任务及钩子函数

2023-05-16

FreeRTOS-空闲任务及钩子函数

  • FreeRTOS中空闲任务是开启任务调度器自动创建的一个任务,这样可以保证系统中有任务可以运行,这个任务优先级是最低的,如果有其他任务处于就绪态,那么空闲任务就会主动让出CPU使用权给其他任务。那么空闲任务只是为了保证系统中有任务可以运行吗?其实不是,空闲任务其实有两个重要功能,其一,如果某任务调用了vTaskDelete()将自身删除了,那么这个任务的任务控制块和任务堆栈不会同时释放,而是在空闲任务中释放掉,所以空闲任务可以理解为清理垃圾的。其二,由于不是所有的RTOS都有Tickless模式,所以在空闲任务期间可以用来进入低功耗模式,这种方法使用与几乎所以的RTOS系统。下面来看一下空闲任务创建过程及相关函数。

空闲任务创建及函数

vTaskStartScheduler()

  • 空闲任务是在开启任务调度器中创建的,函数定义如下。
void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	{
		StaticTask_t *pxIdleTaskTCBBuffer = NULL;
		StackType_t *pxIdleTaskStackBuffer = NULL;
		uint32_t ulIdleTaskStackSize;

	
		vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
		xIdleTaskHandle = xTaskCreateStatic(	prvIdleTask,
												"IDLE",
												ulIdleTaskStackSize,
												( void * ) NULL,
												( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
												pxIdleTaskStackBuffer,
												pxIdleTaskTCBBuffer ); 
		
		xReturn = xTaskCreate(	prvIdleTask,
								"IDLE", configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
								&xIdleTaskHandle ); 
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */
   //其余部分省略
   ...
   ...
	( void ) xIdleTaskHandle;
}
  • 从上面空闲任务创建可以看出,空闲任务函数名为prvIdleTask,接下来我们看一下该函数中进行了什么操作。

prvIdleTask()

  • 实际上,在寻找该函数的时候是找不到的,因为该函数是通过宏定义来实现的,其定义如下。
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )

  • 真正调用的函数是portTASK_FUNCTION(),函数定义如下。
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
	/* Stop warnings. */
	( void ) pvParameters;
	for( ;; )
	{
		prvCheckTasksWaitingTermination();----1

		#if ( configUSE_PREEMPTION == 0 )----2
		{
			taskYIELD();
		}
		#endif /* configUSE_PREEMPTION */

		#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )----3
		{
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 )----4
			{
				taskYIELD();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */

		#if ( configUSE_IDLE_HOOK == 1 )----5
		{
			extern void vApplicationIdleHook( void );
			vApplicationIdleHook();
		}
		#endif /* configUSE_IDLE_HOOK */


		#if ( configUSE_TICKLESS_IDLE != 0 )
		{
		TickType_t xExpectedIdleTime;

			xExpectedIdleTime = prvGetExpectedIdleTime();----6

			if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )----7
			{
				vTaskSuspendAll();
				{

					configASSERT( xNextTaskUnblockTime >= xTickCount );
					xExpectedIdleTime = prvGetExpectedIdleTime();----8

					if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )----9
					{
						traceLOW_POWER_IDLE_BEGIN();
						portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
						traceLOW_POWER_IDLE_END();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICKLESS_IDLE */
	}
}
  1. 检查是否有已经删除的函数没有释放内存,该函数会在下面分析
  2. 如果没有使用抢占式内核,在这里进行一次任务调度
  3. 如果使用了抢占式内核,并且需要进行任务调度
  4. 判断是否有准备好的任务,如果有则进行任务调度
  5. 如果使用了钩子函数,则调用钩子函数
  6. 如果开启了低功耗模式,则计算下一次唤醒时间
  7. 判断唤醒时间是否有效,即是否大于2个时钟节拍
  8. 再次获取唤醒时间
  9. 再次判断,如果仍旧成立,则调用函数portSUPPRESS_TICKS_AND_SLEEP()进入低功耗模式
  • 从这里我们就可以知道,内存清除、钩子函数调用、低功耗模式的进入都是在空闲任务中进行的。下面来分析prvCheckTasksWaitingTermination()函数

prvCheckTasksWaitingTermination()

  • 函数定义如下。
static void prvCheckTasksWaitingTermination( void )
{
	#if ( INCLUDE_vTaskDelete == 1 )
	{
		BaseType_t xListIsEmpty;
		while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U )---1
		{
			vTaskSuspendAll();
			{
				xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );---2
			}
			( void ) xTaskResumeAll();

			if( xListIsEmpty == pdFALSE )---3
			{
				TCB_t *pxTCB;

				taskENTER_CRITICAL();
				{
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );---4
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );---5
					--uxCurrentNumberOfTasks;---6
					--uxDeletedTasksWaitingCleanUp;---7
				}
				taskEXIT_CRITICAL();

				prvDeleteTCB( pxTCB );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
	#endif /* INCLUDE_vTaskDelete */
}
  1. 判断是否还有被删除了但没有释放内存的任务
  2. 判断等待释放内存的任务列表是否为空
  3. 如果等待释放内存的任务列表不为空
  4. 获取列表头指针
  5. 将任务从列表中移除
  6. 系统中任务数量-1
  7. 等待释放内存的任务数量-1

钩子函数

  • FreeRTOS中钩子函数如下表所示
描述
configUSE_IDLE_HOOK空闲任务钩子函数,空闲任务执行时会调用
configSUE_TICK_HOOK时间片钩子函数,xTaskIncrementTick()会调用该函数
configUSE_MALLOC_FAILED_HOOK内存申请失败钩子函数,使用pvPortMalloc()申请内存失败的时候会调用该函数
configUSE_DAEMON_TASK_STARTUP_HOOK定时器服务任务钩子函数
  • 这里我们仅就空闲任务钩子函数做一个用法示例,其他函数用法类似。使用空闲任务钩子函数的时候,当调用该函数的时候使系统进入低功耗模式,这种低功耗模式相对于tickless模式效果要差些,但是几乎适用于所有RTOS系统。其运行对比如下图所示。
    在这里插入图片描述
  • 从上面可以看出,调用空闲任务钩子函数实现的低功耗模式,在每个系统节拍处必然不在低功耗模式状态,这个即是没有其他任务处于就绪态,系统也会被反复唤醒。而FreeRTOS提供的tickless模式则要好很多。
  • 下面是通过空闲任务进入低功耗模式配置的源码
#define configUSE_IDLE_HOOK						1                       

//进入低功耗模式前的工作
void PreSleepProcessing(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,DISABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,DISABLE);
}

//退出低功耗模式后的工作
void PostSleepProcessing(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
}

void vApplicationIdleHook( void )
{
	__disable_irq();
	__dsb( portSY_FULL_READ_WRITE );
	__isb( portSY_FULL_READ_WRITE );
	PreSleepProcessing();
	__dsb( portSY_FULL_READ_WRITE );
	__wfi();
	__isb( portSY_FULL_READ_WRITE );
	PostSleepProcessing();
	__enable_irq();
}

  • 下面仅就vApplicationIdleHook()中的代码做一个简要分析。首先调用__disable_irq()函数关闭系统中断请求,然后调用PreSleepProcessing()做进入睡眠模式前的一些低功耗处理,这里仅关闭了GPIO的时钟,接着调用__wfi()进入WFI低功耗模式,当调用PostSleepProcessing()的时候说明已经退出低功耗模式了,然后恢复外设时钟,调用__enable_irq();开启中断请求

  • 至此就将FreeRTOS空闲任务及钩子函数讲述完了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS-空闲任务及钩子函数 的相关文章

随机推荐

  • 初学四旋翼之定高

    本项目使用US 100超声波模块测高 xff0c 与飞控的通讯方式为UART 硬件连接应注意 xff1a 通常飞控的发送管脚连超声波的接收管脚 xff0c 飞控的接收管脚连超声波的发送管脚 xff08 即tx rx xff1b rx tx
  • 初学四旋翼之光流定点

    本项目使用px4flow模块测速 xff0c 与飞控的通讯方式为I2C 安装时因注意光流模块与飞控的方向 xff08 一 xff09 为什么使用光流模块 xff1f 在悬停时 xff0c 若采用开环控制 xff0c 由于一些不可控的外界因素
  • 初学JetsonTX2之部署YOLO

    本人准备使用 YOLO进行人脸检测 xff0c 硬件设备为 Jetson TX2 查阅 YOLO 官网 xff0c 要部署 YOLO xff0c 首先要安装 CUDA CUDNN OPENCV xff0c 然后部署 Darknet xff0
  • C语言,超过10位数的字符串转整型函数

    include lt stdio h gt static long str2int const char str long temp 61 0 const char p 61 str if str 61 61 NULL return 0 i
  • C语言去掉MAC地址中的冒号

    include lt stdio h gt include lt string h gt void strdel char s char del x char p char q for p 61 s q 61 s p 61 39 0 39
  • Jetson Xavier NX 套件将系统装到SSD

    目录 第一步 xff1a 虚拟机 第二步 xff1a 装SDK Manager 第三步 xff1a 将系统装到eMMC 第四步 xff1a 将系统装到SSD内 xff0c 我以新买的500G硬盘为例 第五步 xff1a 装各种库 解决问题时
  • MySQL使用.ibd文件恢复或者迁移数据库

    使用86的Alice数据库的 ibd文件备份 恢复到76数据库 xff0c 该数据库版本为8 0 17 1 创建一个表确认与原始表结构一致 将86数据库的表结构导出 xff0c 在76上执行 xff08 注 xff1a 在5 5 26版本需
  • 学习ARM反汇编工具objdump和一个简单实例

    学习ARM反汇编工具objdump和一个简单实例 参考朱有鹏ARM裸机编程 1 反汇编的原理 amp 为什么需要反汇编 arm linux objdump D led elf gt led elf dis objdump是gcc工具链中的反
  • 从零开始学习UCOSII操作系统1--UCOSII的基础知识

    从零开始学习UCOSII操作系统1 UCOSII的基础知识 前言 xff1a 首先比较主流的操作系统有UCOSII FREERTOS LINUX等 xff0c UCOSII的资料相对比其余的两个操作系统的资料是多很多的 更重要的原因是自己本
  • 从零开始学习UCOSII操作系统2--UCOSII的内核实现

    从零开始学习UCOSII操作系统2 UCOSII的内核实现 参考书籍 xff1a 嵌入式实时操作系统 COS II原理及应用 嵌入式实时操作系统uCOS II 邵贝贝 第二版 1 任务的结构 任务控制块 首先这个任务控制块是非常的大的 xf
  • 从零开始学习UCOSII操作系统4--任务管理

    从零开始学习UCOSII操作系统4 任务管理 1 重讲任务 1 任务可以是一个无限的循环 xff0c 也可以在一次执行完毕后被删除 这里需要注意的是 xff0c 任务的代码并不是真正的删除了 xff0c 而是UCOSII不再理会该任务代码
  • 从零开始学习UCOSII操作系统7--信号量

    从零开始学习UCOSII操作系统7 信号量 参考博客 xff1a 64 http blog csdn net gatiemehttps blog csdn net gatieme article details 21071379 前言 xf
  • 从零开始学习UCOSII操作系统15--总结篇

    从零开始学习UCOSII操作系统15 总结篇 前言 xff1a 在大学的时候 xff0c 我们班级上面都有很多人觉得学习UCOSII 包括UCOSIII 是没什么厉害的 xff0c 因为很多人都喜欢去学习Linux操作系统 xff0c 但是
  • 手把手教你搭建TFTP服务器

    手把手教你搭建TFTP服务器 前言 xff0c 东西来自于网络 xff0c 但是根据自己的理解写了一下建议 xff0c 记录下来 xff0c 让下次不要在网络上面浪费时间搜索 1 保证自己的虚拟机能够上网 测试方法 xff1a 里面一般都有
  • 从零开始写一个单向不循环链表

    从零开始写一个单向不循环链表 总结 xff1a 郝斌数据结构与算法课程 数据结构概述 xff1a 定义 xff1a 我们如何把现实中大量的而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器 xff08 内存 xff09 中 xff0
  • STM32-CAN通信协议

    STM32 CAN通讯协议 CAN协议简述 CAN Controller Area Network xff08 控制器局域网 xff09 xff0c 由Bosch开发的一种面向汽车的通信协议 这是目前应用最广泛的通信协议 xff0c 更是尤
  • FreeRTOS-任务运行时间统计

    FreeRTOS 任务运行时间统计 引入 上一章节中我们讲述了任务信息获取 xff0c 我们已经能够获取绝大部分任务信息了 xff0c 但是任务还有一个很重要的信息 xff0c 那就是运行时间 如果我们知道了每个任务的运行时间和占比我们就可
  • 【Linux】解决Nvidia Jetson Xavier NX开发套件开机启动时间过长问题

    环境 硬件 xff1a Jetson Xavier NX 套件 系统 xff1a Ubuntu 20 04 解决 0 现象 在使用Nvidia 的Jetson Xavier NX套件 xff0c 开发产品 xff0c 准备发布时 xff0c
  • FreeRTOS-信号量

    FreeRTOS 信号量 信号量其实就是队列的一种应用 xff0c 信号量的各种操作都是在队列的基础上建立起来的 那么既然是在队列的基础上建立的 xff0c 信号量一定具有和队列相同的属性 因此信号量也是为任务和任务 任务和中断之间通信做准
  • FreeRTOS-空闲任务及钩子函数

    FreeRTOS 空闲任务及钩子函数 FreeRTOS中空闲任务是开启任务调度器自动创建的一个任务 xff0c 这样可以保证系统中有任务可以运行 xff0c 这个任务优先级是最低的 xff0c 如果有其他任务处于就绪态 xff0c 那么空闲