多任务系统
对于单任务系统,每个任务的优先级都是相同的。多任务将一个大问题分成小问题,每个小任务完成的时间很短,看起来像同时完成,多任务系统通过任务调度器来安排任务优先顺序。FreeROTS根据是一个抢占式的实时多任务系统。高优先级的任务可以打断低优先级任务的运行从而获得CPU的使用权,高优先级的任务执行完成以后重新把CPU的使用权归还给低优先级。
FreeRTOS任务与协程
任务和协程使用不同的API函数,不能通过队列将数据从任务发送协程,或从协程发送给任务。
任务特性
任何一个时间点只能运行一个任务;
每个任务都有自己的运行环境,不依赖于系统中的其他任务或RTOS调度器;
运行哪个任务由RTOS调度器决定。
任务特性:
协程特性
所有协程使用同一个堆栈(对于任务,每个任务都有自己的堆栈);
协程使用合作式的调度器,但可以在使用抢占式的调度器中使用协程;
协程是通过宏定义来实现的;
为了降低对RAM的消耗做了很多限制。
任务状态
运行态:正在运行,当前正在使用处理器的任务。
就绪态:已经准备就绪但是还没有运行的任务。
阻塞态:当前正在等待某个外部事件。任务进入阻塞态会有一个超时时间,超时后会退出阻塞态。
挂起态:挂起态不能被调度器调用进入运行态,也没有超时时间。它是通过函数控制的。进入挂起vTaskSuspend(),退出挂起 xTaskResume()。
任务优先级
每个任务都可以分配一个优先级。
优先级数字越低表示任务的优先级越低。
调度器会选择处于就绪态的最高优先级进行运行。优先级相同时,就会使用时间片轮转调度器获取运行时间。
任务实现
创建任务: xTaskCreate()(动态方法)或xTaskCreateStatic()(静态方法)
任务函数:
本质上就是一个函数,不过任务函数的返回类型一定是void,任务的参数也是void指针类型。
void vATaskFunction(void* pvParameters)
{
for(; ;) //while(1)
{
任务应用程序 //具体要进行的内容
vTaskDelay();
}
vTaskDelete(NULL); //延时函数
}
任务控制块 TCB_t
一个存储任务属性的结构体.。
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 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t * pxEndOfStack;//任务堆栈栈底
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting;//临界区嵌套深度
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
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 )
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; //用来记录任务运行时间
#endif
#if ( ( configUSE_NEWLIB_REENTRANT == 1 ) || ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) )
configTLS_BLOCK_TYPE xTLSBlock; /*< Memory block used as Thread Local Storage (TLS) Block for the task. */
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];//任务通知值
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];//任务通知状态
#endif
/* See the comments in FreeRTOS.h with the definition of
* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated; //用来标记动态创建还是静态创建
#endif
#if ( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
} tskTCB;
任务堆栈
任务调度器在进行任务切换的时候会将当前任务的现场保存在此任务的的任务堆栈中,等到此任务下次运行的时候会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始。和操作系统的上下文切换类似。
如果使用xTaskCreate()创建任务的话,任务堆栈就会由函数xTaskCreate()自动创建;使用函数xTaskCreateStatic()创建任务的话就需要程序员自行定义任务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer传递给函数。