FreeRTOS多任务系统

news2024/11/15 12:00:44

FreeRTOS


文章目录

  • FreeRTOS
  • 1 单任务和多任务系统
    • 1.1 单任务系统
    • 1.2 多任务系统
  • 2 FreeRTOS 任务状态
  • 3 FreeRTOS 任务优先级
  • 4 Free RTOS 任务调度方式
    • 4.1 抢占式调度
    • 4.2 时间片调度
  • 5 FreeRTOS 任务控制块
  • 6 FreeRTOS 任务栈


1 单任务和多任务系统

1.1 单任务系统

单任务系统的编程方式,即裸机的编程方式,这种编程方式的框架一般都是在 main()函数中使用一个大循环,在循环中顺序地调用相应的函数以处理相应的事务,这个大循环的部分可以视为应用程序的后台, 而应用程序的前台,则是各种中断的中断服务函数。因此单任务系统也叫做前后台系统,前后台系统的运行示意图, 如下图所示:
在这里插入图片描述

从上图可以看出,前后台系统的实时性很差,因为大循环中函数处理的事务没有优先级之分, 必须是顺序地被执行处理的,不论待处理事务的紧急程度有多高,没轮到只能等着,虽然中断能够处理一些紧急的事务,但是在一些大型的嵌入式应用中,这样的单任务系统就会显得力不从心。

1.2 多任务系统

多任务系统在处理事务的实时性上比单任务系统要好得多,从宏观上来看,多任务系统的多个任务是可以“同时” 运行的,因此紧急的事务就可以无需等待 CPU 处理完其他事务,在被处理。
要注意的是多任务系统的多个任务可以“同时” 运行,是从宏观的角度而言的,对于单核的 CPU 而言, CPU 在同一时刻只能够处理一个任务,但是多任务系统的任务调度器会根据相关的任务调度算法,将 CPU 的使用权分配给任务,在任务获取 CPU 使用权之后的极短时间(宏观角度)后,任务调度器又会将 CPU 的使用权分配给其他任务,如此往复,在宏观的角度看来,就像是多个任务同时运行了一样。
多任务系统的运行示意图,如下图所示:
在这里插入图片描述

从上图可以看出, 相较于单任务系统而言,多任务系统的任务也是具有优先级的,高优先级的任务可以像中断的抢占一样,抢占低优先级任务的 CPU 使用权;优先级相同的任务则各自轮流运行一段极短的时间(宏观角度),从而产生“同时”运行的错觉。 以上就是抢占式调度和时间片调度的基本原理。
在任务有了优先级的多任务系统中,用户就可以将紧急的事务放在优先级高的任务中进行处理,那么整个系统的实时性就会大大地提高。

2 FreeRTOS 任务状态

FreeRTOS 中任务存在四种任务状态,分别为运行态、就绪态、阻塞态和挂起态。 FreeRTOS运行时,任务的状态一定是这四种状态中的一种,下面就分别来介绍一下这四种任务状态。
1.运行态
如果一个任务得到CPU 的使用权,即任务被实际执行时,那么这个任务处于运行态。如果
运行 RTOS 的 MCU 只有一个处理器核心,那么在任务时刻,都只能有一个任务处理运行态。
2. 就绪态
如果一个任务已经能够被执行(不处于阻塞态后挂起态),但当前还未被执行(具有相同优
先级或更高优先级的任务正持有 CPU 使用权),那么这个任务就处于就绪态。
3. 阻塞态
如果一个任务因延时一段时间或等待外部事件发生,那么这个任务就处理阻塞态。例如任
务调用了函数vTaskDelay(),进行一段时间的延时,那么在延时超时之前,这个任务就处理阻塞
态。任务也可以处于阻塞态以等待队列、信号量、事件组、通知或信号量等外部事件。通常情
况下,处于阻塞态的任务都有一个阻塞的超时时间,在任务阻塞达到或超过这个超时时间后,
即使任务等待的外部事件还没有发生,任务的阻塞态也会被解除。
要注意的是,处于阻塞态的任务是无法被运行的。
4. 挂起态
任务一般通过函数 vTaskSuspend()和函数 vTaskResums()进入和退出挂起态与阻塞态一样,
处于挂起态的任务也无法被运行。
四种任务状态之间的转换图如下图所示:
在这里插入图片描述

3 FreeRTOS 任务优先级

