一、task简介
应用程序的运行基础是task,通常一个应用的正常工作得益于多个task的共同运行。其类似与linux中的线程。下面将对freertos中的task做简要介绍。
二、task 状态
FreeRTOS 中的任务永远处于下述任一状态:
1 运行态
当一个任务正在运行时, 那么就说这个任务处于运行态, 处于运行态的任务就是当前正在使用处理器的任务。
2就绪态
处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起), 可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行。
3 阻塞态
如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态, 比如说如果某个任务调用了函数 vTaskDelay()的话就会进入阻塞态, 直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态,如果等待的事件到了就会退出阻塞态进入就绪态。
任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!
4挂起态
像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态, 但是进入挂起态的
任务没有超时时间。任务进入和退出挂起态是通过调用函数 vTaskSuspend()和 xTaskResume()来完成的。
状态转移图如下所示:
三.task 优先级
每个任务均被指定一个优先级,通过优先级指定任务的运行顺序。优先级数字越低表示任务的优先级越低, 0 的优先级最低。空闲任务的优先级最低,为 0。
FreeRTOS 调度器确保处于就绪态或运行态的高优先级的任务优先获取处理器使用权,换言之,即处于就绪态的最高优先级的任务才会运行。 而处于就绪态的优先级相同的任务使用时间片轮转调度的方式获取运行时间。
四. task 控制块
在freertos中,每个task都需要维护一些属性信息,这些信息集合到一个结构体中,叫task 控制块(TCB_t)。在使用函数 xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。
typedef struct tskTaskControlBlock
{
volatile StackType_t * pxTopOfStack; /** 任务堆栈的栈顶指针 */
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings; /**< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
#endif
ListItem_t xStateListItem; /**task 状态列表 ((Ready, Blocked, Suspended )) */
ListItem_t xEventListItem; /** task 事件列表. */
UBaseType_t uxPriority; /**< task 优先级. 0 is the lowest priority. */
StackType_t * pxStack; /**< task 堆栈起始地址. */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /** task neme */
#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
BaseType_t xPreemptionDisable; /**< Used to prevent the task from being preempted. */
#endif
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t * pxEndOfStack; /**< task 堆栈栈底指针. */
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting; /**< 临界区嵌套深度. */
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber; /**< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */
UBaseType_t uxTaskNumber; /**< Stores a number specifically for use by third party trace code. */
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority; /**< The priority last assigned to the task - used by the priority inheritance mechanism. */
UBaseType_t uxMutexesHeld; /** task 的互斥信号量个数*/
#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; /** task 运行的时间片数量. */
#endif
#if ( 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 ) /** task 通知相关变量*/
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; /** task 通知值*/
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; /** task 通知状态*/
#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; /**< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
} tskTCB;
/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
* below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;
五. task 堆栈
freertos中任务调度器在进行任务切换时会将当前任务的现场(CPU 寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行。
创建任务的时候需要给任务指定堆栈,如果使用的函数 xTaskCreate()创建任务(动态方法)的话那么任务堆栈就会由函数 xTaskCreate()自动创建。
六. 创建task
1. xTaskCreate()
该函数用来创建一个任务,在嵌入式中,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),也需要一定的 RAM 来作为任务堆栈。使用函数 xTaskCreate()来创建任务,所需的 RAM 会自动的从 FreeRTOS 的堆中分配。如
果使用函数 xTaskCreateStatic()创建的话这些 RAM 就需要用户来提供。新创建的任务默认是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行。
此外,不管在任务调度器启动前还是启动后,都可以创建任务。
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任务函数
const char * const pcName, //任务名字
const uint16_t usStackDepth, //任务堆栈大小
void * const pvParameters, //传递给任务的参数
UBaseType_t uxPriority, //任务优先级
TaskHandle_t * const pxCreatedTask ) //任务句柄,传出参数
2. xTaskCreateStatic()
此函数创建的任务所需要的RAM须用户提供。
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 ) //任务控制块 TCB
创建成功,则返回任务句柄。
3. xTaskDelete()
任务被删除后将不再存在,即不会再进行运行态。如果此任务使用函数 xTaskCreate()创建的,那么在此任务被删除以后,此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数 vTaskDelete()删除任务以后必须给空闲任务一定的运行时间。而使用函数 xTaskCreateStatic()创建的函数,需要手动释放分配的内存。
vTaskDelete( TaskHandle_t xTaskToDelete )
总结
上述是task的基本描述。