-
任务状态
在FreeRTOS中一个任务经创建后会有多个状态,通常可分为以下几种状态:
- 就绪态:新创建的任务一般处于就绪态。处于就绪态的任务表明其已经存在于就绪列表中,其已经具备所有的任务执行需要条件 ,只等待调度器调度运行;
- 运行态:运行态,表明该任务正在占用处理器执行任务 。调度器永远都只会从就绪列表中选取优先级最高任务运行;
- 挂起态:处于挂起态的任务一般都是长时间不允许运行的任务,此时CUP不会处理该任务的任何信息;
- 阻塞态:任务处于阻塞态,说明该任务不在就绪列表中 ,其正在等待某个时序或者是某个外部中断。通常任务挂起 、任务延时 、任务等待信号量都属于阻塞。
既然如此,各个状态间转换关系又是怎么的呢?
- 创建任务--->就绪态1:任务经过创建 函数(
xTaskCreate
/xTaskCreateStatic
)后可以直接进入就绪态; - 就绪态--->运行态2:任务发生切换时,调度器总从就绪列表中选取优先级最高 的任务进入运行态开始运行;
- 运行态--->就绪态3:当有更高优先级任务被创建或恢复 后,调度器就会将新任务变成运行态 ,而原来的任务就会变成阻塞状态 ,直到新任务执行完毕后原任务才会继续运行;看到这大家有没有觉得这玩意有点像中断呢?🤣🤣🤣
- 运行态--->阻塞态4:当正在执行的任务发生阻塞 (挂起、延时、读取信号量等)时,该任务就会变成阻塞态 ,并且任务还会从就绪列表中删除 ,最后调度器会从就绪列表中执行当前优先级最高的任务;
- 阻塞态--->就绪态5:当阻塞任务恢复 (任务恢复、延时超时、读取信号量超时等)后,该任务会变成就绪态 ,并且重新加入到就绪列表 ,此时调度器仍然执行就绪列表中优先级最高的任务;
- 就绪态--->挂起态6:无论任务处于哪个状态一旦调用API(
vTaskSuspend
)任务都会切换到挂起状态 ,挂起任务不会获得CPU使用权,更不会参与调度器的调度 ,挂起后调度器仍然调度就绪列表中优先级最高的任务运行; - 阻塞态--->挂起态7:与6相同;
- 运行态--->挂起态8:与6相同;
- 挂起态--->就绪态9:处于挂起态的任务经任务恢复 (
vTaskResume
)后,任务会切换到就绪态 ,此时调度器仍然调度就序列表中优先级最高的任务运行;
任务函数
启动任务调度器 函数原型
cvoid vTaskStartScheduler(void);
函数参数
无
函数说明 函数
vTaskStartScheduler
是FreeRTOS中一个非常重要的函数,无论是什么样的FreeRTOS程序都需要使用vTaskStartScheduler
函数启动调度器,该函数就像一个总开关,只有将其打开才能够使得FreeRTOS正常工作。静态任务创建 函数原型
cTaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer );
参数解析
- TaskFunction_t pxTaskCode:设置创建任务所需执行的任务函数;
- const char * const pcName:设置任务名字;
- const uint32_t ulStackDepth:设置任务栈长度;
- void * const pvParameters:传递给任务的参数;
- UBaseType_t uxPriority:设置任务优先级;
- StackType_t * const puxStackBuffer:设置任务堆栈;
- StaticTask_t * const pxTaskBuffer:任务控制块;
函数说明 静态创建任务函数
xTaskCreateStatic
,含有七个参数、一个返回值(表示任务是否创建成功)。需要注意的是使用其创建任务时需要传入任务栈。动态任务创建 函数原型
cBaseType_t xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const configSTACK_DEPTH_TYPE usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask);
参数解析
- TaskFunction_t pxTaskCode:设置创建任务所需执行的任务函数;
- const char * const pcName:设置任务名字;
- const configSTACK_DEPTH_TYPE usStackDepth:设置任务栈大小;
- void * const pvParameters:传递任务函数参数;
- UBaseType_t uxPriority:设置任务优先级;
- TaskHandle_t * const pxCreatedTask:保存任务控制块;
函数说明 动态创建任务函数,共含有六个参数、一个返回值(返回值也表示任务是否创建成功),与静态创建任务不同之处是该函数不用传入任务栈,其任务栈使用动态方式创建。
单任务挂起 函数原型
cvoid vTaskSuspend( TaskHandle_t xTaskToSuspend );
参数解析
- TaskHandle_t xTaskToSuspend:任务的控制权柄;
函数说明 通过该函数可以使得某任务进入挂起状态,等待下次任务恢复才有可能继续运行。
多任务挂起 函数原型
cvoid vTaskSuspendAll( void );
参数解析
无
函数说明 通过函数
vTaskSuspendAll
能够使得所有的任务都进入挂起状态 ,看起来将所有任务都挂起了,实际上仅仅锁住调度器 (也就是挂起任务调度器 )。使用该函数虽然锁住了调度器,但系统中断依旧可以正常使用。单任务恢复 函数原型
cvoid vTaskResume( TaskHandle_t xTaskToResume );
参数解析
- TaskHandle_t xTaskToSuspend:任务的控制权柄;
函数说明 通过函数
vTaskResume
可以使由挂起函数vTaskSuspend
挂起的任务重新恢复。 无论同一任务 在挂起时候调用过多少次vTaskSuspend()
函数,也只需调用一次vTaskResume()
函数即可将任务恢复运行;当然,无论调用多少次vTaskResume()
函数 ,也只有在任务是挂起态的时候才进行恢复。多任务恢复 函数原型
cBaseType_t xTaskResumeAll( void );
参数解析
- 返回值:类型为BaseType_t,用于表示恢复任务是否成功;
函数说明 通过函数
xTaskResumeAll
可以使得所有被挂起的任务都恢复。 调度器恢复可以调用xTaskResumeAll()
函数,调用多少次的vTaskSuspendAll()
就要调用多少次xTaskResumeAll()
进行恢复。在中断中恢复任务 函数原型
cBaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
参数解析
- TaskHandle_t xTaskToResume:任务控制权柄;
- 返回值:类型为BaseType_t,用于表示任务恢复是否成功;
函数说明 函数
xTaskResumeFromISR
是在中断中恢复被挂起任务。 使用该函数时必须将INCLUDE_vTaskSuspend
和INCLUDE_vTaskResumeFromISR
都定义为 1 才有效 。但任务还没有处于挂起态 的时候,调用xTaskResumeFromISR()
函数是没有任何意义的。任务删除 函数原型
cvoid vTaskDelete( TaskHandle_t xTaskToDelete );
参数解析
- TaskHandle_t xTaskToDelete:任务的控制句柄;
函数说明 使用函数
vTaskDelete()
前需要将INCLUDE_vTaskDelete
定义为 1;被删除任务可以从所有就绪、阻塞、挂起和事件列表中删除 。 通过函数vTaskDelete()
可以删除某个任务;当删除自身时,传入参数NULL
,但自己的内存并没有得到释放。任务相对延时 函数原型
cvoid vTaskDelay( const TickType_t xTicksToDelay );
参数解析
- const TickType_t xTicksToDelay:延时时间,单位为系统节拍时间 (tick);
函数说明 使用
vTaskDelay()
函数前,必须把INCLUDE_vTaskDelay
定义为 1。函数vTaskDelay()
是相对地阻塞延时 ,调用该函数后,任务将进入阻塞状态 ,并且让出 CPU 资源。延时时长由形参xTicksToDelay
决定,单位为系统节拍周期, 比如系统的时钟节拍周期为 1ms,那么调用vTaskDelay(10)
的延时时间则为10ms,那么经过从调用vTaskDelay()
相对的10ms后任务会解除阻塞,因此,函数vTaskDelay()
不适用于周期性执行任务的场合,并且其他任务与中断也会影响该函数正常工作。任务绝对延时 函数原型
cBaseType_t xTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
参数解析
- TickType_t *pxPreviousWakeTime:指针,指向一个变量,该变量保存任务最后一次解除阻塞的的时刻。第一次使用时,该变量必须初始化为当前时间,之后这个变量会在vTaskDelayUntil()函数内自动更新。
- const TickType_t xTimeIncrement:周 期 循 环 时 间 。 当 时 间 等 于(*pxPreviousWakeTime + xTimeIncrement)时,任务解除阻塞。如果不改变参数 xTimeIncrement 的值,调用该函数的任务会按照固定频率执行。
- 返回值:数据类型为BaseType_t,用于检验任务是否实际延迟的值。
函数说明 在实际使用过程中,一般使用
vTaskDelayUntil
函数,但是它的实现本质上还是依靠函数xTaskDelayUntil
实现。函数重声明如下:c/* * vTaskDelayUntil() is the older version of xTaskDelayUntil() and does not * return a value. */ #define vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) \ do { \ ( void ) xTaskDelayUntil( ( pxPreviousWakeTime ), ( xTimeIncrement ) ); \ } while( 0 )
使用函数
vTaskDelayUntil
前,必须把INCLUDE_vTaskDelayUntil
定义为 1。 函数vTaskDelayUntil()
延时是绝对性的。 功能上看,函数vTaskDelayUntil()
与函数vTaskDelay ()
都是用来实现任务的周期性延时 。但vTaskDelay ()
的延时是相对的,是不确定的,它的延时是 等vTaskDelay ()
调用完毕后开始计算**的。并且vTaskDelay ()
延时时间到了之后,如果有高优先级的任务或者中断正在执行,被延时阻塞的任务并不会马上解除阻塞,所有每次执行任务的周期并不完全确定。而vTaskDelayUntil()
延时是绝对的,适用于周期性执行的任务 。当(*pxPreviousWakeTime +xTimeIncrement)
时间到达后,vTaskDelayUntil()
函数立刻返回,如果此任务是最高优先级的,则任务会立马解除阻塞。
示例
示例1 利用FreeRTOS的空闲任务计时实现LED1、LED2灯闪烁。
- 步骤一:创建两个FreeRTOS的任务,其优先级都设置为1;
- 步骤二:完成两个FreeRTOS任务的函数体,每次执行一次任务后就使用函数
vTaskSuspend
将任务挂起; - 步骤三:完成空闲任务函数函数体,每执行一个函数就将计数值加 1 。当空闲任务执行次数到达指定次数时,就使用函数
vTaskResume
恢复函数。
cvoid task1(void); void task2(void); unsigned int count[2] = {0,0}; /***************************************** * 函数功能:freertos工作函数 * 函数参数:无 * 函数返回值:无 *****************************************/ void freertosWork(void) { /*创建任务两个LED灯闪烁任务*/ //存储创建任务的返回值 BaseType_t xReturn[2] ; //动态创建任务1 xReturn[0] = xTaskCreate( (TaskFunction_t )task1,//任务入口函数 (const char *)"task1",//任务名字 (uint16_t)512,//任务栈大小 (void*)NULL,//任务入口参数 1,//任务优先级 优先级越高,任务优先选越高 &xHandleTsak[0]//任务控制块 ); //动态创建任务2 xReturn[1] = xTaskCreate( (TaskFunction_t )task2,//任务入口函数 (const char *)"task2",//任务名字 (uint16_t)512,//任务栈大小 (void*)NULL,//任务入口参数 1,//任务优先级 优先级越高,任务优先选越高 &xHandleTsak[1]//任务控制块 ); //创建成功 if (pdPASS == xReturn[0] == xReturn[1]) //启动任务,开启调度 vTaskStartScheduler(); //创建失败 else //点亮LED6 changeLedStateByLocation(LED6,ON); return 0; } /***************************************** * 函数功能:freertos的任务1 * 函数参数:无 * 函数返回值:无 *****************************************/ void task1(void) { while(1) { //LED1反转状态 rollbackLedByLocation(LED1); //挂起任务 vTaskSuspend(xHandleTsak[0]); } } /***************************************** * 函数功能:freertos的任务2 * 函数参数:无 * 函数返回值:无 *****************************************/ void task2(void) { while(1) { //LED2状态反转 rollbackLedByLocation(LED2); //挂起任务 vTaskSuspend(xHandleTsak[1]); } } /***************************************** * 函数功能:freertos的空闲任务 * 函数参数:无 * 函数返回值:无 *****************************************/ void vApplicationIdleHook (void) { ++count[0]; if(count[0] % 900000 == 0) //恢复任务1 vTaskResume(xHandleTsak[1]); if(count[0] % 1000000 == 0) //恢复任务0 vTaskResume(xHandleTsak[0]); if(count[0] % 500000 == 0) rollbackLedByLocation(LED8); }
结果 其结果为LED1、LED2根据空闲任务执行次数来切换LED1与LED2的状态;
示例2 利用FreeRTOS的空闲任务计时实现LED1、LED2、LED3轮换灯闪烁。
- 步骤一:创建三个FreeRTOS的任务,其优先级都设置为1;
- 完成三个FreeRTOS任务的函数体,每次执行一次任务后就使用函数
vTaskDelayUntil
将任务周期性阻塞,切换各个任务;
cvoid task1(void); void task2(void); void task3(void); unsigned int count[2] = {0,0}; /***************************************** * 函数功能:freertos工作函数 * 函数参数:无 * 函数返回值:无 *****************************************/ void freertosWork(void) { /*创建任务两个LED灯闪烁任务*/ //存储创建任务的返回值 BaseType_t xReturn[2] ; //动态创建任务1 xReturn[0] = xTaskCreate( (TaskFunction_t )task1,//任务入口函数 (const char *)"task1",//任务名字 (uint16_t)512,//任务栈大小 (void*)NULL,//任务入口参数 1,//任务优先级 优先级越高,任务优先选越高 &xHandleTsak[0]//任务控制块 ); //动态创建任务2 xReturn[1] = xTaskCreate( (TaskFunction_t )task2,//任务入口函数 (const char *)"task2",//任务名字 (uint16_t)512,//任务栈大小 (void*)NULL,//任务入口参数 1,//任务优先级 优先级越高,任务优先选越高 &xHandleTsak[1]//任务控制块 ); //动态创建任务3 xReturn[2] = xTaskCreate( (TaskFunction_t )task3,//任务入口函数 (const char *)"task3",//任务名字 (uint16_t)512,//任务栈大小 (void*)NULL,//任务入口参数 1,//任务优先级 优先级越高,任务优先选越高 &xHandleTsak[2]//任务控制块 ); //创建成功 if (pdPASS == xReturn[0] == xReturn[1] == xReturn[2]) //启动任务,开启调度 vTaskStartScheduler(); //创建失败 else //点亮LED6 changeLedStateByLocation(LED6,ON); return 0; } /***************************************** * 函数功能:freertos的任务1 * 函数参数:无 * 函数返回值:无 *****************************************/ void task1(void) { //保存上次任务执行的时间,调用后会再次刷新时间 static portTickType PreviousWakeTime; const volatile TickType_t xDelay900ms = pdMS_TO_TICKS( 900UL ); //获取当前时间 PreviousWakeTime = xTaskGetTickCount(); while(1) { //LED1反转状态 rollbackLedByLocation(LED1); vTaskDelayUntil( &PreviousWakeTime,xDelay900ms ); } } /***************************************** * 函数功能:freertos的任务2 * 函数参数:无 * 函数返回值:无 *****************************************/ void task2(void) { //保存上次任务执行的时间,调用后会再次刷新时间 static portTickType PreviousWakeTime; const volatile TickType_t xDelay1400ms = pdMS_TO_TICKS( 1400UL ); //获取当前时间 PreviousWakeTime = xTaskGetTickCount(); while(1) { //LED2状态反转 rollbackLedByLocation(LED2); vTaskDelayUntil( &PreviousWakeTime,xDelay1400ms ); } } /***************************************** * 函数功能:freertos的任务3 * 函数参数:无 * 函数返回值:无 *****************************************/ void task3(void) { //保存上次任务执行的时间,调用后会再次刷新时间 static portTickType PreviousWakeTime; const volatile TickType_t xDelay2000ms = pdMS_TO_TICKS( 2000UL ); //获取当前时间 PreviousWakeTime = xTaskGetTickCount(); while(1) { //LED2状态反转 rollbackLedByLocation(LED3); vTaskDelayUntil( &PreviousWakeTime,xDelay2000ms ); } }
结果 FreeRTOS创建的三个任务,分别以900ms、1400ms、2000ms的时间间隔轮流执行,视觉上就是LED1、LED2、LED3轮流闪烁。
小编这里还有一篇关于定时器的文章,也欢迎各位点击观看😉😉😉【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法
最后 ,也欢迎大家留言或私信交流,大家共同进步!😁😁😁
【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法
黑心萝卜三条杠2023-09-01 13:29
相关推荐
Amd7941 天前
在不同操作系统上安装 PostgreSQL你好helloworld2 天前
《操作系统真象还原》第九章(一) —— 在内核空间中实现线程IT 青年3 天前
操作系统(23)外存的存储空间的管理你好helloworld3 天前
《操作系统真象还原》第八章(一) —— 位图及其实现望获linux3 天前
赋能新一代工业机器人-望获实时linux在工业机器人领域应用案例IT 青年3 天前
操作系统(21)文件保护肖老师+4 天前
期末复习.操作系统课后习题答案p@nd@5 天前
Oracle筑基篇-调度算法-LRU的引入肖老师+5 天前
“从零到一:揭秘操作系统的奇妙世界”【操作系统中断和异常】GoGeekBaird6 天前
69天探索操作系统-第20天:页替换算法 - 深度剖析