任务优先级是决定任务调度器如何分配 CPU 使用权的因素之一。 每一个任务都被分配一个0~(configMAX_PRIORITIES-1)的任务优先级,宏 configMAX_PRIORITIES 在 FreeRTOSConfig.h文件中定义
如果在 FreeRTOSConfig.h文件中,将宏 configUSE_PORT_OPTIMISED_TASK_SELECTION定义为 1,那么 FreeRTOS 则会使用特殊方法计算下一个要运行的任务,这种特殊方法一般是使用硬件计算前导零指令,对于 STM32 而言,硬件计算前导零的指令,最大支持 32 位的数,因此宏 configMAX_PRIORITIES 的值不能超过 32。当然,系统支持的优先级数量越多,系统消耗的资源也就越多,因此读者在实际的工程开发当中,应当合理地将宏 configMAX_PRIORITIES定义为满足应用需求的最小值。
FreeRTOS 的任务优先级高低与其对应的优先级数值,是成正比的,也就是说任务优先级数值为 0 的任务优先级是最低的任务优先级,任务优先级数值为(configMAX_PRIORITIES-1)的任务优先级是最高的任务优先级。 FreeRTOS 的任务优先级高低与其对应数值的逻辑关系正好与STM32 的中断优先级高低与其对应数值的逻辑关系相反,如下图所示,因此作为刚入门FreeRTOS 的读者,特别注意
在这里插入图片描述

4 Free RTOS 任务调度方式

FreeRTOS 一共支持三种任务调度方式,分别为抢占式调度、时间片调度和协程式调度。在 FreeRTOS 官方的在线文档中, FreeRTOS 官方对协程式调度做了特殊说明,说明如下图所示:

FreeRTOS 官方对协程式调度的特殊说明,翻译过来就是“协程式调度是用于一些资源非常少的设备上的,但是现在已经很少用到了。虽然协程式调度的相关代码还没有被删除,但是今后也不打算继续开发协程式调度。”
可以看出, FreeRTOS 官方已经不再开发协程式调度了,因此笔者并不推荐读者在开发中使用协程式调度。协程式调度是专门为资源十分紧缺的设备开发的,因此使用协程式调度也会有受到很多的限制,但是现在 MCU 的资源都已经十分富裕了,因此也就没有必要再使用和学习协程式调度了,本开发文档也就不再提供协程式调度的相关教程。

4.1 抢占式调度

抢占式调度主要时针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占优先级低的任务,只有当优先级高的任务发生阻塞或者被挂起,低优先级的任务才可以运行。

4.2 时间片调度

时间片调度主要针对优先级相同的任务,当多个任务的优先级相同时, 任务调度器会在每一次系统时钟节拍到的时候切换任务,也就是说 CPU 轮流运行优先级相同的任务,每个任务运行的时间就是一个系统时钟节拍。 有关系统时钟节拍的相关内容,在下文讲解 FreeRTOS 系统时钟节拍的时候会具体分析

5 FreeRTOS 任务控制块

FreeRTOS 中的每一个已创建任务都包含一个任务控制块,任务控制块是一个结构体变量,FreeRTOS 用任务控制块结构体存储任务的属性。
任务控制块的定义如以下代码所示:

typedef struct tskTaskControlBlock
{
/* 指向任务栈栈顶的指针 */
volatile StackType_t * pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 )
/* MPU 相关设置 */
xMPU_SETTINGS xMPUSettings;
#endif
/* 任务状态列表项 */
ListItem_t xStateListItem;
/* 任务等待事件列表项 */
ListItem_t xEventListItem;
/* 任务的任务优先级 */
UBaseType_t uxPriority;
/* 任务栈的起始地址 */
StackType_t * pxStack;
/* 任务的任务名 */
char pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
/* 指向任务栈栈底的指针 */
StackType_t * pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
/* 记录任务独自的临界区嵌套次数 */
UBaseType_t uxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
/* 由系统分配(每创建一个任务,值增加一),分配任务的值都不同,用于调试 */
UBaseType_t uxTCBNumber;
/* 由函数 vTaskSetTaskNumber()设置,用于调试 */
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 )
/* 记录任务处于运行态的时间 */
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter;
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
/* 用于 Newlib */
struct  _reent xNewLib_reent;
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
/* 任务通知值 */
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
/* 任务通知状态 */
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
/* 任务静态创建标志 */
uint8_t ucStaticallyAllocated;
#endif
#if ( INCLUDE_xTaskAbortDelay == 1 )
/* 任务被中断延时标志 */
uint8_t ucDelayAborted;
#endif
#if ( configUSE_POSIX_ERRNO == 1 )
/* 用于 POSIX */
int iTaskErrno;
#endif
} tskTCB;
typedef struct tskTaskControlBlock * TaskHandle_t;

从上面的代码可以看出, FreeRTOS 的任务控制块结构体中包含了很多成员变量,但是,大部分的成员变量都是可以通过 FreeRTOSConfig.h 配置文件中的配置项宏定义进行裁剪的。

