当前位置: 首页 > news >正文

第十二章:FreeRTOS多任务创建与删除

FreeRTOS多任务创建与删除教程

概述

本教程介绍FreeRTOS多任务的创建与删除方法,主要涉及两个核心函数:

  • 任务创建:xTaskCreate()
  • 任务删除:vTaskDelete()

实践步骤

1. 准备工程文件

复制005工程并重命名为006

在这里插入图片描述

2. 创建多个任务

打开工程,修改任务创建代码,分别创建任务1、任务2和任务3,并修改相应的任务句柄:

xTaskCreate(myTask1, "myTask1", 128, NULL, 2, &myTaskHandler1);
xTaskCreate(myTask2, "myTask2", 128, NULL, 2, &myTaskHandler2);
xTaskCreate(myTask3, "myTask3", 128, NULL, 2, &myTaskHandler3);

在这里插入图片描述

3. 编写任务函数

为三个任务分别实现任务函数:

void myTask1(void *arg)
{
    while(1)
    {
        printf("myTask1 running\n");
        vTaskDelay(500);
    }
}

void myTask2(void *arg)
{
    while(1)
    {
        printf("myTask2 running\n");
        vTaskDelay(500);
    }
}

void myTask3(void *arg)
{
    while(1)
    {
        printf("myTask3 running\n");
        vTaskDelay(500);
    }
}

在这里插入图片描述

4. 定义任务句柄

在文件顶部定义三个任务的句柄:

TaskHandle_t myTaskHandler1;
TaskHandle_t myTaskHandler2;
TaskHandle_t myTaskHandler3;

在这里插入图片描述

5. 解决串口输出混乱问题

编译工程并运行,发现串口输出是混乱的。这是因为每个任务都在使用printf函数,而printf是非线程安全的。当多个任务同时调用printf时,输出会相互干扰。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6. 使用临界区保护printf

使用临界区保护printf函数,防止输出被打断。临界区可以简单理解为关闭和开启中断的操作。在FreeRTOS中,通过taskENTER_CRITICAL()taskEXIT_CRITICAL()这对宏函数来实现进入和退出临界区。

修改任务函数如下:

void myTask1(void *arg)
{
    while(1)
    {
        taskENTER_CRITICAL();
        printf("myTask1 running\n");
        taskEXIT_CRITICAL();
        vTaskDelay(500);
    }
}

void myTask2(void *arg)
{
    while(1)
    {
        taskENTER_CRITICAL();
        printf("myTask2 running\n");
        taskEXIT_CRITICAL();
        vTaskDelay(500);
    }
}

void myTask3(void *arg)
{
    while(1)
    {
        taskENTER_CRITICAL();
        printf("myTask3 running\n");
        taskEXIT_CRITICAL();
        vTaskDelay(500);
    }
}

在这里插入图片描述

7. 观察任务执行顺序

重新编译并运行程序,观察到打印信息已经正常显示。有趣的是,虽然任务创建顺序是1、2、3,但执行顺序却是3、2、1。这是因为FreeRTOS在调度相同优先级的任务时,会按照特定规则进行。

在这里插入图片描述

8. 实现任务删除功能

使用vTaskDelete()函数删除任务。如果要删除其他任务,需传入目标任务的句柄;如果要删除当前任务自身,则传入NULL参数。

修改myTask1函数,实现计数删除功能:

void myTask1(void *arg)
{
    uint8_t i = 0;
    while(1)
    {
        taskENTER_CRITICAL();
        printf("myTask1 running\n");
        taskEXIT_CRITICAL();
        
        i++;
        if(i == 1)
        {
            vTaskDelete(myTaskHandler3); // 删除任务3
        }
        if(i == 2)
        {
            vTaskDelete(NULL); // 删除自身(任务1)
        }
        
        vTaskDelay(500);
    }
}

9. 观察任务删除效果

编译并运行修改后的程序,观察到:

  • 第一次循环:任务3、2、1依次运行,当任务1执行时删除了任务3
  • 第二次循环:只有任务2和任务1运行,当任务1执行时删除了自身
  • 之后只有任务2在运行

在这里插入图片描述

10. 使用启动任务创建其他任务

FreeRTOS中另一种常用的任务创建方式是使用一个启动任务来创建其他所有任务。在main函数中修改:

xTaskCreate(startTask, "startTask", 128, NULL, 2, &startTaskHandler);

在这里插入图片描述

11. 定义启动任务句柄

在文件顶部添加启动任务句柄定义:

TaskHandle_t startTaskHandler;

在这里插入图片描述

12. 编写启动任务函数

在main函数前实现启动任务函数:

void startTask(void *arg)
{
    // 在这里创建其他任务
}

13. 在启动任务中创建其他任务

将原来直接创建的三个任务移动到启动任务函数中:

void startTask(void *arg)
{
    xTaskCreate(myTask1, "myTask1", 128, NULL, 2, &myTaskHandler1);
    xTaskCreate(myTask2, "myTask2", 128, NULL, 2, &myTaskHandler2);
    xTaskCreate(myTask3, "myTask3", 128, NULL, 2, &myTaskHandler3);
}

在这里插入图片描述

14. 启动任务完成后删除自身

启动任务创建完其他任务后,可以删除自身:

vTaskDelete(NULL);

在这里插入图片描述

15. 完善启动任务函数

为了观察启动任务的执行,添加打印信息:

void startTask(void *arg)
{
    taskENTER_CRITICAL();
    printf("startTask running\n");
    taskEXIT_CRITICAL();
    xTaskCreate(myTask1, "myTask1", 128, NULL, 2, &myTaskHandler1);
    xTaskCreate(myTask2, "myTask2", 128, NULL, 2, &myTaskHandler2);
    xTaskCreate(myTask3, "myTask3", 128, NULL, 2, &myTaskHandler3);
    vTaskDelete(NULL);
}

16. 观察启动任务执行效果

编译并运行修改后的程序,观察到任务执行顺序变为1、2、3,与创建顺序一致。这种方式与直接创建的行为有所不同。

在这里插入图片描述

总结

  1. 多任务创建方式

    • 可以直接创建多个任务
    • 也可以通过一个启动任务来创建其他任务(这是更常用的方式)
  2. 任务删除

    • 使用vTaskDelete()函数删除任务
    • 删除自身时,参数传入NULL
    • 删除其他任务时,传入相应任务的句柄
  3. 线程安全

    • printf函数是非线程安全的,在多任务环境中使用时需要保护
    • 可以使用临界区(taskENTER_CRITICAL()taskEXIT_CRITICAL())来保护非线程安全的代码
  4. 任务执行顺序

    • 直接创建的相同优先级任务,执行顺序可能与创建顺序不同
    • 通过启动任务创建的相同优先级任务,执行顺序通常与创建顺序一致

项目开源

本项目已开源到GitHub,欢迎访问获取更多FreeRTOS学习资源:
https://github.com/Despacito0o/FreeRTOS/blob/main/README_zh.md

相关文章:

  • SEO长尾词深度优化策略
  • 体验智能体构建过程:从零开始构建Agent
  • 实信号的傅里叶变换为何属于埃尔米特函数?从数学原理到 MATLAB 动态演示
  • 1 程序的本质,计算机语言简史,TIOBE 指数,C 语言的独特魅力、发展历程、发行版本和应用场景
  • 第二篇:Python函数与模块化编程深度教程
  • bitsandbytes 报错心得
  • 解析 WebSocket 协议的基本原理
  • PostgreSQL全平台安装指南:从入门到生产环境部署
  • Redis常用数据结构和应用场景
  • spring 服务调用中 RestTemplate vs OpenFeign 对比详解
  • CE、NCE、InfoNCE的演变过程
  • Vue3内置组件Teleport
  • SSM智能卤菜销售平台
  • es6学习02-let命令和const命令
  • Python operator模块itemgetter函数介绍
  • fastadmin后端添加页面,自主控制弹出框关闭,关闭父页面弹框
  • LeetCode 解题思路 37(Hot 100)
  • #MES系统运维问题分析思路
  • leaflet 之 获取中国某个行政区的经纬度边界(latLngBounds)
  • HBuilderX 开发的uniapp项目在微信开发者工具中调试运行
  • 针对人群不同,网站做细分/seo推广外包
  • 专业重庆房产网站建设/最简单的网页制作
  • 化妆品品牌网站建设/seo网站建设优化什么意思
  • 手机网站开发学习/聚名网域名注册
  • 电影网站开发技术/百度推广云南总代理
  • wordpress 插件漏洞/宁波seo外包快速推广