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

news2025/4/17 17:57:57

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Seed-Thinking-v1.5:字节豆包新推理模型发布,200B参数战胜Deepseek

摘要 本文引入了Seed-Thinking-v1.5,能够在响应之前通过思考进行推理,从而提高了各种基准测试的性能。Seed-Thinking-v1.5在AIME 2024上获得86.7分,在Codeforces上获得55.0分,在GPQA上获得77.3分,展示了优秀的STEM和编…

AIDD-人工智能药物设计-提升分子预测反事实解释可靠性

UQ 过滤:提升分子预测反事实解释可靠性 目录 I-INF 指标结合 F1 评分,为评估大分子复合物(包括 RNA-蛋白质)的界面相互作用网络提供了可靠且全面的新方法。通过使用生成的人工 CAR 序列微调蛋白质语言模型(PLM),显著提高了 CAR-T 细胞活性的预测准确性,有效克服了合成蛋…

【前端】webpack一本通

今日更新完毕,不定期补充,建议关注收藏点赞。 目录 简介使用webpack默认只能处理js文件 ->引入加载器对JS语法降级,兼容低版本语法合并文件再次打包进阶 工作原理html-webpack-plugin插件webpack开发服务器引入使用webpack-dev-server模块…

代码学习总结(一)

代码学习总结(一) 这个系列的博客是记录下自己学习代码的历程,有来自平台上的,有来自笔试题回忆的,主要基于 C 语言,包括题目内容,代码实现,思路,并会注明题目难度&…

第十五届蓝桥杯C/C++B组省赛真题讲解(分享去年比赛的一些真实感受)

试题A——握手问题 一、解题思路 直接用高中学的排列组合思路 二、代码示例 #include<bits/stdc.h> using namespace std; int fun(int n) {int sum0;for(int i0;i<n;i){for(int ji1;j<n;j)sum; } return sum; } int main() {cout<<fun(50)-fun(7); }三、…

【Qt】qDebug() << “中文测试“; 乱码问题

环境 Qt Creator版本&#xff1a;4.7.1 编译器&#xff1a;MSVC2015_32bit 解法一 在.pro文件中添加 msvc:QMAKE_CXXFLAGS -execution-charset:utf-8注意&#xff1a; 1、需要清理项目&#xff0c;并重新qmake&#xff0c;然后构建。 测试项目下载&#xff1a;https://do…

Vue接口平台学习六——接口列表及部分调试页面

一、实现效果图及界面布局简单梳理 整体布局分左右&#xff0c;左边调试&#xff0c;右边显示接口列表 左侧&#xff1a; 一个输入框按钮&#xff1b;下面展示信息&#xff0c;大部分使用代码编辑器就好了&#xff0c;除了请求体传文件类型需要额外处理。然后再下方显示响应信…

【C语言】预处理(下)(C语言完结篇)

一、#和## 1、#运算符 这里的#是一个运算符&#xff0c;整个运算符会将宏的参数转换为字符串字面量&#xff0c;它仅可以出现在带参数的宏的替换列表中&#xff0c;我们可以将其理解为字符串化。 我们先看下面的一段代码&#xff1a; 第二个printf中是由两个字符串组成的&am…

低频rfid手持机,助力动物耳标智能化管理

低频RFID手持机&#xff0c;助力动物耳标智能化管理&#xff0c;正逐步成为现代畜牧业不可或缺的工具。它不仅能够高效读取动物耳标中的信息&#xff0c;如唯一识别码、疫苗接种记录、健康状态等&#xff0c;还极大地提升了数据录入的准确性和时效性。 1.精准识别与追踪‌ 通过…

【从零开始学习JVM | 第三篇】虚拟机的垃圾回收学习(一)

堆空间的基本结构 Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时&#xff0c;Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收。 Java 堆是垃圾收集器管理的主要区域&#xff0c;因此也被称作 GC 堆&#xff08;Garbage Collected Heap&am…

Jieba分词的原理及应用(三)

前言 “结巴”中文分词&#xff1a;做最好的 Python 中文分词组件 上一篇文章讲了使用TF-IDF分类器范式进行企业级文本分类的案例。其中提到了中文场景不比英文场景&#xff0c;在喂给模型之前需要进行分词操作。 分词的手段有很多&#xff0c;其中最常用的手段还是Jieba库进行…

Openlayers:flat样式介绍

在前段时间我在使用WebGL矢量图层时接触到了flat样式&#xff0c;我对其十分的感兴趣&#xff0c;于是我花了几天的时间对其进行了了解&#xff0c;在这篇文章中我将简单的介绍一下flat样式的使用方式以及我对其的一些理解。 一、了解flat样式 1.什么是flat样式&#xff1f; …

149页研读——华为基于IPD全过程研发质量管理【附全文阅读】

本文介绍了IPD(集成产品开发)的全过程研发质量管理,强调了以客户需求为导向,通过跨部门协同、资源整合、快速响应等方式提高研发效率和成功率。文章详细阐述了IPD研发管理体系的精要,包括其核心思想、优势、框架以及核心理念。 其中,跨领域平台与技术研发、端到端流程与项…

Oracle 23ai Vector Search 系列之5 向量索引(Vector Indexes)

文章目录 Oracle 23ai Vector Search 系列之5 向量索引Oracle 23ai支持的向量索引类型内存中的邻居图向量索引 (In-Memory Neighbor Graph Vector Index)磁盘上的邻居分区矢量索引 (Neighbor Partition Vector Index) 创建向量索引HNSW索引IVF索引 向量索引示例参考 Windows 环…

vue模拟扑克效果

vue模拟扑克效果 效果图&#xff1a; step1:C:\Users\wangrusheng\PycharmProjects\untitled18\src\views\Home.vue <template><div class"poker-container"><!-- 使用复合数据对象实现双行显示 --><divv-for"(card, index) in POKER_…

Android12源码编译之预置Android Studio项目Android.mk文件编写

1、在AndroidManifest.xml文件中添加package"com.sprd.silentinstalldemo"属性&#xff0c;因为新版本的Android Studio默认生成的AndroidManifest.xml是没有这个属性值的 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:an…

Spring Boot 测试详解,包含maven引入依赖、测试业务层类、REST风格测试和Mock测试

Spring Boot 测试详解 1. 测试依赖引入 Spring Boot 默认通过以下 Maven 依赖引入测试工具&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</s…

leetcode刷题日记——螺旋矩阵

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 题目要求按顺时针顺序给出m行n列的矩阵的数组按照题目所给的顺序挨个插入答案数组中运行如下 int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {*returnSize matrixSize * matrixCol…

模板引擎语法-标签

模板引擎语法-标签 文章目录 模板引擎语法-标签[toc]一、用于进行判断的{% if-elif-else-endif %}标签二、关于循环对象的{% for-endfor %}标签三、关于自动转义的{% autoescape-endautoescape %}标签四、关于循环对象的{% cycle %}标签五、关于检查值是否变化的{% ifchange %}…

深度学习学习笔记

目录 摘要 Abstracts 简介 Hourglass Module&#xff08;Hourglass 模块&#xff09; 网络结构 Intermediate Supervision&#xff08;中间监督&#xff09; 训练过程细节 评测结果 摘要 本周阅读了《Stacked Hourglass Networks for Human Pose Estimation》&#xf…