6 FreeRTOS 任务栈

不论是裸机编程还是 RTOS 编程,栈空间的使用的非常重要。函数中的局部变量、函数调用时的现场保护和函数的返回地址等都是存放在栈空间中的。
对于 FreeRTOS,当使用静态方式创建任务时,需要用户自行分配一块内存,作为任务的栈空间, 静态方式创建任务的函数原型如下所示:

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

其中函数的参数 ulStackDepth,为任务栈的大小;参数 puxStackBuffer,为任务的栈的内存空间。 FreeRTOS 会根据这两个参数,为任务设置好任务的栈。
而使用动态方式创建任务时,系统则会自动从系统堆中分配一块内存,作为任务的栈空间,动态方式创建任务的函数原型如下所示:

BaseType_t xTaskCreate( TaskFunction_t                 pxTaskCode,
const char * const             pcName,
const configSTACK_DEPTH_TYPE   usStackDepth,
void * const                   pvParameters,
UBaseType_t                    uxPriority,


TaskHandle_t * const           pxCreatedTask)

其中函数的参数usStackDepth,即为任务栈的大小。FreeRTOS会根据栈的大小,从FreeRTOS的系统堆中分配一块内存,作为任务的栈空间。
值得一提的是, 参数 usStackDepth 表示的任务栈大小,实际上是以字为单位的,并非以字节为单位。对于静态方式创建任务的函数 xTaskCreateStatic(), 参数 usStackDepth 表示的是作为任务栈且其数据类型为 StackType_t 的数组 puxStackBuffer 中元素的个数;而对于动态方式创建任务的函数 xTaskCreate(),参数 usStackDepth 将被用于申请作为任务栈的内存空间,其内存申请相关代码,如下所示:

pxStack = pvPortMallocStack((((size_t)usStackDepth) * sizeof(StackType_t)));

可以看出, 静态和动态创建任务时,任务栈的大小都与数据类型 StackType_t 有关, 对于STM32 而言,该数据类型的相关定义,如下所示:

#define portSTACK_TYPE  uint32_t
typedef portSTACK_TYPE  StackType_t;

因此, 不论是使用静态方式创建任务还是使用动态方式创建任务, 任务的任务栈大小都应该为 ulStackDepth*sizeof(uint32_t)字节,即 ulStackDepth 字。

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

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

相关文章

手把手教你搭建网站(零基础,不用写代码)

没有前言直接开始正文,搭建一个博客需要服务器,域名,博客程序。 博客程序常用的有wordpress,z-blog,typecho等等,其中wordpress和z-blog最为简单,typecho需要一定的技术含量,这里暂…

17.4:图的拓扑排序

拓扑序一定是有向无环图。 拓扑排序不唯一 拓扑排序方法一: 利用入度为零进行排序 思路:谁的入度为0,谁就是开始节点,将开始节点打印完之后,将开始节点的直接邻居的入度减1,然后重复。 package algorith…

【软件测试】稳定性和可靠性测试在软件开发中的重要性

软件测试的某些方面经常会在那些刚接触流程的人中造成混淆——例如在稳定性和可靠性测试之间划清界限。 两者通常可以互换使用,并且有一个共同的目标,即确保系统可以在选定的时间范围内稳定运行。 在这篇文章中,我们将仔细研究什么是稳定性测…

10年Java开发,准备去腾讯了~

大家好,最近有不少小伙伴在后台留言,又得准备面试了,不知道从何下手! 不论是跳槽涨薪,还是学习提升!先给自己定一个小目标,然后再朝着目标去努力就完事儿了! 为了帮大家节约时间&a…

chatgpt赋能python:Python分词,助力文本处理和搜索引擎优化

Python分词,助力文本处理和搜索引擎优化 作为一种广泛应用于文本处理的编程语言,Python在分词处理方面也有着得天独厚的优势。Python分词不仅可以帮助我们完成文本处理任务,还能够为搜索引擎优化提供便利。 什么是分词? 分词&a…

集线器与交换机的区别

1.集线器与交换机的区别 笔记来源: 湖科大教书匠:集线器与交换机的区别 声明:该学习笔记来自湖科大教书匠,笔记仅做学习参考 1.1 集线器 早期总线型以太网的机械连接点不太可靠 使用双绞线和集线器相对可靠 集线器可以在物理层…

计算机网络和因特网概述

一.计算机网络和因特网 计算机网络是指多台计算机通过通信线路或其他连接方式相互连接起来,实现信息交换和共享的系统。而因特网是利用标准化协议互联起来的全球性计算机网络,是计算机网络的一种特殊形式。 1.1 什么是因特网 1.1.1 从具体构成描述 因…

day04——特征处理之特征降维

