FreeRTOS 任务基础知识

news2025/1/22 12:55:18

文章目录

  • 一、什么是多任务系统?
  • 二、FreeRTOS 任务与协程
  • 三、任务状态
  • 四、任务优先级
  • 五、任务实现
  • 六、任务控制块
  • 七、任务堆栈


RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习 RTOS 系统的工程师或者学生主要就是为了使用 RTOS 的多任务处理功能,初步上手 RTOS 系统首先必须掌握的也是任务的创建、删除、挂起和恢复等操作。

一、什么是多任务系统?

回想一下我们以前在使用 51、AVR、STM32 单片机裸机(未使用系统)的时候一般都是在main 函数里面用 while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要中断中完成一些处理。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数作为前台程序,大循环while(1)作为后台程序,如下图 所示:
在这里插入图片描述
前后台系统的实时性差,前后台系统各个任务(应用程序)都是排队等着轮流执行,不管你这个程序现在有多紧急,没轮到你就只能等着!相当于所有任务(应用程序)的优先级都是一样的。但是前后台系统简单啊,资源消耗也少啊!在稍微大一点的嵌入式应用中前后台系统就明显力不从心了,此时就需要多任务系统出马了。

多任务系统会把一个大问题(应用)“分而治之”,把大问题划分成很多个小问题,逐步的把小问题解决掉,大问题也就随之解决了,这些小问题可以单独的作为一个小任务来处理。这些小任务是并发处理的,注意,并不是说同一时刻一起执行很多个任务,而是由于每个任务执行的时间很短,导致看起来像是同一时刻执行了很多个任务一样。多个任务带来了一个新的问题,究竟哪个任务先运行,哪个任务后运行呢?完成这个功能的东西在 RTOS 系统中叫做任务调度器。不同的系统其任务调度器的实现方法也不同,比如 FreeRTOS 是一个抢占式的实时多任务系统,那么其任务调度器也是抢占式的,运行过程如下图所示:
在这里插入图片描述
在上图中,高优先级的任务可以打断低优先级任务的运行而取得 CPU 的使用权,这样就保证了那些紧急任务的运行。这样我们就可以为那些对实时性要求高的任务设置一个很高的优先级,比如自动驾驶中的障碍物检测任务等。高优先级的任务执行完成以后重新把 CPU 的使用权归还给低优先级的任务,这个就是抢占式多任务系统的基本原理。

二、FreeRTOS 任务与协程

再 FreeRTOS 中应用既可以使用任务,也可以使用协程(Co-Routine),或者两者混合使用。但是任务和协程使用不同的API函数,因此不能通过队列(或信号量)将数据从任务发送给协程,反之亦然。协程是为那些资源很少的 MCU 准备的,其开销很小,但是 FreeRTOS 官方已经不打算再更新协程了,所以本篇只讲解任务。

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

任务特性:
1、简单。
2、没有使用限制。
3、支持抢占
4、支持优先级
5、每个任务都拥有堆栈导致了 RAM 使用量增大。
6、如果使用抢占的话的必须仔细的考虑重入的问题。

2. 协程(Co-routine)的特性
协程是为那些资源很少的 MCU 而做的,但是随着 MCU 的飞速发展,性能越来越强大,现在协程几乎很少用到了!但是 FreeRTOS 目前还没有把协程移除的计划,但是 FreeRTOS 是绝对不会再更新和维护协程了,因此协程大家了解一下就行了。在概念上协程和任务是相似的,但是有如下根本上的不同:

1、堆栈使用
所有的协程使用同一个堆栈(如果是任务的话每个任务都有自己的堆栈),这样就比使用任务消耗更少的 RAM。

2、调度器和优先级
协程使用合作式的调度器,但是可以在使用抢占式的调度器中使用协程。

3、宏实现
协程是通过宏定义来实现的。

4、使用限制
为了降低对 RAM 的消耗做了很多的限制。

三、任务状态

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

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

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

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

