人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2025-03-03 20:58阅读: 9评论: 0推荐: 0

第6章 任务管理

第六章 任务管理

1. 任务的基本概念

FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环境中运行。在任何时刻,只有一个任务得到运行, FreeRTOS 调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。作为任务,不需要对调度器的活动有所了解,在任务切入切出时保存上下文环境(寄存器值、堆栈内容)是调度器主要的职责。为了实现这点,每个 FreeRTOS 任务都需要有自己的栈空间。当任务切出时,它的执行环境会被保存在该任务的栈空间中,这样当任务再次运行时,就能从堆栈中正确的恢复上次的运行环境,任务越多,需要的堆栈空间就越大,而一个系统能运行多少个任务,取决于系统的可用的 SRAM。

FreeRTOS 的可以给用户提供多个任务单独享有独立的堆栈空间,系统可以决定任务的状态,决定任务是否可以运行, 同时还能运用内核的 IPC 通信资源,实现了任务之间的通信,帮助用户管理业务程序流程。这样用户可以将更多的精力投入到业务功能的实现中。

FreeRTOS 中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。 同时 FreeRTOS 也支持时间片轮转调度方式,只不过时间片的调度是不允许抢占任务的 CPU 使用权。

任务通常会运行在一个死循环中,也不会退出,如果一个任务不再需要,可以调用FreeRTOS 中的任务删除 API 函数接口显式地将其删除。

2. 任务调度器的基本概念

FreeRTOS 中提供的任务调度器是基于优先级的全抢占式调度:在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的。系统理论上可以支持无数个优先级(0 ~ N,优先级数值越小的任务优先级越低, 0 为最低优先级,分配给空闲任务使用,一般不建议用户来使用这个优先级。假如使能了 configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏(在 FreeRTOSConfig.h 文件定义), 一般强制限定最大可用优先级数目为 32。在一些资源比较紧张的系统中, 可以根据实际情况选择只支持 8 个或 32 个优先级的系统配置。在系统中,当有比当前任务优先级更高的任务就绪时,当前任务将立刻被换出,高优先级任务抢占处理器运行。

一个操作系统如果只是具备了高优先级任务能够“立即”获得处理器并得到执行的特点,那么它仍然不算是实时操作系统。因为这个查找最高优先级任务的过程决定了调度时间是否具有确定性,例如一个包含 n 个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和 n 相关,而下一个就绪任务抉择时间的长短将会极大的影响系统的实时性。

FreeRTOS 内核中采用两种方法寻找最高优先级的任务,第一种是通用的方法,在就绪链表中查找从高优先级往低查找 uxTopPriority,因为在创建任务的时候已经将优先级进行排序,查找到的第一个 uxTopPriority 就是我们需要的任务,然后通过 uxTopPriority 获取对应的任务控制块。第二种方法则是特殊方法,利用计算前导零指令 CLZ,直接在uxTopReadyPriority 这个 32 位的变量中直接得出 uxTopPriority,这样子就知道哪一个优先级任务能够运行,这种调度算法比普通方法更快捷,但受限于平台(在 STM32 中我们就使用这种方法)。

FreeRTOS 内核中也允许创建相同优先级的任务。相同优先级的任务采用时间片轮转方式进行调度(也就是通常说的分时调度器),时间片轮转调度仅在当前系统中无更高优先级就绪任务存在的情况下才有效。为了保证系统的实时性,系统尽最大可能地保证高优先级的任务得以运行。任务调度的原则是一旦任务状态发生了改变,并且当前运行的任务优先级小于优先级队列组中任务最高优先级时,立刻进行任务切换(除非当前系统处于中断处理程序中或禁止任务切换的状态)。

3. 任务状态的概念

FreeRTOS 系统中的每一任务都有多种运行状态。系统初始化完成后,创建的任务就可以在系统中竞争一定的资源,由内核进行调度。

任务状态通常分为以下四种:

  • 就绪( Ready):该任务在就绪列表中, 就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态。

  • 运行(Running):该状态表明任务正在执行, 此时它占用处理器, FreeRTOS 调度器选择运行的永远是处于最高优先级的就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态。

  • 阻塞(Blocked): 如果任务当前正在等待某个时序或外部中断,我们就说这个任务处于阻塞状态,该任务不在就绪列表中。包含任务被挂起、 任务被延时、任务正在等待信号量、读写队列或者等待读写事件等。

  • 挂起态(Suspended): 处于挂起态的任务对调度器而言是不可见的, 让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend()函数;而把一个挂起状态的任 务 恢复的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR()函数,我们可以这么理解挂起态与阻塞态的区别,当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样子调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数;而任务处于阻塞态的时候,系统还需要判断阻塞态的任务是否超时,是否可以解除阻塞。

4. 任务的设计要点

作为一个嵌入式开发人员,要对自己设计的嵌入式系统要了如指掌,任务的优先级信息,任务与中断的处理,任务的运行时间、逻辑、状态等都要知道,才能设计出好的系统,所以,在设计的时候需要根据需求制定框架。在设计之初就应该考虑下面几点因素:任务运行的上下文环境、任务的执行时间合理设计。

FreeRTOS 中程序运行的上下文包括:中断服务函数。普通任务。空闲任务。

5. 任务管理实验

任务管理实验是将任务常用的函数进行一次实验,在野火 STM32 开发板上进行该试验,通过创建两个任务,一个是 LED 任务,另一个是按键任务, LED 任务是显示任务运行的状态,而按键任务是通过检测按键的按下与否来进行对 LED 任务的挂起与恢复

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
/* 其他头文件 */
#include "uart.h"
#include "led.h"
#include "key.h"

/* 任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL; // 创建任务句柄

static TaskHandle_t LED_Task_Handler = NULL; // LED任务句柄
static TaskHandle_t KEY_Task_Handler = NULL; // KEY任务句柄

/* 内核对象句柄 */
/* 全局变量声明 */

/* 任务函数声明 */
static void AppTaskCreate(void); // 创建任务函数

static void LED_Task(void* pvParameters); // LED任务函数
static void KEY_Task(void* pvParameters); // KEY任务函数

static void BSP_Init(void); // 板级初始化函数

// 主函数启动流程
/*
    1.BSP初始化
    2.创建APP任务
    3.启动FreeRTOS,开启调度
*/
int main(void)
{
    BaseType_t xReturn = pdPASS; // 定义一个创建信息返回值,默认为pdPASS
    BSP_Init(); // 板级初始化
    printf("按下KEY1挂起任务,按下KEY2恢复任务\r\n");
    // 创建AppTaskCreate任务   
    xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,
                        (const char*    )"AppTaskCreate",
                        (uint16_t       )512,  
                        (void*          )NULL,
                        (UBaseType_t    )1, 
                        (TaskHandle_t*)&AppTaskCreate_Handle); 
    if(xReturn == pdPASS)
    {
        vTaskStartScheduler(); // 启动FreeRTOS
        printf("FreeRTOS Start Success\r\n");
    }
    else
    {
        return -1;
    }
    while(1);
}

