任务特性
- 没有数量限制(一个优先级下也可以拥有多个任务)
- 支持抢占(FreeRTOS为抢占式内核高优先级可以抢占低优先级的CPU使用权)
- 支持优先级(决定任务运行的先后)
- 每个任务都拥有堆栈导致了RAM使用量增大
- 抢占需考虑重入问题
任务状态
- 运行态(当前任务正在运行就是运行态)
- 就绪态(任务准备运行,所有就绪态态中优先级最高的任务下一次运行)
- 阻塞态(某个需要事件来引发的任务,事件没有发生时该任务的状态)
- 挂起态(暂停运行,有挂起和解挂两个过程)
这四个状态是可以互相转化的,刚创建好的任务是处于就绪态的,具体的转化过程如下图所示:
任务优先级
任务的优先级决定了任务执行的先后,在FreeRTOS中有32个优先级即0到31,数字越大优先级越高!
任务实现
void vATaskFunction(void *pvParameters)
{
while(1)//for(;;)
{
任务程序
vTaskDelay();
}
vTaskDelay();
}
上方代码为任务实现的基本框架,要注意的是任务中途不能返回或退出,如果一定要退出的话需要调用vTaskDelay(NULL)来删除此任务。
任务控制块(描述任务属性的数据结构)
FreeRTOS 的每个任务都有一些属性需要存储,FreeRTOS 把这些属性集合到一起用一个结
构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数 xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务堆栈栈顶
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGSxMPUSettings; //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;
//新版本的 FreeRTOS 任务控制块重命名为 TCB_t,但是本质上还是 tskTCB,主要是为了兼容
//旧版本的应用。
typedef tskTCB TCB_t;
上述代码为TCB_t结构体在tasks.c 中的定义。
任务堆栈
FreeRTOS是依靠堆栈来回复一个任务的运行的,任务调度器在切换任务的时候就是使用堆栈来恢复任务原现场的。
创建任务的时候需要给任务指定堆栈,如果使用的函数 xTaskCreate()创建任务(动态方法)
的话那么任务堆栈就会由函数 xTaskCreate()自动创建;如果使用函数 xTaskCreateStatic()创建任务(静态方法)的话就需要程序员自行定义任务堆栈,然。后堆栈首地址作为函数的参数 puxStackBuffer 传递给函数。
堆栈的大小
不管是使用函数 xTaskCreate()还是 xTaskCreateStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为 StackType_t,StackType_t 本质上是 uint32_t,在 portmacro.h 中有如下定义
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE long
typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
注意:StackType_t 类型的变量为 4 个字节,那么任务的实际堆栈大小就应该是我们所
定义的 4 倍