任务状态之间的转换如下图所示:
在这里插入图片描述

四、任务优先级

每个任 务都可以分 配一个 从 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最好设置为一个满足应用的最小值。

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

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

五、任务实现

在使用 FreeRTOS 的过程中,我们要使用函数 xTaskCreate()或 xTaskCreateStatic()来创建任务,这两个函数的第一个参数 pxTaskCode,就是这个任务的任务函数。什么是任务函数?任务
函数就是完成本任务工作的函数。我这个任务要干嘛?要做什么?要完成什么样的功能都是在这个任务函数中实现的。 比如我要做个任务,这个任务要点个流水灯,那么这个流水灯的程序就是任务函数中实现的。FreeRTOS 官方给出的任务函数模板如下:

void vATaskFunction(void *pvParameters) (1)
{
	for( ; ; ) (2)
	{
		--任务应用程序-- (3)
		vTaskDelay(); (4)
	}
	/* 不 能 从 任 务 函 数 中 返 回 或 者 退 出 , 从 任 务 函 数 中 返 回 或 退 出 的 话 就 会 调 用
	configASSERT(),前提是你定义了 configASSERT()。如果一定要从任务函数中退出的话那一定 
	要调用函数 vTaskDelete(NULL)来删除此任务。*/
	vTaskDelete(NULL); (5)
}

(1)、任务函数本质也是函数,所以肯定有任务名什么的,不过这里我们要注意:任务函数的返回类型一定要为 void 类型,也就是无返回值,而且任务的参数也是 void 指针类型的!任务函数名可以根据实际情况定义。

(2)、任务的具体执行过程是一个大循环,for(; ; )就代表一个循环,作用和 while(1)一样,笔者习惯用 while(1)。

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

(4)、FreeRTOS 的延时函数,此处不一定要用延时函数,其他只要能让 FreeRTOS 发生任务切换的 API 函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用的就是 FreeRTOS 的延时函数。

(5)、任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用函数 vTaskDelete(NULL)删除此任务!
FreeRTOS 的任务函数和 UCOS 的任务函数模式基本相同的,不止 FreeRTOS,其他 RTOS的任务函数基本也是这种方式的。

六、任务控制块

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

typedef struct tskTaskControlBlock
{
	volatile StackType_t *pxTopOfStack; //任务堆栈栈顶
	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGSxMPUSettings; //MPU 相关设置
	#endif
	
	ListItem_t xStateListItem; //状态列表项
	ListItem_t xEventListItem; //事件列表项
	UBaseType_t uxPriority; //任务优先级
	StackType_t *pxStack; //任务堆栈起始地址
	char pcTaskName[ configMAX_TASK_NAME_LEN ];//任务名字
	
	#if ( portSTACK_GROWTH > 0 )
		StackType_t *pxEndOfStack; //任务堆栈栈底
	#endif
	
	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t uxCriticalNesting; //临界区嵌套深度
	#endif
	
	#if ( configUSE_TRACE_FACILITY == 1 ) //trace 或到 debug 的时候用到
		UBaseType_t uxTCBNumber;
		UBaseType_t uxTaskNumber;
	#endif
	
	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t uxBasePriority; //任务基础优先级,优先级反转的时候用到
		UBaseType_t uxMutexesHeld; //任务获取到的互斥信号量个数
	#endif
	
	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
		TaskHookFunction_t pxTaskTag;
	#endif
	
	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) //与本地存储有关
		void 
		*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
	#endif
	
	#if( configGENERATE_RUN_TIME_STATS == 1 )
		uint32_t ulRunTimeCounter; //用来记录任务运行总时间
	#endif
	
	#if ( configUSE_NEWLIB_REENTRANT == 1 )
		struct _reent xNewLib_reent; //定义一个 newlib 结构体变量
	#endif
	
	#if( configUSE_TASK_NOTIFICATIONS == 1 )//任务通知相关变量
		volatile uint32_t ulNotifiedValue; //任务通知值
		volatile uint8_t ucNotifyState; //任务通知状态
	#endif
	
	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
		//用来标记任务是动态创建的还是静态创建的,如果是静态创建的此变量就为 pdTURE,
		//如果是动态创建的就为 pdFALSE
		uint8_t ucStaticallyAllocated; 
	#endif
	
	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif
	
} tskTCB;
	//新版本的 FreeRTOS 任务控制块重命名为 TCB_t,但是本质上还是 tskTCB,主要是为了兼容
	//旧版本的应用。
	typedef tskTCB TCB_t;

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