特征处理之特征降维 一、特征选择Filter(过滤式)1,低方差特征过滤2、相关系数 Embedded (嵌入式) 二、主成分分析(PCA) 特征降维:如果特征本身存在问题或者特征之间相关性较强,对于算法学习预测会影响较大。降维是指在…

设计模式-解释器模式

解释器模式 问题背景解释器模式基本介绍原理类图 使用解释器模式来解决问题UML类图代码示例运行结果 注意事项和细节 问题背景 通过解释器模式来实现一个表达式的加减运算 例如:在我们先输入一个表达式:abc-de,然后输入a,b&#…

layui框架实战案例(22):layui-tab-title的宽度自适应的解决方案

HTML源代码 <div class"layui-card"><div class"layui-card-header">卡片风格</div><div class"layui-card-body"><div class"layui-tab layui-tab-card"><ul class"layui-tab-title">…

酷游SVG-地址路径

说到SVG中最复杂的东西&#xff0c;路径(Path)绝对是一个大魔王&#xff0c;它有太多的commands可以用来定义路径。【娜娜提供酷游地kw9㍠neт地址】透过Path虽然可以绘制效果不错的SVG图形&#xff0c;但是要自己定义一个个坐标点&#xff0c;再去把它们完美的串连在一起&…

《银行从业法律法规》第二章——金融基础知识

第二章 金融基础知识 第二节 货币政策 【 知识点1】 货币政策目标 制定和实施货币政策&#xff0c; 首先必须明确货币政策最终要达到的目的&#xff0c; 即货币政策的最终目标。中央银行通过货币政策工具操作直接引起操作目标的变动&#xff0c;操作目标的变动又通过一定的途…

libarchive 如何在Ubuntu中进行安装

简介 libarchive是一个开源的压缩和归档库。 它支持实时访问多种压缩文件格式&#xff0c;比如7z、zip、cpio、pax、rar、cab、uuencode等&#xff0c;因此应用十分广泛。 举个例子&#xff0c;我写了一段代码&#xff0c;想要解压一个压缩包&#xff0c;那我就得 fork 一个 …

python制作简单版天天酷跑,是不是你平日里摸鱼小游戏呀

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 天天酷跑是一款轻松好玩、简单时尚的跑酷类手机游戏。 那我们能不能用python模拟出这个一个游戏呢&#xff1f; 答案当然是可以的&#xff0c;今天我就给大家带来简易版的天天酷跑小游戏 开发环境&#xff1a; 版 本&a…

如何用Python写个网页爬取程序

如何用Python写个网页爬取程序 准备开发工具安装PythonPython安装pipPip安装爬取插件准备好网页地址代码实现 准备开发工具 额&#xff0c;作者用的是vscode。具体怎么安装自行百度哈&#xff0c;这个都不会建议就不要学爬取了。 不忍心藏着也&#xff0c;给你个方法吧 vsc…

2023年第六届广西大学生程序设计竞赛(热身赛)题解

题目均来自去年的省赛原题 参考资料 知乎&#xff1a;第五届GXCPC广西大学生程序设计竞赛 部分题解&#xff08;无CDK&#xff09; A题送分题&#xff0c;跳过 B 位运算lowbit函数 题目大意&#xff1a; 对一个数&#xff08;二进制&#xff09;进行操作&#xff0c;询问使其…

C++11/C++14中constexpr的使用

常量表达式(const expression)是指值不会改变并且在编译过程中就能得到计算结果的表达式。字面值属于常量表达式&#xff0c;用常量表达式初始化的const对象也是常量表达式。 只要有可能使用constexpr&#xff0c;就使用它。 C11中constexpr的使用&#xff1a; constexpr是C11中…

MKS SERVO4257D 闭环步进电机_系列7 MODBUS-RTU通讯示例

第1部分 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口和RS485/CAN串行接口&#xff0c;支持MODBUS-RTU通讯协议&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&a…

论坛项目学习记录【预备篇2】

论坛项目学习记录【预备篇2】 1. 什么是依赖注入2. 怎么使用依赖注入依赖注入注意事项 3.组件扫描情况下依赖注入的实现4.Resource注解的使用与Autowired的区别5.SpringMvcSpringMvc执行流程 1. 什么是依赖注入 就是在Spring容器内容将各个对象的依赖关系建立好的操作&#xf…

持续集成和持续交付:构建高效的软件交付流水线

在现代软件开发中&#xff0c;持续集成&#xff08;Continuous Integration&#xff09;和持续交付&#xff08;Continuous Delivery&#xff09;已成为构建高效、可靠软件交付流水线的关键实践。通过自动化和频繁地集成代码、构建、测试和部署&#xff0c;团队能够更快地交付高…