FreeRTOS学习笔记(一):任务的基础知识
一、多任务系统
1.1 前后台系统
在学习操作系统之前都是都是裸机开发的,单片机中没有操作系统。裸机开发的特点就是在main函数里进行一个while(1)的大循环,所有的动作都在这个循环里。当有中断来临时,就去执行中断的程序。这种系统称之为前后台系统,中断服务程序为前台,大循环为后台。
前后台系统缺点:就是实时性差,多个任务之间是轮询的执行的,当一些紧急的任务也必须排队等候。
前后台系统优点:资源消耗小。
1.2 多任务抢占系统
多任务系统是多个任务并发处理的,在很小的时间片段执行每个任务,由于时间片段太小导致人们看起来是同时进行的。在多任务抢占系统中,依靠操作系统的任务调度器来决定在某一时刻到底该哪个任务执行。
从上图我们可以看出将任务划分优先级,高优先级任务可以抢夺CPU执行权,用这样的工作模式实现了实时性。
1.3任务的特性
● 简单
● 没有使用限制
● 支持抢占
● 支持优先级
● 每个任务都有堆栈导致了RAM使用量增大
二、任务的状态
FreeRTOS总共有4个状态:运行态、就绪态、阻塞态以及挂起态。
运行态:任务正在运行的状态。
就绪态:已经准备运行,但还没有运行,由于同优先级或者更高优先级的任务在运行
阻塞态:任务等待某一事件发生就处于阻塞态。能够阻塞的条件有:vTaskDelay()函数,任务在等待队列、信号量、事件组、通知或者信号量。
挂起:任务进入挂起以后不能被调度器调用进入运行态,只有解挂后才可以被调度。
三、任务优先级
每个任务都可以被分配一个从0-(configMAX_PRIORITIES-1)的优先级,configMAX_PRIORITIES不能超过32,也就是优先级数不能超过32。
在FreeRTOS系统中,优先级数越小代表优先级越低,configMAX_PRIORITIES-1优先级最高,空闲任务的优先级为0,既是最低。
四、任务控制块
FreeTROS每个任务都有要存储的属性,而FreeTROS用一个结构体来存储这些属性,这个结构体就叫任务控制块:TCB_t,在创建任务时就会分配一个任务控制块。
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; //任务栈顶
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; //MPU相关
#endif
ListItem_t xStateListItem; pended ) //状态列表项
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 )
uint8_t ucStaticallyAllocated; //任务的动态/静态创建
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
typedef tskTCB TCB_t;
五、任务堆栈
任务堆栈是存放任务资源的,任务调度器在切换任务时将当前任务的现场(CPU寄存器值等)保存在任务堆栈中,等待下次运行时从堆栈中拿出保存的数据,恢复之前的状态继续运行。
创建任务时需要个任务指定堆栈,如果使用动态xTaskCreate创建任务的话,任务堆栈会由xTaskCreate函数自动创建;如果使用静态创建xTaskCreateStatic任务的话,需要程序员自己定义任务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer传递给函数,如下:
TaskHandle_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 )
堆栈大小:
不管用什么方式创建任务,任务堆栈大小都是StackType_t,实际上是uint32_t。变量类型位4字节,那么任务实际的堆栈大小就应该是我们所定义的4倍。
参考文献:
FreeRTOS开发手册.左忠凯