5.1 什么是多任务系统
以前的代码主要分布在两个地方,一个是main()函数中的while(1),一个是中断回调函数。这种一般叫做单任务系统或者前后台系统。大循环为后台、中断为前台。
前后台系统的实时性差,前后台系统各个任务(应用程序)都是排队等着轮流执行,不管你
这个程序现在有多紧急, 没轮到你就只能等着! 相当于所有任务(应用程序)的优先级都是一样
的。
FreeRTOS系统是一个抢占式多任务系统。抢占体现在他的任务具备优先级。高优先级的任务可以抢占低优先级的CPU使用权。多任务体现在它可以伪并行处理多个任务。所谓的伪体现在并不是说同一时刻一起执行很多个任务,而是由于每个任务执行的时间很短,导致看起来像是同一时刻执行了很多个任务一样。
5.2 FreeRTOS任务与协程
协程暂时没有搞懂,并且这方面没有视频讲到,文档也是简单待过;可能已经被弃用。
FreeRTOS中最小的功能单元为任务;每个任务都有自己的执行函数、堆栈;即每个任务都可以单独运行互相不受影响,但具体运行在何时间允许那个任务这个是由任务调度器来实现的。
5.3 任务状态
FreeRTOS中的任务状态有4个;分别为:就绪态、运行态、阻塞态、挂起态
- 就绪态 CPU只要有空就会从就绪列表里面获取任务来执行
- 运行态 CPU正在运行的任务
- 阻塞态 该任务因为资源没到位主动让贤退出,等待资源就绪他就回到就绪态(有超时时间)
- 挂起态 任务不会被CPU获取执行(无超时时间)
5.4 任务优先级
FreeRTOS中的任务优先级与NVIC中的中断优先级相反。FreeRTOS中优先级越高。越容易获取CPU的使用全(数值越大越牛逼)
每个任务都可以分配一个从0~(configMAX_PRIORITIES-1)的优先级;configMAX_PRIORITIES 在文件 FreeRTOSConfig.h 中有定义;一般为32。
当CPU空闲时候,调度器就会在就绪列表中寻找任务优先级最高的任务开始运行。
5.5 任务实现
任务函数的编写如下:
void TaskName(void *pvParameters)
{
while(1){
应用程序
类延时函数
}
vTaskDelete(NULL)
}
要注意的点有一下4点
- 函数的返回类型必须为void类型
- 任务的具体执行执行过程一般是一个大循环
- 必须含有可以引起任务调度器工作的功能代码,如延时、请求信号量、队列等。甚至可以直接调用任务调度器
一般不跳出循环,若跳出循环就必须删除掉当前任务
5.6 任务控制块
每一个任务都有自己的一个任务块,在这个任务块中存储着与该任务有关的一些信息。
在创建任务时候,FreeRTOS会为任务分配一个任务控制块。说直白点。任务控制块就是一个数据结构,在task.c中定义的一个结构体。定义如下typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { volatile StackType_t * pxTopOfStack; /**< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ #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 #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) UBaseType_t uxCoreAffinityMask; /**< Used to link the task to certain cores. UBaseType_t must have greater than or equal to the number of bits as configNUMBER_OF_CORES. */ #endif ListItem_t xStateListItem; /**< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ ListItem_t xEventListItem; /**< Used to reference a task from an event list. */ UBaseType_t uxPriority; /**< The priority of the task. 0 is the lowest priority. */ StackType_t * pxStack; /**< Points to the start of the stack. */ #if ( configNUMBER_OF_CORES > 1 ) volatile BaseType_t xTaskRunState; /**< Used to identify the core the task is running on, if the task is running. Otherwise, identifies the task's state - not running or yielding. */ UBaseType_t uxTaskAttributes; /**< Task's attributes - currently used to identify the idle tasks. */ #endif char pcTaskName[ configMAX_TASK_NAME_LEN ]; /**< Descriptive name given to the task when created. Facilitates debugging only. */ #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; /**< Points to the highest valid address for the stack. */ #endif #if ( portCRITICAL_NESTING_IN_TCB == 1 ) UBaseType_t uxCriticalNesting; /**< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ #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; #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; /**< Stores the amount of time the task has spent in the Running state. */ #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 ) 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; /**< 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;
5.7 任务堆栈
FreeRTOS 之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航。
任务调度器在进行任务切换的时候会将当前任务的现场(CPU 寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行。
要注意的是任务创建有两种方法。静态创建和动态创建。静态创建要传入栈大小和栈首地址。栈大小的单位是字(4Byte)。同理动态创建时要传入栈大小,单位也是字(4Byte)
评论 (0)