文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

FreeRTOS进阶之空闲任务示例完全解析

2024-04-02 19:55

关注

当RTOS调度器开始工作后,为了保证至少有一个任务在运行,空闲任务被自动创建,占用最低优先级(0优先级)。

   xReturn = xTaskCreate( prvIdleTask,
                         "IDLE",configMINIMAL_STACK_SIZE,
                         (void * ) NULL,
                         (tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
                         &xIdleTaskHandle);

空闲任务是FreeRTOS不可缺少的任务,因为FreeRTOS设计要求必须至少有一个任务处于运行状态。我们来看一下空闲任务要做的工作。

1.释放内存

从V9.0版本开始,如果一个任务删除另外一个任务,被删除任务的堆栈和TCB立即释放。如果一个任务删除自己,则任务的堆栈和TCB和以前一样,通过空闲任务删除。所以空闲任务开始就会检查是否有任务删除了自己,如果有的话,空闲任务负责删除这个任务的TCB和堆栈空间。

2. 处理空闲优先级任务

当使用抢占式内核,相同优先级的任务使用时间片方式获得CPU权限。如果有任务与空闲任务共享一个优先级,并且宏configIDLE_SHOULD_YIELD设置为1,那么空闲任务不必等到时间片耗尽再进行任务切换。

所以空闲任务检查空闲优先级下的就绪列表中是否有多个任务,有的话则执行任务切换,让用户任务获得CPU权限。

宏configIDLE_SHOULD_YIELD控制任务在空闲优先级中的行为。仅在满足下列条件后,才会起作用。

通过时间片共享同一个优先级的多个任务,如果共享的优先级大于空闲优先级,并假设没有更高优先级任务,这些任务应该获得相同的处理器时间。

但如果共享空闲优先级时,情况会稍微有些不同。当configIDLE_SHOULD_YIELD为1时,其它共享空闲优先级的用户任务就绪时,空闲任务立刻让出CPU,用户任务运行,这样确保了能最快响应用户任务。处于这种模式下也会有不良效果(取决于你的程序需要),描述如下:

图中描述了四个处于空闲优先级的任务,任务A、B和C是用户任务,任务I是空闲任务。上下文切换周期性的发生在T0、T1…T6时刻。当用户任务运行时,空闲任务立刻让出CPU,但是,空闲任务已经消耗了当前时间片中的一定时间。这样的结果就是空闲任务I和用户任务A共享一个时间片。用户任务B和用户任务C因此获得了比用户任务A更多的处理器时间。

可以通过下面方法避免:

设置configIDLE_SHOULD_YIELD为0将阻止空闲任务为用户任务让出CPU,直到空闲任务的时间片结束。这确保所有处在空闲优先级的任务分配到相同多的处理器时间,但是,这是以分配给空闲任务更高比例的处理器时间为代价的。

3.执行空闲任务钩子函数

空闲任务钩子是一个函数,这个函数由用户来实现,RTOS规定了函数的名字和参数,这个函数在每个空闲任务周期都会被调用。

要创建一个空闲钩子:

void vApplicationIdleHook(void ); 

这个钩子函数不可以调用会引起空闲任务阻塞的API函数(例如:vTaskDelay()、带有阻塞时间的队列和信号量函数),在钩子函数内部使用协程是被允许的。

使用空闲钩子函数设置CPU进入省电模式是很常见的。

4.低功耗tickless模式

通常情况下,FreeRTOS回调空闲任务钩子函数(需要设计者自己实现),在空闲任务钩子函数中设置微处理器进入低功耗模式来达到省电的目的。因为系统要响应系统节拍中断事件,因此使用这种方法会周期性的退出、再进入低功耗状态。如果系统节拍中断频率过快,则大部分电能和CPU时间会消耗在进入和退出低功耗状态上。

FreeRTOS的tickless空闲模式会在空闲周期时停止周期性系统节拍中断。停止周期性系统节拍中断可以使微控制器长时间处于低功耗模式。移植层需要配置外部唤醒中断,当唤醒事件到来时,将微控制器从低功耗模式唤醒。

微控制器唤醒后,会重新使能系统节拍中断。由于微控制器在进入低功耗后,系统节拍计数器是停止的,但我们又需要知道这段时间能折算成多少次系统节拍中断周期,这就需要有一个不受低功耗影响的外部时钟源,即微处理器处于低功耗模式时它也在计时的,这样在重启系统节拍中断时就可以根据这个外部计时器计算出一个调整值并写入RTOS 系统节拍计数器变量中。

空闲任务的源代码如下所示,其中宏portTASK_FUNCTION翻译出来为:

void prvIdleTask(void * pvParameters)。

static portTASK_FUNCTION( prvIdleTask,pvParameters ){  (void ) pvParameters; for(;; ) {  prvCheckTasksWaitingTermination(); #if( configUSE_PREEMPTION == 0 ) {taskYIELD(); } #endif #if( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) {if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) >( UBaseType_t ) 1 ){ taskYIELD();} } #endif #if( configUSE_IDLE_HOOK == 1 ) {externvoid vApplicationIdleHook( void );vApplicationIdleHook(); } #endif #if( configUSE_TICKLESS_IDLE != 0 ) { TickType_txExpectedIdleTime;xExpectedIdleTime= prvGetExpectedIdleTime();if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ){ vTaskSuspendAll(); {  configASSERT(xNextTaskUnblockTime >= xTickCount ); xExpectedIdleTime= prvGetExpectedIdleTime(); if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) {portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime ); } } (void ) xTaskResumeAll();} } #endif }}static portTASK_FUNCTION( prvIdleTask,pvParameters )
{
	 
	 (void ) pvParameters;
	 for(;; )
	 {
		   
		   prvCheckTasksWaitingTermination();
 
		   #if( configUSE_PREEMPTION == 0 )
		   {
				
				taskYIELD();
		   }
		   #endif
		   #if( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
		   {
				
				if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) >( UBaseType_t ) 1 )
				{
						 taskYIELD();
				}
		   }
		   #endif
 
		   #if( configUSE_IDLE_HOOK == 1 )
		   {
				externvoid vApplicationIdleHook( void );
 
				
				vApplicationIdleHook();
		   }
		   #endif
 
		   #if( configUSE_TICKLESS_IDLE != 0 )
		   {
		   TickType_txExpectedIdleTime;
 
				
				xExpectedIdleTime= prvGetExpectedIdleTime();
 
				if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
				{
					 vTaskSuspendAll();
					 {
						   
						   configASSERT(xNextTaskUnblockTime >= xTickCount );
						   xExpectedIdleTime= prvGetExpectedIdleTime();
 
						   if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
						   {
								portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime );
						   }
					 }
					 (void ) xTaskResumeAll();
				}
		   }
		   #endif
	 }
}

以上就是FreeRTOS进阶之空闲任务示例完全解析的详细内容,更多关于FreeRTOS进阶空闲任务的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