七、任务堆栈

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

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

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer, (1)
StaticTask_t * const pxTaskBuffer ) 

(1)、任务堆栈,需要用户定义,然后将堆栈首地址传递给这个参数。

堆栈大小:
我们不管是使用函数 xTaskCreate()还是 xTaskCreateStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为 StackType_t,StackType_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 倍

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

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

相关文章

Revit中怎么绘制多面坡度的屋顶及生成墙

​一、Revit中怎么绘制多面坡度的屋顶 像这种坡屋顶我们可以观察到,它的屋顶轮廓都是带有坡度的,那我可以通过添加定义坡度的方式来绘制出该屋顶。 点击建筑选项卡中的屋顶按钮,选择迹线屋顶。 选择使用拾取线工具,在选项栏中将偏…

从零学习SDK(4)使用SDK创建一个简单的应用程序

SDK(Software Development Kit)即软件开发工具包,是一组帮助我们开发出软件的工具,包括代码、文档、示例等。一般情况下,我们需要将SDK引入到我们的项目中才能使用它。比如,学Java的朋友最早接触的JDK&…

JMeter使用JDBC Request取样器 获取查询结果

JDBC获取查询结果Java脚本创建文件JSON字符串解析 数据库连接配置定义全局变量JDBC Request 创建文件路径以及文件的脚本 import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.Date; boolean result false; try {//String message new Si…

JAVA初学下(仅做笔记)

一. Map集合(双列集合) 1.特点 键不能重复,值可以重复 Map接口位于最高层 2.常见API 2.1基本功能 ①注意V put(K key,V value)这个方法, 当加入 的键值对元素的键(key) 不存在时,就会将 键值…

django项目名称重命名

学会这个小白也能轻松修改项目名称;日常板砖中难免遇到项目多次利用的情况,修改项目名称也成了一门手艺😀;实际操作起来也非常简单,没有那些花花绿绿的东西. 一.项目奔跑 1.拿到项目后,我们先在pycharm运行下,看项目是否能正常奔跑起来; 注意收集正常奔跑后项目的执行环境等…

某程序员哀叹:月薪四五万,却每天极度焦虑痛苦,已有生理性不适,又不敢裸辞,怎么办?...

高薪能买来快乐吗?来看看这位程序员的哀叹:实在是扛不住了,每天都在极度焦虑和痛苦中度过,早上起来要挣扎着做心理建设去上班,已经产生生理性的头晕恶心食欲不振。有工作本身的原因,更多是自己心态的问题&a…

12纳米做出了7纳米的性能,为国产芯片打破美国限制指明道路

美国联合日本、荷兰等限制对中国供应先进芯片设备,试图借此阻止中国发展14纳米以下的先进工艺,然而日前中国一家芯片企业发布了一款全新的芯片,却给中国芯片行业指明了新道路,发展先进性能芯片又了可能性。龙芯近期发布了一款全新…

Flink 优化 (四) --------- 数据倾斜

目录一、判断是否存在数据倾斜二、数据倾斜的解决1. keyBy 后的聚合操作存在数据倾斜2. keyBy 之前发生数据倾斜3. keyBy 后的窗口聚合操作存在数据倾斜一、判断是否存在数据倾斜 相同 Task 的多个 Subtask 中,个别 Subtask 接收到的数据量明显大于其他Subtask 接收…

【DevOps】GitOps之痛 -不完美的GitOps

前言 在前两篇文章中,我们对GitOps进行了大致的介绍: 【DevOps】GitOps初识(上) - 让DevOps变得更好 【DevOps】GitOps初识(下) - 让DevOps变得更好 GitOps 作为软件发布实践方式,有着许多的优点,然而,世上并没有完美…

【Linux】Centos安装mvn命令(maven)

🍁博主简介 🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 文章目录一、下载maven包方法一:官…

CTF流量分析

在CTF里,一些pcapng或pcap文件后缀的数据 不同的数据包有不同的协议,常见的有HTTP,TCP协议 Wireshark 简介 是一个网络封包分析软件。网络封包分析软件的功能是获取网络封包,并尽可能显示出最为详细的网络封包资料 使用WinPC…

舔狗日记:学姐生日快到了,使用Python把她的照片做成视频当礼物

舔狗日记1前言一、需要调入的模块二、实现合并多张图片转成 mp4 视频三、优化改进一下总结前言 这不是学姐生日快到了,于是我学了一手使用Python来把学姐的照片生成为视频,到时候给她一个惊喜! 好了先不舔了,下面分享一下用pytho…

基于朴素贝叶斯分类器的钞票真伪识别模型

基于朴素贝叶斯分类器的钞票真伪识别模型 内容 本实验通过实现钞票真伪判别案例来展开学习朴素贝叶斯分类器的原理及应用。 本实验的主要技能点: 1、 朴素贝叶斯分类器模型的构建 2、 模型的评估与预测 3、 分类概率的输出 源码下载 环境 操作系统&#xf…

Leetcode.130 被围绕的区域

题目链接 Leetcode.130 被围绕的区域 mid 题目描述 给你一个 m x n的矩阵 board,由若干字符 X和 O,找到所有被 X围绕的区域,并将这些区域里所有的 O用 X填充。 示例 1: 输入:board [[“X”,“X”,“X”,“X”],[“X…

stm32霸道-lvgl移植学习(一)

文章目录效果有用链接要求创建工程屏幕驱动以及触屏驱动LVGL PortWidgets demo其它效果 目前显示驱动显示较慢,后续会优化。 有用链接 LVGL官网 代码下载 要求 要求最低要求 建议要求架构16、32、64位微控制器或微处理器时钟 > 16 MHz > 48 MHzFlash/RO…

《低代码PaaS驱动集团企业数字化创新白皮书》-平台化加低代码提供破解之道(1)

平台化加低代码提供破解之道 大型企业亟需通过下一代平台开发技术实现软件创新,实现对海量数据的采集加工,以及企业内部数据的互联互通,帮助客户以低成本、短周期、高效率的方式实现数字化应用,进而赋能业务创新。基于此&#xf…

408--计算机网络--网络层总结1

目录 一、网络层概述: 1、网络层的主要任务: 2、网络层向上提供两种服务: 二、IPV4地址分类与子网划分: 1、分类编址: 一、网络层概述: 1、网络层的主要任务: 络层的主要任务就是将分组从…

【数据库基操】启动与连接MySQL数据库

一、启动与关闭 只介绍一种方法: 打开命令行工具,以管理员身份运行 1.启动数据库 net start mysql80 //80是在安装的时候设置的名字(默认),不用在意 2.关闭数据库 net stop mysql80 如题已经成功&#…

java获取本机ip的方法

Java中有一个类叫 Application,可以用来获取本机 ip,也可以用来获取网络连接的信息,例如网络上有什么主机、需要访问本机的主机名等。但是这个类只能在本机上使用,如果要访问外部的主机,还需要使用其它的方法。 首先在…

教育大数据总体解决方案(5)

(4)错题整理 将学生的本次考试错题进行集中整理,提炼出所有题目的题干和正确的答案。 (5)提高方案 分析学生对知识点掌握情况,推算出学生的进步空间以及下次考试的预测拔高分数。根据学生本次考试错误知识点…