1 FreeRTOS任务基础
1.1 多任务系统
在51单片机、AVR、STM32裸机编程时,我们通常都是用一个main函数里面加一个while(1)做一个大循环来完成所有的事务处理,同时在加上中断处理一些较为紧急的事务。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数
作为前台程序,大循环while(1)作为后台程序,如图 1.1.1 所示:
图1.1.1 前后台系统
前后台系统的实时性较大(尤其是调度任务较多),每个任务都是轮流执行,没有轮到该任务运行的时候,不管该任务有多么的紧急,就只能等着,大家拥有一样的优先级。但是该类系统简单,所消耗的资源较少。
多任务系统可以将一个大问题分成很多个具有共性的小问题,逐一的将这些小问题解决,进而大问题将得到全面的解决,我们可将每一个小问题都视为一个任务。这些小任务是并发处理的,由于他们的执行时间很短,我们所能感觉到的是所有的任务都是同时进行的。那么多任务运行的问题就来了,这就涉及到任务执行的先后顺序及什么任务该执行不该执行了。该模块的功能将由任务调度器来完成,具体如何实现,各类系统是有很大差别的,通常来说我们可分为抢占式(UCos、FreeRTOS)和非抢占式(Linux)。FreeRTOS是一个支持抢占式的实时操作系统,其任务运行如图1.1.2所示:
-
图1.1.2 抢占式多任务系统 -
1.2 FreeRTOS任务
在使用RTOS的时候一个实时应用可以一个独立的任务,每个任务都有自己的运行环境,CPU在任一时间点只能运行一个任务,具体运行哪一个任务将由任务调度器决定。因此,任务调度器将会不断的开启和关闭任务,任务无需了解RTOS调度器的行为,RTOS调度器的功能是确保一个任务在开始执行的时候与上一次退出的时候的运行环境相同(寄存器值、堆栈内容等),这就是上下文切换(Linux中将该过程称为:进程上下文切换)。学过Linux的就会很清楚的知道每一个进程都拥有自己的堆栈,RTOS也是一样,每一个任务都拥有自己独立的堆栈,当任务发生切换时任务调度器就会将其上下文环境保存在堆栈中,等到该任务拿到CPU的使用权时再从其堆栈中取出所保存的上下文环境,继续运行该任务。
RTOS的任务特性:
任务简单,相对于Linux来说该类任务简单很多;
没有使用限制,任务可以运行无数次;
支持抢占和优先级;
每个任务都拥有独立的堆栈,导致RAM必须较大;
1.3 FreeRTOS任务状态
FreeRTOS中的任务状态有运行态、就绪态、阻塞态、挂起态,但任一任务只能处于这几种状态中的一个。
1.3.1 运行态
当一个任务正在运行时(这一时刻该任务的代码在CPU中执行),那么该任务就处于运行态。如果所使用的是单核CPU,那么不管任何时刻只有一个任务处于运行态,这就证明了CPU在某一时刻只能被一个任务拿到使用权。
1.3.2 就绪态
就绪态是指那些已经准备好了的任务,可以随时拿到CPU的使用权,进而进入运行态,但是此时此刻该状态的任务还没有执行,主要是因为当前有一个同优先级或者更高优先级的任务正在运行。
1.3.3 阻塞态
如果一个任务当前正在等待某一外部事件的发生所处于的状态,如:任务正在等待队列、信号量、事件组等都会进入阻塞态。任务进入阻塞态是有一定的时间限制,当超时等待时,该任务将退出阻塞态,进入就绪态,等待拿到CPU的使用权,进入运行态。
1.3.4 挂起态
任务进入挂起的状态和阻塞态一样,将不会被任务调度器所调用,但是出于挂起态的任务是没有超时的问题。在FreeRTOS中,任务进入和退出挂起态只能通过调用VTaskSuspend()和xTaskReume()。
图1.3.1 任务状态间的转换
1.3.5 任务优先级
FreeRTOS中每一个任务都可以都可以分配从 0~(configMAX_PRIORITIES-1) 的 优 先 级 ,configMAX_PRIORITIES 在文件 FreeRTOSConfig.h 中有定义。如果所使用的硬件平台支持类似计算前导零这样的指令(可以通过该指令选 择 下 一 个 要 运 行 的 任 务 , Cortex-M 处 理 器 是 支 持 该 指 令 的 ) , 并 且 宏configUSE_PORT_OPTIMISED_TASK_SELECTION 也 设 置 为 了 1 , 那 么 宏configMAX_PRIORITIES 不能超过 32!也就是优先级不能超过 32 级。其他情况下宏configMAX_PRIORITIES 可以为任意值,但是考虑到 RAM 的消耗,宏 configMAX_PRIORITIES最好设置为一个满足应用的最小值。
优先级的数字越低表示任务的优先级越低,0的任务优先级最低,configMAX_PRIORITIES-1的优先级最高。空闲任务的优先级最低,为0。
1.3.6 任务控制块
任务控制块(Task Contrl Block,简称TCB),FreeRTOS的每一个任务都有一些属性需要存储,所有的信息将存储在一个结构体中,该结构体叫做任务控制块:TCB_t,在使用xTaskCreate()创建任务时将会自动给每一个任务分配一个任务控制块,此结构体在文件task.c中有定义,具体成员如下:
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务堆栈栈顶
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; //MPU 相关设置
#endif
ListItem_t xStateListItem; //状态列表项
ListItem_t xEventListItem; //事件列表项
UBaseType_t uxPriority; //任务优先级
StackType_t *pxStack; //任务堆栈起始地址
char pcTaskName[ configMAX_TASK_NAME_LEN ]; //任务名字
#if ( portSTACK_GROWTH > 0 )
StackType_t *pxEndOfStack; //任务堆栈栈底
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; //临界区嵌套深度
#endif
#if ( configUSE_TRACE_FACILITY == 1 ) //trace 或到 debug 的时候用到
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; //任务基础优先级,优先级反转的时候用到
UBaseType_t uxMutexesHeld; //任务获取到的互斥信号量个数
#endif
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) //与本地存储有关
Void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
#if( configGENERATE_RUN_TIME_STATS == 1 )
uint32_t ulRunTimeCounter; //用来记录任务运行总时间
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent; //定义一个 newlib 结构体变量
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 ) //任务通知相关变量
volatile uint32_t ulNotifiedValue; //任务通知值
volatile uint8_t ucNotifyState; //任务通知状态
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
// 用来标记任务是动态创建的还是静态创建的,
// 如果是静态创建的此变量就为 pdTURE,
// 如果是动态创建的就为 pdFALSE
uint8_t ucStaticallyAllocated;
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
————————————————
版权声明:本文为CSDN博主「楓潇潇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013836909/java/article/details/90259801
来源:oschina
链接:https://my.oschina.net/u/3874841/blog/4305340