FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程

Posted 果果小师弟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程相关的知识,希望对你有一定的参考价值。

一、什么是任务

在裸机系统中,系统的主体就是main函数里面顺序执行的无限循环,这个无限循环里面CPU按照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务,也可以称之为线程。

void task1(void *pvParameters)
{
   /* 任务主体,无限循环且不能返回 */
	for(;;)
	{
		/* 任务主体代码 */
		vTaskDelay( 2000 );
	}
}

二、任务状态

FreeRTOS中的任务永远处于下面几个状态中的某一个:

1、运行态

当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

2、就绪态

处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!

3、阻塞态

如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务调用了函数 vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!

4、挂起态

像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数 VTaskSuspendo和 x Task Resumed。

三、任务优先级

每个任务都可以分配一个从0-(configMAX_PRIORITIES-1)的优先级,configMAX_PRIORITIES在文件FreeRTOSConfig.h中有定义,前面我们讲解 FreeRTos系统配置的时候已经讲过了。如果所使用的硬件平台支持类似计算前导零这样的指令(可以通过该指令选择下一个要运行的任务,Cortex-M处理器是支持该指令的),并且宏configUSE_PORT_OPTIMISED_TASK_SELECTION也设置为了1,那么宏configMAX_PRIORITIES不能超过32!也就是优先级不能超过32级。其他情况下宏configMAX_PRIORITIES可以为任意值,但是考虑到RAM的消耗,宏 configMAX_PRIORITIES最好设置为一个满足应用的最小值。

优先级数字越低表示任务的优先级越低,0的优先级最低,configMAX_PRIORITIES-1的优先级最高。空闲任务的优先级最低,为0。

FreeRTOS调度器确保处于就绪态或运行态的高优先级的任务获取处理器使用权,换句话说就是处于就绪态的最高优先级的任务才会运行。当宏configUSE_TIME_SLICING定义为1的时候多个任务可以共用一个优先级,数量不限。默认情况下宏configUSE_TIME_SLICING在文件FreeRTOS.h中已经定义为1。此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。为了方便也可以定义到FreeRTOSConfig.h中。

四、任务控制块

FreeRTOS的每个任务都有一些属性需要存储,FreeRTOS把这些属性集合到一起用一个结构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数 xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。在老版本的FreeRTOS中任务控制块叫做tskTCB,新版本重命名为TCB_t,但是本质上还是tskTCB,本教程后面提到任务控制块的话均用TCB_t表示,此结构体在文件tasks.c中有定义,如下

任务控制块通俗的理解激素是:FreeRTOS的有很多任务对吧,任务1运行到时间片的时间后,赶紧去运行任务2,然后在运行任务3。几个任务一遍跑完了之后,又回过头来在接住上一次运行的地方接着跑,但是接着上一次哪一个地方开始跑呢?是不是需要用一个东西把上次运行的地方信息保存一下,而这个东西就是任务控制块:TCB_t

五、任务堆栈

FreeRTOS之所以能正确的恢复一个仼务的运行就是因为有任务堆栈在保驾护航,任务调度器在进行仼务切换的时候会将当前任务的现场(CPU寄存器值等)保存在此仼务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后仼务就会接着从上次中断的地方开始运行。

创建任务的时候需要给任务指定堆栈,如果使用的函数xTaskCreated创建任务(动态方法)
的话那么任务堆栈就会由函数xTaskCreate()自动创建,后面分析xTaskCreate()的时候会讲解。如果使用函数xTaskCreateStatic()创建仼务(静态方法)的话就需要程序员自行定义仼务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer传递给函数,如下
注意:我们不管是使用函数xTaskCreate()还是xTaskCreateStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为StackType_tStackType_t本质上是uint32_t,在 portmacro.h中有定义,如下:

#define portSTACK_TYPE    uint32_t;//宏定义portSTACK_TYPE为 uint32_t
typedef portSTACK_TYPE   StackType_t;//起别名,然后再给portSTACK_TYPE起一个StackType_t的名字

这里为啥要讲任务的优先级和任务的堆栈呢?

因为我们在创建一个任务是这两点是必须要设置的,不管你动态创建任务还是静态创建任务优先级和堆栈大小都要提前声明。所以这里讲一下,到后面看程序就清楚多了。

#define START_TASK_PRIO		1				//任务优先级
#define START_STK_SIZE 		128  		//任务堆栈大小
TaskHandle_t StartTask_Handler;		//任务句柄

//创建开始任务
xTaskCreate((TaskFunction_t )start_task,            //任务函数
            (const char*    )"start_task",          //任务名称
            (uint16_t       )START_STK_SIZE,        //任务堆栈大小
            (void*          )NULL,                  //传递给任务函数的参数
            (UBaseType_t    )START_TASK_PRIO,       //任务优先级
            (TaskHandle_t*  )&StartTask_Handler);   //任务句柄   

以上是关于FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程的主要内容,如果未能解决你的问题,请参考以下文章

FreeRTOSFreeRTOS学习笔记— 开始创建任务并测试任务代码

FreeRTOSFreeRTOS学习笔记— 学习FreeRTOS的编程风格和本质

FreeRTOSFreeRTOS学习笔记— 任务创建删除挂起和恢复

FreeRTOSFreeRTOS学习笔记— FreeRTOS任务与协程

FreeRTOSFreeRTOS学习笔记(10)— FreeRTOS的osThreadDef创建任务(CMSIS_API)

FreeRTOSFreeRTOS学习笔记(14)— FreeRTOS的消息队列(原生API)