FreeRTOS学习(四)FreeRTOS(动态)任务创建与删除及临界区保护

FreeRTOS学习(四)FreeRTOS(动态)任务创建与删除及临界区保护



一、FreeRTOS动态任务管理基础

动态管理是由FreeRTOS自己分配内存

1.1 动态创建任务详解

1.1.1 xTaskCreate()函数完整解析

BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,       // 任务函数指针
    const char * const pcName,       // 任务名称
    uint16_t usStackDepth,          // 任务栈大小(字)
    void *pvParameters,             // 传递给任务的参数
    UBaseType_t uxPriority,         // 任务优先级(0-configMAX_PRIORITIES-1)
    TaskHandle_t *pxCreatedTask     // 任务句柄
);

参数详细说明:

  1. pvTaskCode
  • 任务函数的入口地址
  • 必须是永久循环,不能返回
  • 也就是不同任务的执行:
void vTaskFunction(void *pvParameters)
{
    for(;;)
    {
        //任务代码
        vTaskDelay(xDelay);
    }
}

  1. pcName
  • 任务的描述性名称
  • 主要用于调试
  • 最大长度configMAX_TASK_NAME_LEN
  1. usStackDepth
  • 以字为单位的栈深度
  • 需要根据任务实际需求设置
  • 常见任务栈大小:50~500字
  1. pvParameters
  • 传递给任务的参数指针
  • 可以是任何类型
  • 不需要时设为NULL
  1. uxPriority
  • 任务优先级0~(configMAX_PRIORITIES-1)32位的configMAX_PRIORITIES就是32(硬件决定的)
  • 数值越大优先级越高
  • 相同优先级采用时间片轮转
  1. pxCreatedTask
  • 任务句柄的指针
  • 用于后续对任务的操作
  • 不需要时可设为NULL

1.1.2 返回值说明

  • pdPASS(1): 创建成功
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(0): 内存不足
  • 其他错误值

1.1.3 创建任务的内部过程

  1. 申请任务栈空间
  2. 申请任务控制块(TCB)空间
  3. 初始化任务栈
  4. 初始化TCB
  5. 将任务添加到就绪列表
  6. 根据优先级可能触发任务调度

1.2 动态删除任务深入解析

1.2.1 vTaskDelete()函数详解

void vTaskDelete(TaskHandle_t xTask);

使用方式:

  1. 删除自身:
vTaskDelete(NULL);
  1. 删除其他任务:
vTaskDelete(xTaskHandle);

1.2.2 删除任务的内部过程

  1. 从所有任务列表中移除
  2. 从就绪列表移除
  3. 将任务放入删除队列
  4. 空闲任务最终释放内存

1.2.3 存在的问题

未加临界区保护存在以下风险:

  1. 任务创建过程可能被打断,导致任务参数设置不完整
  2. 任务切换时机不可控,可能造成系统不稳定
  3. 任务优先级设置可能混乱
  4. 任务句柄可能无效

二、临界区保护原理与实现


未加的情况

// 第1步: 程序从main函数开始执行
int main(void)
{
    /* 创建开始任务 */
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄  
    
    // 第2步: 调用vTaskStartScheduler()启动调度器
    vTaskStartScheduler();  // 启动后FreeRTOS接管系统
    
    // 注意:这里之后的代码正常情况下不会执行到,因为系统已被FreeRTOS接管
    while(1);  
}

// 第3步: 调度器启动后,会执行优先级最高的就绪任务
// 此时只有start_task这个任务,所以会执行start_task函数
void start_task(void *pvParameters)
{
    // 第4步: 创建task1
    xTaskCreate((TaskFunction_t )task1,     
                (const char*    )"task1",   
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_PRIO,
                (TaskHandle_t*  )&Task1_Handler);

    // 第5步: 创建task2
    // 注意:在这里可能会被task1抢占,因为没有临界区保护
    xTaskCreate((TaskFunction_t )task2,     
                (const char*    )"task2",   
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2_Handler);

    // 第6步: 创建task3
    // 同样这里也可能被task1或task2抢占
    xTaskCreate((TaskFunction_t )task3,     
                (const char*    )"task3",   
                (uint16_t       )TASK3_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&Task3_Handler);
                
    // 第7步: 删除start_task自身
    vTaskDelete(NULL);  
}

// task1、task2、task3的执行时机取决于任务调度
void task1(void *pvParameters)
{
    // 任务1的无限循环
    while(1)
    {
        printf("Task1 is running...\r\n");
        vTaskDelay(500);  // 延时500ms,任务进入阻塞态
        // 延时期间可能执行task2或task3
    }
}

void task2(void *pvParameters)
{
    while(1)
    {
        printf("Task2 is running...\r\n");
        vTaskDelay(500);  // 延时500ms,任务进入阻塞态
        // 延时期间可能执行task1或task3
    }
}

void task3(void *pvParameters)
{
    while(1)
    {
        printf("Task3 is running...\r\n");
        vTaskDelay(500);  // 延时500ms,任务进入阻塞态
        // 延时期间可能执行task1或task2
    }
}

存在的问题和风险:

  1. 在start_task创建任务的过程中:
// 假设正在创建task2
xTaskCreate((TaskFunction_t )task2,...);  // 还未执行完

// 此时可能被task1抢占
printf("Task1 is running...\r\n");  // task1开始执行

// task2的创建过程被中断,可能导致:
- 任务创建参数设置不完整
- 任务句柄未正确赋值
- 任务控制块数据混乱

  1. 任务切换的不确定性:
// start_task中
xTaskCreate(...task1...);  // 创建task1
// task1可能立即得到执行
task1();  // task1抢占执行
// 返回start_task继续创建其他任务
xTaskCreate(...task2...);  // 创建task2

  1. 可能的执行顺序:
    start_task开始

    创建task1(未完成) → task1抢占执行 → 返回start_task

    创建task2(未完成) → task2抢占执行 → 返回start_task

    创建task3(未完成) → task3抢占执行 → 返回start_task

2.1 临界区保护的作用

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();  //进入临界区         
    //创建任务1
    xTaskCreate((TaskFunction_t )task1,     
                (const char*    )"task1",   
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_PRIO,
                (TaskHandle_t*  )&Task1Task_Handler);
    
    //创建任务2    
    xTaskCreate((TaskFunction_t )task2,     
                (const char*    )"task2",   
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);

    //创建任务3    
    xTaskCreate((TaskFunction_t )task3,     
                (const char*    )"task3",   
                (uint16_t       )TASK3_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&Task3Task_Handler);
                
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();  //退出临界区          
}

  1. taskENTER_CRITICAL():
  • 关闭中断
  • 防止任务切换
  • 保证代码段完整执行
  1. 保护范围:
  • 任务创建的完整过程
  • 参数设置
  • 任务控制块初始化
  • 任务添加到就绪列表
  1. taskEXIT_CRITICAL():
  • 退出临界区
  • 恢复中断
  • 允许任务切换

2.2 执行流程对比

未加保护:

在这里插入图片描述
加入保护后:
在这里插入图片描述
也就是创建完任务再去执行任务!!!

完整代码

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "FreeRTOS.h"
#include "task.h"

/* 任务优先级和堆栈大小定义 */
#define START_TASK_PRIO        1
#define TASK1_PRIO            2
#define TASK2_PRIO            3
#define TASK3_PRIO            4

#define START_TASK_STACK_SIZE  128
#define TASK1_STACK_SIZE      128
#define TASK2_STACK_SIZE      128
#define TASK3_STACK_SIZE      128

/* 任务句柄 */
TaskHandle_t StartTask_Handler;
TaskHandle_t Task1_Handler;
TaskHandle_t Task2_Handler;
TaskHandle_t Task3_Handler;

/* 任务函数声明 */
static void start_task(void *pvParameters);
static void task1(void *pvParameters);
static void task2(void *pvParameters);
static void task3(void *pvParameters);

void freertos_demo(void)
{
    /* 创建起始任务 */
    xTaskCreate((TaskFunction_t )start_task,          /* 任务函数 */
                (const char*    )"start_task",        /* 任务名称 */
                (uint16_t      )START_TASK_STACK_SIZE,/* 任务堆栈大小 */
                (void*         )NULL,                 /* 传递给任务函数的参数 */
                (UBaseType_t   )START_TASK_PRIO,     /* 任务优先级 */
                (TaskHandle_t* )&StartTask_Handler);  /* 任务句柄 */
    
    /* 开启任务调度 */
    vTaskStartScheduler();
}

/* 起始任务函数 */
static void start_task(void *pvParameters)
	
{
	 taskENTER_CRITICAL();           //进入临界区
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,
                (const char*    )"task1",
                (uint16_t      )TASK1_STACK_SIZE,
                (void*         )NULL,
                (UBaseType_t   )TASK1_PRIO,
                (TaskHandle_t* )&Task1_Handler);
    
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t )task2,
                (const char*    )"task2",
                (uint16_t      )TASK2_STACK_SIZE,
                (void*         )NULL,
                (UBaseType_t   )TASK2_PRIO,
                (TaskHandle_t* )&Task2_Handler);
    
    /* 创建任务3 */
    xTaskCreate((TaskFunction_t )task3,
                (const char*    )"task3",
                (uint16_t      )TASK3_STACK_SIZE,
                (void*         )NULL,
                (UBaseType_t   )TASK3_PRIO,
                (TaskHandle_t* )&Task3_Handler);
						taskEXIT_CRITICAL();            //退出临界区
                
    /* 删除起始任务 */
    vTaskDelete(NULL);
}

/* 任务1函数 */
static void task1(void *pvParameters)
{
    while(1)
    {
        printf("task1 is running\r\n");
        LED0_TOGGLE();
        vTaskDelay(pdMS_TO_TICKS(500));    // 使用pdMS_TO_TICKS转换时间
    }
}

/* 任务2函数 */
static void task2(void *pvParameters)
{
    while(1)
    {
        printf("task2 is running\r\n");
        LED1_TOGGLE();
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

/* 任务3函数 */
static void task3(void *pvParameters)
{
    uint8_t key;
    
    while(1)
    {
        key = key_scan(0);
        printf("task3 is running\r\n");
        
        if(key == KEY0_PRES)
        {
            if(Task1_Handler != NULL)
            {
                vTaskDelete(Task1_Handler);
                Task1_Handler = NULL;        // 删除后将句柄置空
                printf("task1 has been deleted\r\n");
            }
        }
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

  • 系统启动后创建start_task
  • start_task创建task1、task2、task3
  • 三个任务交替运行,每500ms打印一次信息

总结

本文详细介绍了FreeRTOS动态任务创建删除的实现方法,以及在多任务环境下必要的临界区保护机制。通过正确使用这些机制,可以确保系统的稳定性和可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值