任务管理
FreeRTOS具备强大的执行跟踪功能、堆栈溢出检测、互斥信号量、优先继承权等特点。FreeRTOS提供的功能包括:任务管理,时间管理,信号量,消息队列,内存管理,记录功能等。
任务的状态
一个任务具有以下几种状态:
运行状态 |
正在执行的任务就是处于运行状态,它占用了处理器。 |
就绪状态 |
就绪状态就是任务处于没有被阻塞和挂起的情况,等待执行但因为其他相同或者更高的优先级任务正在运行造成还没有运行的任务。 |
阻塞状态 |
当一个任务处于等待临时事件或外部事件时就是处于阻塞状态。阻塞状态的任务一般有一个超时时间,超时后任务将解锁。阻塞的任务不会参与调度。任务可以进入阻塞状态等待一下两种不同类型的事件: 定时事件——这类事件可以是延迟到期或是绝对时间到点。 同步事件——等待其他任务或者中断事件。 |
挂起状态 |
在FreeRTOS中,调用vTaskSuspend() API函数可以使一个任务进入挂起态,调用vTaskResume()或者vTaskResumeFromISR() API函数可唤醒一个挂起态的任务。处于挂起状态的任务不能参与调度,其不能指定超时时间。 |
vTaskDelay()函数原型:
void vTaskDelay( portTickType xTicksToDelay); |
vTaskDelay()参数说明:
参数 |
说明 |
xTicksToDelay |
延迟心跳周期数目。调用该延时函数的任务将进入阻塞态,延迟结束后转移到就绪态。常数portTICK_RATE_MS可以用来将以毫秒为单位的时间转换为以心跳周期为单位的时间值。 |
任务函数
任务函数原型
void vATaskFunction( void *pvParameters ); |
在FreeRTOS中,任务是由C语言函数实现的。其必须返回void,而且带有一个void指针参数。每个任务都是一个功能相对独立的程序。任务具有程序入口,通常运行在一个死循环中,永远不退出,也不返回任何结果。任务不应该执行到任务代码的末尾,必要时,应在末尾添加报错功能的代码,即当程序运行到此处时,通过某种办法来通知用户出错了。此外,通常在此处添加一个删除任务的函数,以确保出错任务不影响其他任务的进行。任务函数可以在系统处理器初始化后的主函数部分创建,也可以在具体的任务中创建。无论以何种方式创建的任务都拥有独立的栈空间,以及自己的自动变量,即任务函数本身定义的变量。需要注意的是,在编程时,应最大程度地避免任务之间的耦合,如不使用全局变量,这样可使代码更具可读性和可维护性。
void vATaskFunction( void *pvParameters ) { /*可以像普通函数定义变量一样。用这个函数创建的每个任务都有一个属于自己的iVariableExample变量。但是如果iVariableExample被定义为static则这一点不会成立——这种情况下只有一个变量,所有的任务共享这一个变量*/ int iVariableExample = 0; /*任务通常实现在一个死循环中*/ for( ;; ) { /*完成任务功能的代码*/ } /*如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除的是当前任务*/ vTaskDelete( NULL ); } |
任务创建
xTaskCreate()函数
任务的创建采用xTaskCreate() API函数来创建,该函数的函数原型如下所示:
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, unsigned short usStackDepth, void * pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask ); |
xTaskCreate()参数说明
参数名 |
说明 |
pvTaskCode |
指向任务函数入口的指针(采用任务实现函数的函数名),任务通常采用死循环来实现,任务绝对不能从实现函数中返回或者退出。任务可以删除自己。 |
pcName |
任务的描述性名称。其主要功能就是方便调试,也能用来观测任务的句柄。其名字的最大限度长度是由FreeRTOSConfig.h配置文件中的configMAX_TASK_NAME_LEN来设置的。 |
usStackDepth |
分配给该任务的堆栈大小(以字为单位而非字节)。例如,如果堆栈是宽度是16位的,usStackDepth为100,那么将会分配200字节给任务堆栈。如果堆栈宽度是32位的,usStackDepth为400,那么1600字节将会分配给任务堆栈。FreeRTOSConfig.h文件中的configMINMAL_STACK_SIZE宏定义限定了空闲任务的堆栈容量。FreeRTOS提供的Demo通常是对所有任务最小建议值。 |
pvParameters |
该参数会作为任务参数传入到要创建的任务中,void类型指针(void*), pvParameters的值将传递到任务函数的传参。 |
uxPriority |
设定创建的任务的优先级。取值范围为:0—(configMAX_PRIORITIES-1)。 |
pvCreatedTask |
pvCreatedTask为任务句柄。其他API可以通过该句柄对该任务进行引用,如果改变任务优先级或者删除任务。如果不使用该句柄,可将此参数设置为NULL。 |
返回值 |
如果任务创建成功则返回pdPASS,否则的话返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY。 |
任务删除
vTaskDelete() API函数原型
void vTaskDelete(xTaskHandle pxTaskToDelete); | |
参数 |
说明 |
pxTaskToDelete |
被删除任务的句柄(目标任务)——参考xTaskCreate() API函数的参数pxCreatedTask,参数为NULL时,删除当前任务。 |
任务可以使用vTaskDelete()函数来删除自己或者其他任务。任务被删除后就不复存在,也不会再进入运行态。空闲任务的责任是释放分配给已删除任务的内存。因此,若使用vTaskDelete()函数的任务使空闲任务得不到执行,将致使内存得不到释放而瘫痪。此外,只有内核为任务分配的内存空间才会在任务被删除后自动回收。任务自己占用的内存或资源需要由应用程序自己显示的释放。
任务优先级设置
在利用xTaskCreate创建任务时,uxPriority参数为新创建的任务设置了一个初始的优先级。该优先级可以在启动调度器后用vTaskPrioritySet()函数进行修改。在FreeRTOS的配置文件FreeRTOSConfig.h中,参数configMAX_PRIORITIES值为可设置的优先级的最大数目。FreeRTOS本身并没有限定这个值的最大值,但是该值越大则内核占用的内存就越大。所以设置时需要根据实际需求,将该值设定为需要的最小值。
对于任务的优先级分配,FreeRTOS支持不同任务共享一个优先级,调度器将采用时间片轮转的方式保证同优先级的任务都能得到运行,这也保证设计弹性的最大化。当然,如果需要的话,每个任务可指定唯一的优先级。优先级的取值范围为0~(configMAX_PRIORITIES-1),数值越大,优先级越高。
调度器保障具有最高优先级的任务优先得到执行,使其进入运行态。如果当前最高优先级上具有多个任务,调度器会让这些任务轮流执行,每个任务都执行一个“时间片”,任务在时间片起始时刻进入运行状态。在时间片介绍时刻又退出运行态。
调度器在每个心跳结束时运行自身(实质上是进入空闲任务),选择下一个将要运行的任务,通常心跳时钟采用微处理器的滴答计数器来实现,抵达计数器中断用于产生心跳时钟。时间片的长短通过心跳中断频率进行设定,心跳的中断频率由FreeRTOSConfig.h中的编译时配置常量configTICK_RATE_HZ来配置。若configTICK_TATE_HZ设置为100Hz,则时间片的长度为10ms。需要说明的是,FreeRTOS的API函数调用中指定的时间总以心跳周期为单位,常量portTICK_RATE_MS用于将心跳为单位的时间值转换为以毫秒为单位的时间值。有效精度依赖于系统心跳频率。心跳计数(tick count)值表示的是从调度器启动开始,心跳中断发生的次数。用户程序在定延时周期时不必考虑心跳计数溢出问题,因为时间连贯性可在内核中进行管理。
vTaskPrioritySet()函数
用于修改任务优先级
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ); | |
参数 |
说明 |
pxTask |
被修改优先级任务的句柄(目标任务)——参考xTaskCreate() API函数的参数pxCreatedTask,以了解如何得到任务句柄方面的信息。 任务可以通过传入NULL值来修改自己的优先级 |
uxNewPriority |
目标任务需要被设置到的优先级,最大能被设置为(configMAX_PRIORITIES-1),如果比这个大则缺省为最大值。 |
uxTaskPriorityGet()函数
该函数用于查询一个任务的优先级。
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ); | |
参数 |
说明 |
pxTask |
被查询任务的句柄(目标任务)——参考参考xTaskCreate() API函数的参数pxCreatedTask,以了解如何得到任务句柄方面的信息。 |
返回值 |
返回被查询任务的优先级 |
参考:
《STM32嵌入式开发指南》