// 为了方便管理,所有任务创建函数都在AppTaskCreate中实现
static void AppTaskCreate(void)
{
    BaseType_t xReturn = pdPASS;
    taskENTER_CRITICAL(); // 进入临界区
    xReturn = xTaskCreate((TaskFunction_t)LED_Task, // 任务入口函数
                         (const char*)"LED_Task",   // 任务名称
                         (uint16_t)512,             // 任务堆栈大小
                         (void*)NULL,              // 任务入口函数参数
                         (UBaseType_t)2,            // 任务优先级
                         (TaskHandle_t*)&LED_Task_Handler);// 任务控制块指针                           
    if(xReturn == pdPASS)
    {
        printf("Create LED_Task Success\r\n");
    }
    xReturn = xTaskCreate((TaskFunction_t)KEY_Task, // 任务入口函数
                         (const char*)"KEY_Task",   // 任务名称
                         (uint16_t)512,             // 任务堆栈大小
                         (void*)NULL,              // 任务入口函数参数
                         (UBaseType_t)3,            // 任务优先级
                         (TaskHandle_t*)&KEY_Task_Handler);// 任务控制块指针                           
    if(xReturn == pdPASS)
    {
        printf("Create KEY_Task Success\r\n");
    }
    vTaskDelete(AppTaskCreate_Handle);
    taskEXIT_CRITICAL(); // 退出临界区
}

// LED任务函数
static void LED_Task(void* pvParameters)
{
    while(1)
    {
        LED1_ON();
        printf("LED ON\r\n");
        vTaskDelay(500);
        LED1_OFF();
        printf("LED OFF\r\n");
        vTaskDelay(500);
    }
}

// KEY任务函数
static void KEY_Task(void* pvParameters)
{
    while(1)
    {
        if(Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
        {
            printf("KEY1 Pressed, 挂起任务!\r\n");
            vTaskSuspend(LED_Task_Handler); // 挂起LED任务
        }
        if(Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
        {
            printf("KEY2 Pressed, 恢复任务!\r\n");
            vTaskResume(LED_Task_Handler); // 恢复LED任务
        }
        vTaskDelay(20);
    }
}

// 板级初始化函数
static void BSP_Init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置中断优先级分组4
    LED_Init();
    USART_Config();
    Key_GPIO_Config();
}

本文作者:hazy1k

本文链接:https://www.cnblogs.com/hazy1k/p/18749297

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起