提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、FreeRTOS简介
- 为什么FreeRTOS要比裸机实时性好
裸机:一般都是main函数里放个while(),无限循环,不管任务有多紧急,也要等轮到才执行。
FreeRTOS:抢占式内核,优先执行高优先级任务,执行完毕后将CPU使用权交还给低优先级任务。
二、任务
-
任务状态
运行态:
就绪态:
阻塞态:
挂起态:不能被调度器调用进入运行态 -
优先级
每个任务都可以分配一个从0-(configMAX_PRIORITIES-1)
的优先级,优先级数字越低表示任务的优先级越低。
这个优先级和STM32 中的中断优先级不同。
configUSE_PORT_OPTIMISED_TASK_SELECTION
设置为1,configMAX_PRIORITIES不能超过32,其他情况可以为任意值。
configUSE_TIME_SLICING
定义为1,允许多个任务共用1个优先级,此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。 -
任务控制块
FreeRTOS把每个任务都有的一些属性集合到一起用一个结构体表示,这个结构体就是任务控制块:TCB_t
,当不使用某些功能的时候与其相关的变量就不参与编译。裁剪以后再考虑。 -
任务堆栈
因为是抢占式操作,所以肯定要恢复抢占之前的现场,在任务调度前会将其保存在这个任务本身的任务堆栈中,创建任务时需要给任务指定堆栈。
三、任务相关API函数
-
任务创建
(1)使用动态方法创建一个任务
xTaskCreate()
2.4讲过创建任务时需要给任务指定堆栈,任务需要RAM来保存与任务有关的状态信息(任务控制块),使用这个函数创建任务的话这些所需要的RAM就会自动从FreeRTOS的堆中分配,因此必须提供内存管理文件,默认使用的是Heap_.c这个内存管理文件,而且configSUPPORT_DYNAMIC_ALLOCATION
必须为1。
新创建的任务默认为就绪态,如果当前没有比它优先级高的任务会立即进入运行态,不管是任务调度器前还是后都可以创建任务。
函数原型如下:BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask)
pxTaskCode:任务函数
pcName: 任务名字,一般用于追踪和调试,任务名字长度不能超过
configMAX_TASK_NAME_LEN。
usStackDepth:任务堆栈大小,注意实际申请到的堆栈是usStackDepth的4倍。其中空闲任务的堆栈大小是
configMINIMAL_STACK_SIZE
pvParameters:传递给任务函数的参数
uxPriority: 任务优先级,0-configMAX_PRIORITIES-1
pxCreatedTask:任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务堆栈。此参数就用来保存这个任务句柄。其他API函数可能会使用到这个句柄。
返回值:pdPASS:任务创建成功 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任务创建失败,因为堆内存不足
(2)使用静态方法创建任务
xTaskCreateStatic()
(3)
xTaskCreateRestricted()
此函数也是用来创建任务的,只不过此函数要求所使用的MCU有MPU(内存保护单元),用此函数创建的任务会受到MPU的保护。其他的功能和函数xTaxkCreate()一样。 -
任务删除
vTaskDelete(TaskHandle_t xTaskToDelete)
被删除了的任务不再存在,也就是说再也不会进入运行态。任务被删除以后就不能再使用此任务句柄,如果此任务是使用动态方法创建的,那么此任务被删除以后此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数vTaskDelete()删除任务以后必须给空闲任务一定运行时间。
只有那些由内核分配给任务的内存才会在任务被释放后自动释放,用户分配给任务的内存需要用户自行释放.
例:调用pvPortMalloc()分配完内存,在任务删除后,需要调用xPortFree()释放内存。不然会导致内存泄漏。
内存泄露:使用完的内存空间没有归还系统,使这段内存一直处于被占用的状态,一次问题不大,一次次累计最终会导致内存 空间占完。 -
任务挂起和恢复
(1)挂起
将任务暂停,可以重新恢复且任务中保存的变量值不会丢失
void vTaskSuspend(TaslHandle_t xTasdToSuspend)
xTasdToSuspend:要挂起的任务句柄,如果参数为NULL的话表示挂起任务自己,所以调用之前最好检查一下句柄。
无返回值
(2)恢复
退出挂起态的唯一方法就是调用恢复函数
void vTaskResume(TaskHandle_t xTaskToResume)
xTaskToResume:要恢复任务的任务句柄
无返回值在中断中恢复一个任务
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)
xTaskToResume:要恢复的任务句柄
返回值:
pdTRUE:恢复运行的任务的优先级等于或高于正在运行的任务(被中断打断的任务),这就意味着在退出中断服务函数以后必须进行上下文切换。
pdFALSE:恢复运行的任务优先级低于当前正在运行的任务(被中断打断的任务),这就意味着在退出中断服务函数以后不需要进行上下文切换。
四、任务调度后的执行顺序
- 第一次执行
在每个任务第一次执行时,会从函数开头开始执行。 - 第二次及以后执行
从while执行,执行while中的代码。