《FreeRTOS任务基础知识以及任务创建相关函数》

目录

1.FreeRTOS多任务系统与传统单片机单任务系统的区别

2.FreeRTOS中的任务(Task)介绍

2.1 任务特性

2.2 FreeRTOS中的任务状态

2.3 FreeRTOS中的任务优先级

2.4 在任务函数中退出

2.5 任务控制块和任务堆栈

2.5.1 任务控制块 

2.5.2 任务堆栈

3.FreeRTOS 任务相关 API 函数

3.1 任务创建和删除 API 函数

3.1.1 动态创建任务函数 xTaxkCreate()

3.1.2静态创建任务函数xTaskCreateStatic()

3.1.3 MPU限制创建函数 xTaskCreateRestricted()

3.1.4 任务删除函数 vTaskDelete()

4.动态任务创建与静态任务创建选择

总结:


1.FreeRTOS多任务系统与传统单片机单任务系统的区别

         回想一下我们以前在使用 51AVRSTM32 单片机裸机(未使用系统)的时候一般都是在main 函数里面用 while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要中断中完成一些处理。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数作为前台程序,大循环while(1)作为后台程序,如图所示:

       前后台系统的实时性差,前后台系统各个任务(应用程序)都是排队等着轮流执行,不管你这个程序现在有多紧急,没轮到你就只能等着!相当于所有任务(应用程序)的优先级都是一样的。但是前后台系统简单啊,资源消耗也少啊!在稍微大一点的嵌入式应用中前后台系统就明显力不从心了,此时就需要多任务系统出马了。

       多任务系统会把一个大问题(应用)“分而治之”,把大问题划分成很多个小问题,逐步的把小问题解决掉,大问题也就随之解决了,这些小问题可以单独的作为一个小任务来处理。这些小任务是并发处理的,注意,并不是说同一时刻一起执行很多个任务,而是由于每个任务执行的时间很短,导致看起来像是同一时刻执行了很多个任务一样。多个任务带来了一个新的问题,究竟哪个任务先运行,哪个任务后运行呢?完成这个功能的东西在 RTOS 系统中叫做任务调度器。不同的系统其任务调度器的实现方法也不同,比如 FreeRTOS 是一个抢占式的实时多任务系统,那么其任务调度器也是抢占式的,运行过程如图所示:

       在图中,高优先级的任务可以打断低优先级任务的运行而取得 CPU 的使用权,这样就保证了那些紧急任务的运行。这样我们就可以为那些对实时性要求高的任务设置一个很高的优先级,比如自动驾驶中的障碍物检测任务等。高优先级的任务执行完成以后重新把 CPU 的使用权归还给低优先级的任务,这个就是抢占式多任务系统的基本原理。

2.FreeRTOS中的任务(Task)介绍

2.1 任务特性

       在使用 RTOS 的时候一个实时应用可以作为一个独立的任务。每个任务都有自己的运行环境,不依赖于系统中其他的任务或者 RTOS 调度器。任何一个时间点只能有一个任务运行,具体运行哪个任务是由 RTOS 调度器来决定的,RTOS 调度器因此就会重复的开启、关闭每个任务。任务不需要了解 RTOS 调度器的具体行为,RTOS 调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器值,堆栈内容等)和任务上一次退出的时候相同。为了做到这一点,每个任务都必须有个堆栈,当任务切换的时候将上下文环境保存在堆栈中,这样当任务再次执行的时候就可以从堆栈中取出上下文环境,任务恢复运行。

任务特性:

1、简单。

2、没有使用限制。

3、支持抢占

4、支持优先级

5、每个任务都拥有堆栈导致了 RAM 使用量增大。

6、如果使用抢占的话的必须仔细的考虑重入的问题。

2.2 FreeRTOS中的任务状态

FreeRTOS 中的任务永远处于下面几个状态中的某一个:

● 运行态

当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

● 就绪态

处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!

● 阻塞态

如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务用了函数 vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!

● 挂起态

像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend()xTaskResume()

任务状态之间的转换如图所示:

2.3 FreeRTOS中的任务优先级

       每个任务都可以分配一个从0~(configMAX_PRIORITIES-1)的优先级,configMAX_PRIORITIES 在文件 FreeRTOSConfig.h 中有定义,前面我们讲解 FreeRTOS 系统配置的时候已经讲过了。如果所使用的硬件平台支持类似计算前导零这样的指令(可以通过该指令选择下一个要运行的任务, Cortex-M处理器是支持该指令的),并且宏configUSE_PORT_OPTIMISED_TASK_SELECTION也设置为了1,那么宏configMAX_PRIORITIES 不能超过 32!也就是优先级不能超过 32 级。其他情况下宏configMAX_PRIORITIES 可以为任意值,但是考虑到 RAM 的消耗,宏 configMAX_PRIORITIES最好设置为一个满足应用的最小值。

在平常程序(例程)中,configMAX_PRIORITIES被设置为32,如下图:

        优先级数字越低表示任务的优先级越低,0 的优先级最低,configMAX_PRIORITIES-1 的优先级最高。空闲任务的优先级最低,为 0。

        FreeRTOS 调度器确保处于就绪态或运行态的高优先级的任务获取处理器使用权,换句话说就是处于就绪态的最高优先级的任务才会运行。当宏 configUSE_TIME_SLICING 定义为 1 的时候多个任务可以共用一个优先级,数量不限。默认情况下宏configUSE_TIME_SLICING 在文件FreeRTOS.h 中已经定义为 1。此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。

2.4 在任务函数中退出

任务函数如下图;

循环里面就是真正的任务代码了,此任务具体要干的活就在这里实现!

FreeRTOS 的延时函数,此处不一定要用延时函数,其他只要能让 FreeRTOS 发生任务

切换的 API 函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用的就是 FreeRTOS 的延时函数

任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用

函数 vTaskDelete(NULL)删除此任务!

2.5 任务控制块和任务堆栈

2.5.1 任务控制块 

       FreeRTOS 的每个任务都有一些属性需要存储,FreeRTOS 把这些属性集合到一起用一个结构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数 xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。在老版本的 FreeRTOS 中任务控制块叫做 tskTCB,新版本重命名为 TCB_t,但是本质上还是 tskTCB,本教程后面提到任务控制块的话均用 TCB_t 表示,此结构体在文件 tasks.c 中有定义,在task.c的typedef struct tskTaskControlBlock结构体中,这里就不列出来了,

      FreeRTOS 的任务控制块中的成员变量相比 UCOSIII 要少很多,而且大多数与裁剪有关,当不使用某些功能的时候与其相关的变量就不参与编译,任务控制块大小就会进一步的减小。

