10.1.3. FreeRTOS任务管理

10.1.3.1. 任务创建与删除

在FreeRTOS中,任务就是一个函数,原型如下

void TaskFunction(void *pvParameters);
  • 这个函数不能返回

  • 同一个函数可以用来创建多个任务

  • 每个任务都有自己的栈,任务的局部变量放在栈中

BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,   //函数指针,任务函数
                        const char * const pcName,  //任务名字
                        const configSTACK_DEPTH_TYPE usStackDepth,  //栈大小,单位为word
                        void *const pvParameters,   //调用任务函数时传入的参数
                        UBaseType_t uxPriortity,    //优先级
                        TaskHandlet_t *const pxCreateTask); //任务句柄,以后使用它来操作这个任务


void vTaskDelete(TaskHandle_t xTaskToDelete);

10.1.3.2. 任务优先级和Tick

优先级的取值范围是: 0~(configMAX_PRIORITES - 1),数值越大优先级越高. FreeRTOS的调度器可以使用2种方法来快速找出优先级最高的,可以运行的任务.使用不同的方法时,configMAX_PRIORITES的取值有所不同

  • 通用方法,使用C函数实现.对configMAX_PRIORITES的取值没有限制.

  • 架构相关的优化方法,架构相关的汇编指令实现.使用这种方法时,configMAX_PRIORITES的取值不能超过32

uBaseType_t uxTaskPriorityGet(const TaskHandle_t xTask);

void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);

10.1.3.3. 任务状态

../../_images/full_task_state_machine.png
  • 运行态(Running): 任务正在运行

  • 就绪态(Ready): 任务具备运行的条件,等待调度器调度

  • 暂停状态(Suspended): 任务挂起

    • 通过调用vTaskSuspend(TaskHandle_t xTaskToSuspend)函数挂起任务

    • 要退出挂起状态,只能由别人来操作,vTaskResume(别的任务调用),xTaskResumeFromISR(中断程序调用)

  • 阻塞态(Blocked): 任务要等待某个事件才能运行,等待过程中不消耗CPU资源

10.1.3.4. Delay函数

//等待多少个tick
void vTaskDelay(const TickType_t xTicksToDelay);

// pxPreviousWakeTime: 上一次被唤醒的时间
// xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement), 单位都是tick count
BaseType_t xTaskDelayUntil(TickType_t *const pxPreviousWakeTime,
                            const TickType_t xTimeIncrement);

10.1.3.5. 空闲任务

一个良好的程序,它的任务都是事件驱动的,平时大部分时间都处于阻塞状态,有可能自己创建的任务都无法运行, 但是调度器必须能找到一个可以运行的任务,所以需要提供空闲任务.在使用 vTaskStartScheduler() 函数来创建, 启动调度器时,这个函数内部会创建空闲任务

  • 空闲任务的优先级为0: 它不能阻碍用户任务运行

  • 空闲任务要不处于就绪态,要不处于运行态,永远不会阻塞

可以添加一个空闲任务的钩子函数(idle task hook functions),空闲任务的循环每执行一次,就会调用一次钩子函数.钩子函数有以下作用

  • 执行一些低优先级的,后台的,需要连续执行的函数

  • 测量系统的空闲时间,空闲任务被执行就意味着所有高优先级的任务都停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率

  • 让系统进入省电模式

10.1.3.6. 调度算法

所谓调度算法,就是怎么确定哪个就绪态的任务可以切换为运行状态

通过配置文件FreeRTOSConfig.h的两个配置项来配置调度算法: configUSE_PREEMPTION, configUSE_TIME_SLICING

调度算法的行为主要体现在两个方面:高优先级的任务先运行,同优先级的就绪态任务如何被选中

从3个角度统一理解多种调度算法

  • 可否抢占?高优先级的任务能否优先执行(配置项:configUSE_PREEMPTION)

    • 可以: 被称作”可抢占调度(pre-emptive)”, 高优先级的就绪任务马上执行

    • 不可以: 不能抢占就只能协商了,被称作”合作调度模式(Co-operative Scheduling)”

      • 当前任务执行,更高优先级的任务就绪了也不能马上运行,只能等待当前任务主动让出CPU资源

      • 其他同优先级的任务也只能等待

  • 可抢占的前提下: 同优先级的任务是否轮流执行(配置项:configUSE_TIME_SLICING)

    • 轮流执行: 被称为”时间片轮转(time slicing)”,同优先级的任务轮流执行

    • 不轮流执行: 当前任务会一直执行,直到主动放弃,或被高优先级任务抢占

  • 在”可抢占”+”时间片轮转”的前提下,进一步细化:空闲任务是否让步于用户任务(配置项:configIDLE_SHOULD_TIELD)

    • 空闲任务每执行一次循环,检查是否主动让位于用户任务

    • 空闲任务跟用户任务一样,轮流执行

注解

一般配置为可抢占+时间片轮转+空闲任务让步