1、freeRTOS的任务
freeRTOS中的任务指的是线程,它是操作系统调度的对象,也是用户功能实现的所在位置。
(1)任务有哪些状态?
任务在运行过程中,可能出现以下几种状态:
1)运行态。任务当前正在运行中,占有cpu的使用权。
2)就绪态。满足了运行条件,但是当前没有运行,可能是有高优先级任务在执行或者在中断,处于等待获得cpu使用权然后运行的状态中。
3)阻塞态。在某些条件下被阻塞了,不能得到运行。比如等待某些信号量,消息队列或者调用了延迟函数。
4)挂起态。任务被通过调用挂起操作被终止执行了,直到被从挂起状态中恢复为止,否则将一直处于挂起的状态中。
(2)任务的优先级
freeRTOS 中任务的最高优先级是通过 FreeRTOSConfig.h 文件中的一个宏定义进行配置的,如下:
configMAX_PRIORITIES
用户实际可以使用的优先级范围是 0 到 configMAX_PRIORITIES – 1。
注:freeRTOS的任务优先级是数值越大,任务的优先级越高。
2、freeRTOS的任务切换的实现
(1)PendSV中断
freeRTOS系统的任务切换的具体过程最终都是在pendSV中断服务函数里面完成的。
PendSV是一种不精确的异常处理,优先级和挂起状态可以通过编程实现,一般在实时操作系统中PendSV的优先级都会被设置为最低,在其他所有的异常中断处理完以后才执行,对上下文的切换非常有用,在OS设计中也是属于非常关键的。
在freeRTOS系统中,执行上下文切换依靠的是任务调度器。上下文切换被触发的场合可以有:
1、执行一个系统调用。如:任务切换函数taskYIELD()、延时vTaskDelay()。
2、Systick中断。
(2)Systick中断
Systick中断服务函数中会进行任务切换。
(3)中断中需要注意的问题
在freeRTOS中,任务切换都是在Systick中断中执行的,最终实现任务切换的过程是在PendSV中实现的。就会出现一种情况:
假如有一个中断(IRQ)在Systick异常之前产生了,当Systick异常发生时会抢占IRQ中断的执行,如果这个时候发生了上下文切换,中断IRQ处理就会被延迟了,具体延迟的时间根据系统自身情况而定,结果是无法预料的。
比如,在cortex-M3 / cortex-M4中,当存在活跃的异常服务时,设计为默认不允许返回到线程模式,当还存在活跃的中断时,如果OS试图返回到线程模式,则会引发fault错误。如下图示:
为了解决这个中断被延迟的问题,PendSV异常将上下文切换请求延迟到所有其他IRQ都被处理完之后才执行,即把PendSV的优先级设置为最低。实现的示意图如下:
3、freeRTOS的任务管理的API函数
(1)FreeRTOS 创建一个任务
在freeRTOS中,可以根据实际使用的需要创建一定数量的任务(线程),任务只有被成功创建了,才有可能被执行到。
在FreeRTOS实时操作系统中,创建任务可以使用如下的API:
portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode,
const char * const pcName,
unsigned short usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pvCreatedTask
);
函数参数说明:
pvTaskCode:指向任务入口函数的指针,即任务函数。
pcName:任务的描述。一般都是为了调试方便而使用的。由 tskMAX_TASK_NAME_LEN 定义的最大长度,默认为 16。
usStackDepth:任务堆栈的大小。不是字节数而是以字为单位。例如,如果堆栈为 16 位宽,并且 usStackDepth 定义为 100,则将分配 200 个字节用于堆栈存储。
pvParameters:任务的参数的指针。可以向创建的任务中传入参数,不需要传送参数可以选NULL。
uxPriority:任务运行的优先级。
pvCreatedTask:任务的句柄,通过该句柄引用创建的任务。
返回值: 任务创建成功返回 pdPASS;创建失败返回 pdFAIL。
举例:创建一个任务的示例如下:
xTaskCreate((TaskFunction_t )Start_Task, //任务函数
(const char* )"Start_Task", //任务名称
(uint16_t )128, //任务堆栈大小
(void * )NULL, //传递给任务函数的参数
(UBaseType_t )5, //任务优先级
(TaskHandle_t * )&StartTask_Handler); //任务句柄
注意:
1)任务的优先级要根据 FreeRTOSConfig.h 文件中的
configMAX_PRIORITIES
进行 设置的,可以使用的优先级范围是 0 到 configMAX_PRIORITIES – 1,如果创建的任务所选的任务优先级超过了这个设置的范围,会被忽略掉,不会被执行。
(2)FreeRTOS任务挂起
当任务在系统运行过程中,因为某些条件或者有其他的更加重要的事情要做的时候,可以考虑把某些任务先挂起,等候条件到达之后再恢复任务继续调度运行。
挂起任务的函数为:
void vTaskSuspend( xTaskHandle pxTaskToSuspend )
参数:
pxTaskToSuspend:需要被挂起的任务的优先级
返回值:无
要使用这个函数,需要在 FreeRTOSConfig.h中将宏定义
INCLUDE_vTaskSuspend 定义为 1,如下:
注:如果要挂起、恢复整个系统所有的任务,freeRTOS也提供了一个API函数,如下:
void vTaskSuspendAll( void ) // 挂起所有的任务
BaseType_t xTaskResumeAll( void ) // 恢复所有被挂起的任务
要恢复某个被挂起的任务,可以使用函数:
void vTaskResume( xTaskHandle pxTaskToResume )
参数: pxTaskToResume:任务的优先级
(3)FreeRTOS任务删除
如果有某些任务只是需要存在于某些条件,当条件满足之后就不再需要这个任务参与运行,这个时候可以选择删除掉这个任务,释放该任务所占用的堆栈空间。
删除任务可以使用函数:
void vTaskDelete( xTaskHandle pxTask )
参数:
pxTask:需要被删除的任务句柄
返回值:无
注意:要使用这个函数,需要将宏定义 INCLUDE_vTaskDelete 设置为 1 。
4、freeRTOS的内核管理函数
freeRTOS中有关的内核管理函数如下:
(1)获取任务的优先级
如果运行过程中需要知道当前运行的任务的优先级,可以调用优先级获取函数得到任务的优先级:
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask )
参数:
pxTask:需要获取优先级的任务的句柄。
返回值: 句柄对应的任务的优先级。
(2)修改任务的优先级
任务在运行过程中,优先级希望能被改变成其他的优先级,可以使用函数:
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority )
参数:
pxTask:任务的句柄。
uxNewPriority:需要修改的新的优先级的值。
(3)freeRTOS的延时函数
1)相对延时函数
void vTaskDelay(const TickType_t TicksToDelay)
2)绝对延时函数
void vTaskDelayUntil (
TickType_t* const pxPreviousWakeTime,
const TickType_t xTimeIncrement
)
绝对延时函数的运行大致过程如下示意图:
注意:相对延时函数和绝对延时函数的调用都会引起系统的任务切换,并且相对延时函数不是精确的延时函数,绝对延时函数才是精确的延时调度。
如果在代码中,有某一部分的功能需要固定时间被执行的话,在优先级较高的时候,可以考虑使用绝对延时实现。