2.5.2 任务堆栈

      FreeRTOS 之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航,任务调度器在进行任务切换的时候会将当前任务的现场(CPU 寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行。

     创建任务的时候需要给任务指定堆栈,如果使用的函数 xTaskCreate()创建任务(动态方法)的话那么任务堆栈就会由函数 xTaskCreate()自动创建,后面分析 xTaskCreate()的时候会讲解。如果使用函数 xTaskCreateStatic()创建任务(静态方法)的话就需要程序员自行定义任务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer 传递给函数。

值得注意的一点是:

        我们不管是使用函数 xTaskCreate()还是 xTaskCreateStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为 StackType_tStackType_t 本质上是 uint32_t,portmacro.h 中有相关定义

#define portSTACK_TYPE  uint32_t

#define portBASE_TYPE    long

typedef portSTACK_TYPE  StackType_t;

typedef long                         BaseType_t;

typedef unsigned long         UBaseType_t;

 可以得出StackType_t 类型的变量为 4 个字节,那么任务的实际堆栈大小就应该是我们所定义的 4 倍。

3.FreeRTOS 任务相关 API 函数

3.1 任务创建和删除 API 函数

FreeRTOS 最基本的功能就是任务管理,而任务管理最基本的操作就是创建和删除任务, FreeRTOS 的任务创建和删除 API 函数

3.1.1 动态创建任务函数 xTaxkCreate()

        此函数用来创建一个任务,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任务也需要一定的 RAM 来作为任务堆栈。如果使用函数 xTaskCreate()来创建任务的话那么这些所需的 RAM 就会自动的从 FreeRTOS 的堆中分配,因此必须提供内存管理文件,默认我们使用heap_4.c 这个内存管理文件,而且宏 configSUPPORT_DYNAMIC_ALLOCATION 必须为 1。

        如果使用函数 xTaskCreateStatic()创建的话这些 RAM 就需要用户来提供了。新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行,不管在任务调度器启动前还是启动后,都可以创建任务。此函数也是我们以后经常用到的,本教程所有例程均用此函数来创建任务,函数原型如下:

下面是我代码中的示例:

具体参数如下:

3.1.2静态创建任务函数xTaskCreateStatic()

        此函数和 xTaskCreate()的功能相同,也是用来创建任务的,但是使用此函数创建的任务所需的RAM需要用用户来提供。如果要使用此函数的话需要将宏configSUPPORT_STATIC_ALLOCATION 定义为 1。函数原型如下:

具体参数如下:

3.1.3 MPU限制创建函数 xTaskCreateRestricted()

此函数也是用来创建任务的,只不过此函数要求所使用的 MCU 有 MPU(内存保护单元),用此函数创建的任务会受到 MPU 的保护。其他的功能和函数 xTaxkCreate()一样。

具体参数如下:

3.1.4 任务删除函数 vTaskDelete()

      这个函数在我个人看来是必须要熟练运用的函数,要想成为一个牛的程序员,而不是一个loser程序员,是否能完全释放掉不用的内存至关重要,而vTaskDelete就是那把清理垃圾的扫帚!

     删除一个用函数 xTaskCreate()或者 xTaskCreateStatic()创建的任务,被删除了的任务不再存在,也就是说再也不会进入运行态。任务被删除以后就不能再使用此任务的句柄!

     如果此任务是使用动态方法创建的,也就是使用函数 xTaskCreate()创建的,那么在此任务被删除以后此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数vTaskDelete()删除任务以后必须给空闲任务一定的运行时间。

     只有那些由内核分配给任务的内存才会在任务被删除以后自动的释放掉,用户分配给任务的内存需要用户自行释放掉,比如某个任务中用户调用函数 pvPortMalloc()分配了 500 字节的内存,那么在此任务被删除以后用户也必须调用函数 vPortFree()将这 500 字节的内存释放掉,否则会导致内存泄露。此函数原型如下:

vTaskDelete( TaskHandle_t xTaskToDelete )

下图是我的代码中vTaskDelete的用法:

     start_task是创建的任务函数,在此任务函数中我们创建了另外两个任务 task1_task 和task2_task。start_task 任务的职责就是用来创建其他的任务或者信号量、消息队列等的,当创建完成以后就可以删除掉 start_task 任务。临界区则是保护该函数在创建是不被外界中断等任务打断。然后删除 start_task 任务,注意函数 vTaskDelete()的参数就是 start_task 任务的任务句柄StartTask_Handler

     简单的总结分析一下此例程的流程,因为这是我们使用 FreeRTOS 写的第一个程序,很多习惯是我们后面要用到的。比如使用任务宏定义任务优先级,堆栈大小等,一般有关一个任务的东西我们的放到一起,比如任务堆栈、任务句柄、任务函数声明等,这样方便修改。这些东西可以放到一个.h 头文件里面去,只是例程里面任务数比较少,所以就直接放到 main.c 文件里面了,要是工程比较大的话最好做一个专用的头文件来管理。在 main 函数中一开始肯定是初始化各种硬件外设,初始化完外设以后调用函数xTaskCreate()创建一个开始任务,注意创建开始任务是在调用函数 vTaskStartScheduler()开启任务调度器之前,这样当后面开启任务调度器以后就会直接运行开始任务了。其他任务的创建就放到开始任务的任务函数中,由于开始任务的职责就是创建其他应用任务和信号量、队列等这些内核对象的,所以它只需要执行一次,当这些东西创建完成以后就可以删除掉开始任务了。

4.动态任务创建与静态任务创建选择

      我们FreeRTOS程序是运行在RAM中的,RAM(随机存取存储器)分为两种DRAM(动态RAM)与SRAM(静态RAM),前者相当于电脑内存,需要耗电维持数据运转,保存,变动,效率高。后者相当与CPU缓存,耗电量小,节能,但效率低。

注意RAM是掉电丢失的,所以,每一次上电,都要重写RAM,这是不是和程序很像? 

以此类推,静态任务与动态任务就是上面的效率与耗能方面的差别了,需要开发者自行选择。

总结:

     学习本篇知识能让我们更加深入的了解任务的基础知识,如优先级,任务的创建函数所需要的值的意义,删除函数的重要性,挂起函数的操作,设定任务堆栈大小时要考虑实际的堆栈值,以及任务控制块、任务堆栈的意义等等。特别是TCB的知识与计算机操作系统知识相互印证了,感觉到了OS的博大精深,也建议大家多回看计算机操作系统的书。

希望这篇文章也能帮助到大家。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RHCE的学习(18)

第二章 变量和引用 深入认识变量 在程序设计语言中,变量是一个非常重要的概念。也是初学者在进行Shell程序设计之前必须掌握的一个非常基础的概念。只有理解变量的使用方法,才能设计出良好的程序。本节将介绍Shell中变量的相关知识。 什么是变量 顾名思义…

AG32 FPGA部分简单开发

环境 Quartus 13.0(Quartus 不能使用Lite 版本,需要使用Full 版本)AGM SDKSupra(快捷方式在SDK目录下,具体路径为AgRV_pio\packages\tool-agrv_logic\bin) FPGA编程 在AG32芯片中,拥有异构双…

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined

VUE_PROD_HYDRATION_MISMATCH_DETAILS 未明确定义。您正在运行 Vue 的 esm-bundler 构建,它期望这些编译时功能标志通过捆绑器配置全局注入,以便在生产捆绑包中获得更好的tree-shaking优化。 Vue.js应用程序正在使用ESM(ECMAScript模块&#…

Spring——事务

事务 JdbcTemplate 简介 Spring框架对JDBC进行封装&#xff0c;使用JdbcTemplate方便实现对数据库操作 准备工作 ①搭建子模块 搭建子模块&#xff1a;spring-jdbc-tx ②加入依赖 <dependencies><!--spring jdbc Spring 持久化层支持jar包--><dependenc…

集合类源码浅析のJDK1.8ConcurrentHashMap(下篇)

文章目录 前言一、分段扩容1、addCount2、transfer3、helpTransfer 二、查询二、删除总结 前言 主要记录ConcurrentHashMap&#xff08;笔记中简称CHM&#xff09;的查询&#xff0c;删除&#xff0c;以及扩容方法的关键源码分析。 一、分段扩容 1、addCount 扩容的逻辑主要在…

H5页面多个视频如何只同时播放一个?

目录 背景1. 首先介绍下 muted 属性2. 监听播放和暂停操作3. 视频播放完毕后返回桌面&#xff0c;再进入H5页面发现视频封面丢失置灰解决思路&#xff1a; 背景 页面模块同时有个四个视频模块&#xff0c;发现可以同时播放四个视频&#xff0c;但是理想的是每次只播放一个。 …

D69【 python 接口自动化学习】- python 基础之数据库

day69 Python 执行 SQL 语句 学习日期&#xff1a;20241115 学习目标&#xff1a; MySQL 数据库&#xfe63;- Python连接redis 学习笔记&#xff1a; redis数据库的用途 使用Python访问redis数据库 使用Python对redis数据库进行读写操作 总结 1. redis是一款高性能的键…

jmeter常用配置元件介绍总结之逻辑控制器

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之逻辑控制器 逻辑控制器1.IF控制器2.事务控制器3.循环控制器4.While控制器5.ForEach控制器6.Include控制器7.Runtime控制器8.临界部分控制器9.交替控制器10.仅一次控制器11.简单控制器12.随机控制器13.随机顺序控制器14.吞…

21.<基于Spring图书管理系统②(图书列表+删除图书+更改图书)(非强制登录版本完结)>

PS&#xff1a; 开闭原则 定义和背景‌ ‌开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;‌&#xff0c;也称为开放封闭原则&#xff0c;是面向对象设计中的一个基本原则。该原则强调软件中的模块、类或函数应该对扩展开放&#xff0c;对修改封闭。这意味着一个软…

springboot实现简单的数据查询接口(无实体类)

目录 前言&#xff1a;springboot整体架构 1、ZjGxbMapper.xml 2、ZjGxbMapper.java 3、ZjGxbService.java 4、ZjGxbController.java 5、调用接口测试数据是否正确 6、打包放到服务器即可 前言&#xff1a;springboot整体架构 文件架构&#xff0c;主要编写框选的这几类…

【已解决】 Tomcat10.1.x使用JSTL标签库

IDEA创建Java EE项目&#xff0c;使用Spring Spring MVC MyBatis框架&#xff0c;使用maven管理依赖。项目当前的环境是&#xff1a; Tomat 10.1.28Maven 3.6.3JDK 17 项目的功能&#xff1a;读取数据库的report表中的数据&#xff0c;返回一个List集合对象reportList在JSP…

权限相关知识

1.Linux权限的概念 在说Linux权限的概念之前我来问大家一个问题&#xff0c;你们觉得什么是权限&#xff1f; 权限平时的体现呢&#xff0c;就比如不是校长的亲戚就不能逛办公室&#xff0c;没充会员的爱奇艺看不了VIP影视剧&#xff0c;没成会员的的蛋糕店拿不到会员价等等等…

uniapp如何i18n国际化

1、正常情况下项目在代码生成的时候就已经有i18n的相关依赖&#xff0c;如果没有可以自行使用如下命令下载&#xff1a; npm install vue-i18n --save 2、创建相关文件 en文件下&#xff1a; zh文件下&#xff1a; index文件下&#xff1a; 3、在main.js中注册&#xff1a…

[刷题]入门3.彩票摇奖

博客主页&#xff1a;算法歌者本篇专栏&#xff1a;[刷题]您的支持&#xff0c;是我的创作动力。 文章目录 1、题目2、基础3、思路4、结果 1、题目 链接&#xff1a;洛谷-P2550-彩票摇奖 2、基础 此题目考察数组、三重循环、自增操作的能力。 3、思路 写代码时候&#xf…

JVM垃圾回收详解(重点)

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

git rebase --continue解冲突操作

git rebase --continue解冲突操作 如果只是执行了 git rebase 命令&#xff0c;那么git会输出一下“错误”提示&#xff1a; There is no tracking information for the current branch. Please specify which branch you want to rebase against. See git-rebase(1) for detai…

腾讯地图GL JS点标识监听:无dragend事件的经纬度获取方案

引入腾讯地图SDK <!--腾讯地图 API--><script charset"utf-8" src"https://map.qq.com/api/gljs?librariestools&v1.exp&key***"></script>构建地图容器 <div class"layui-card"><div class"layui-car…

249: 凸包面积

解法&#xff1a; 使用Andrew算法【计算几何/凸包】安德鲁算法&#xff08;Andrews Algorithm&#xff09;详解_andrew算法求凸包-CSDN博客 排序&#xff1a; 将所有点按照x坐标进行升序排序。如果x坐标相同&#xff0c;则按照y坐标升序排序。 初始化栈&#xff1a; 使用一个栈…

基于VUE实现语音通话:边录边转发送语言消息、 播放pcm 音频

文章目录 引言I 音频协议音频格式:音频协议:II 实现协议创建ws对象初始化边录边转发送语言消息 setupPCM按下通话按钮时开始讲话,松开后停止讲话播放pcm 音频III 第三库recorderplayer调试引言 需求:电台通讯网(电台远程遥控软件-超短波)该系统通过网络、超短波终端等无线…

【Rust中的项目管理】

Rust中的项目管理 前言Package&#xff0c;Crate&#xff0c;Module &use &#xff0c;Path通过代码示例解释 Crate&#xff0c;Module &#xff0c;use&#xff0c;Path创建一个package&#xff1a;代码组织化skin.rs 中的代码struct & enum 相对路径和绝对路径引用同…