FreeRTOS的任务优先级、Tick以及状态讲解(尊敬的嵌入式工程师,不妨进来喝杯茶)

任务优先级和Tick

        在FreeRTOS中,任务的优先级和Tick是两个关键的概念,它们直接影响任务的调度和执行。

任务优先级

        每个任务都被分配一个优先级,用于决定任务在系统中的调度顺序。

        优先级是一个无符号整数,通常从0开始,数值越小,优先级越高。最高优先级是0,最低是configMAX_PRIORITIES - 1。

        优先级的取值范围是:0~(configMAX_PRIORITIES – 1),数值越大优先级越高。

        FreeRTOS的调度器可以使用2种方法来快速找出优先级最高的、可以运行的任务。使用不同的方法时,configMAX_PRIORITIES 的取值有所不同。

(1)通用方法
        使用C函数实现,对所有的架构都是同样的代码。对configMAX_PRIORITIES的取值没有限制。但是configMAX_PRIORITIES的取值还是尽量小,因为取值越大越浪费内存,也浪费时间。configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为0、或者未定义时,使用此方法。
(2)架构相关的优化的方法
        架构相关的汇编指令,可以从一个32位的数里快速地找出为1的最高位。使用这些指令,可以快速找出优先级最高的、可以运行的任务。使用这种方法时,configMAX_PRIORITIES的取值不能超过32。configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为1时,使用此方法。

        在FreeRTOS中,优先级为0的任务是IDLE任务,用于在没有其他任务执行时占用CPU。

        任务的创建时通过指定参数设置任务的优先级,如下所示:

xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1_Handle);

Tick

        Tick是FreeRTOS内核用于计时和任务调度的基本时间单元。

        FreeRTOS通过硬件定时器或者软件定时器,每隔一定时间(即Tick间隔)产生一次中断。

        Tick的间隔由configTICK_RATE_HZ定义,它表示每秒钟产生的Tick数。

关系

        任务的调度是基于优先级的,具有更高优先级的任务将在具有较低优先级的任务之前执行。

        Tick间隔决定了系统时钟的精度,同时也影响了任务的延时和时间控制。

        任务在等待一定时间或进行延时时,使用vTaskDelay()等函数,参数是以Tick为单位的时间。

vTaskDelay(100 / portTICK_PERIOD_MS); // 暂停任务100ms

        任务的优先级和Tick的概念结合在一起,形成了FreeRTOS中任务调度和时间控制的基础。通过调整任务的优先级和配置Tick的间隔,可以灵活地控制系统中任务的执行顺序和时间行为。

        以下是一个基于FreeRTOS的STM32F103芯片的简单优先级实验案例代码。该例子创建了两个任务,分别以不同的优先级运行,以演示优先级如何影响任务的调度。

#include "stm32f1xx.h"
#include "FreeRTOS.h"
#include "task.h"// 函数原型
void SystemClock_Config(void);
static void MX_GPIO_Init(void);// 任务函数
TaskHandle_t Task1_Handle, Task2_Handle;void vTask1(void *pvParameters) {while (1) {// 任务1的处理逻辑HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED状态vTaskDelay(1000 / portTICK_PERIOD_MS); // 每隔1秒执行一次}
}void vTask2(void *pvParameters) {while (1) {// 任务2的处理逻辑vTaskDelay(2000 / portTICK_PERIOD_MS); // 每隔2秒执行一次}
}int main(void) {// 硬件初始化HAL_Init();SystemClock_Config();MX_GPIO_Init();// 创建任务1(优先级1)xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1_Handle);// 创建任务2(优先级2)xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &Task2_Handle);// 启动调度器vTaskStartScheduler();// 代码不应该运行到这里,如果运行到这里说明发生了错误while (1) {// 处理错误}
}// 系统时钟配置
void SystemClock_Config(void) {
}// GPIO初始化
static void MX_GPIO_Init(void) {
}

        在这个例子中,任务1和任务2分别在不同的时间间隔内切换LED的状态。任务1的优先级较高,它每隔1秒执行一次,而任务2的优先级较低,它每隔2秒执行一次。

任务状态

        在FreeRTOS中,较高优先级的任务将优先于较低优先级的任务执行。任务优先级是通过任务创建时指定的参数来设置的(在xTaskCreate函数的第5个参数)。在这个案例中,任务1的优先级为1,任务2的优先级为2。

        FreeRTOS中的任务可以处于不同的状态,这些状态反映了任务在系统中的执行阶段。任务的状态是通过eTaskState枚举类型表示的,定义在task.h头文件中。以下是任务可能的状态:

        eRunning(运行中): 任务正在执行。这是任务处于活动状态的时候。

        eReady(就绪): 任务已经准备好执行,但由于有其他高优先级任务在运行,该任务暂时没有被调度。

        eBlocked(阻塞): 任务由于等待某些事件而被阻塞,例如等待定时器、消息队列、信号量等。

        eSuspended(挂起): 任务被显式地挂起,不再参与调度。

        eDeleted(已删除): 任务已被删除,但其资源尚未被释放。

        eInvalid(无效): 无效状态,通常用于表示错误或未知状态。

你可以使用eTaskGetState函数来获取任务的当前状态。以下是一个示例代码:

#include "FreeRTOS.h"
#include "task.h"TaskHandle_t xTaskHandle;void vTaskFunction(void *pvParameters) {while (1) {// 任务的处理逻辑}
}int main(void) {// 创建任务xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);// 启动调度器vTaskStartScheduler();// 代码不应该运行到这里,如果运行到这里说明发生了错误while (1) {// 处理错误}
}void vApplicationIdleHook(void) {// 空闲钩子函数eTaskState taskState = eTaskGetState(xTaskHandle);switch (taskState) {case eRunning:// 任务正在运行break;case eReady:// 任务就绪break;case eBlocked:// 任务被阻塞break;case eSuspended:// 任务被挂起break;case eDeleted:// 任务已删除break;case eInvalid:// 无效状态break;}
}

        在这个例子中,vApplicationIdleHook是一个空闲钩子函数,用于在系统空闲时检查任务的状态。函数eTaskGetState返回任务的当前状态,然后可以根据任务的状态进行相应的处理。

阻塞状态(Blocked State)

        在FreeRTOS中,任务的阻塞状态(Blocked State)表示任务由于等待某些事件而无法执行。任务可以因为多种原因而被阻塞,包括等待定时器、等待消息队列、等待信号量等。当任务处于阻塞状态时,它将不会被调度执行,直到满足了其阻塞条件。

        以下是几个常见的阻塞状态的示例:

        等待定时器:

// 创建定时器
TimerHandle_t xTimer = xTimerCreate("MyTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, vTimerCallback);// 启动定时器,在任务中使用xTimerPendFunctionCallFromISR等函数
xTimerStart(xTimer, 0);

        在任务中可能会使用xTimerPendFunctionCallFromISR等函数等待定时器的超时事件。

        等待消息队列:

// 创建消息队列
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int));// 在任务中等待消息队列
xQueueReceive(xQueue, &data, portMAX_DELAY);

        任务通过xQueueReceive函数等待消息队列中的消息,如果队列为空,任务将被阻塞,直到队列中有数据。

        等待信号量:

// 创建信号量
SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();// 在任务中等待信号量
xSemaphoreTake(xSemaphore, portMAX_DELAY);

        任务通过xSemaphoreTake函数等待信号量,如果信号量被其他任务取得,当前任务将被阻塞,直到信号量可用。

        在上述示例中,portMAX_DELAY表示任务将一直等待,直到满足其阻塞条件为止。任务也可以使用超时值来设置阻塞的最大等待时间。一旦阻塞条件得到满足,任务将被置为就绪状态,等待调度器调度执行。

暂停状态(Suspended State)

        在FreeRTOS中,任务的暂停状态(Suspended State)表示任务被显式地挂起,使得该任务不再参与调度,即不会被执行。任务在创建后可以通过调用vTaskSuspend()函数将其挂起,然后通过调用vTaskResume()函数将其恢复执行。

以下是一个简单的示例代码,演示了任务的暂停和恢复:

#include "FreeRTOS.h"
#include "task.h"TaskHandle_t xTaskHandle;void vTaskFunction(void *pvParameters) {while (1) {// 任务的处理逻辑}
}int main(void) {// 创建任务xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);// 启动调度器vTaskStartScheduler();// 代码不应该运行到这里,如果运行到这里说明发生了错误while (1) {// 处理错误}
}// 在某个事件或条件下挂起任务
void vSuspendTask(void) {vTaskSuspend(xTaskHandle);
}// 在某个事件或条件下恢复任务
void vResumeTask(void) {vTaskResume(xTaskHandle);
}

        在这个例子中,vTaskSuspend()函数用于挂起任务,vTaskResume()函数用于恢复任务。通常,这样的操作可以用于在某些条件满足或事件发生时挂起任务,然后在其他条件下恢复任务的执行。

        需要注意的是,挂起任务不会立即停止任务的执行,而是在任务下一次被调度执行时生效。此外,挂起任务并不会释放任务所占用的资源,因此在使用这些函数时需要谨慎,以免引起资源泄漏。

就绪状态(Ready)

        在FreeRTOS中,任务的就绪状态(Ready State)表示任务已经准备好被调度执行,但由于有其他高优先级的任务正在运行,该任务暂时还未被调度。

        在FreeRTOS中,任务被创建后,它的状态一开始就是就绪状态,等待调度器来选择合适的时机执行它。任务的就绪状态可以由以下几种情况触发:

        任务创建: 任务被创建后,会进入就绪状态,等待调度执行。

xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);

        任务挂起状态解除: 任务从挂起状态(Suspended State)被恢复后,会进入就绪状态。

vTaskResume(xTaskHandle);

        等待事件结束: 任务等待某个事件的发生,一旦事件发生,任务从阻塞状态切换到就绪状态。

xSemaphoreTake(xSemaphore, portMAX_DELAY);

        任务等待定时器超时: 任务等待一个定时器超时,一旦超时,任务从阻塞状态切换到就绪状态。

xTaskDelay(1000 / portTICK_PERIOD_MS);

        在以上例子中,xTaskCreate创建了一个任务,vTaskResume恢复了一个挂起的任务,xSemaphoreTake和xTaskDelay是任务等待事件或超时的示例。

        任务的就绪状态是由FreeRTOS调度器根据任务的优先级和调度算法来决定的。当调度器判定某个任务处于就绪状态时,该任务将会被调度器选中,并执行相应的任务函数。

任务状态转换图

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

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

相关文章

Mysql- 流程函数-(If, CASE WHEN)的使用及练习

目录 4.1 If函数语法格式 4.2 CASE WHEN 条件表达式格式 4.3 update与 case when 4.4 练习题1 4.5 练习题2 4.6 练习题3-行转列 4.7 牛客练习题 4.8 LeetCode练习题 4.1 If函数语法格式 IF(expr1,expr2,expr3) 解释&#xff1a; 如果表达式expr1true(expr1 <>…

力扣第 119 场双周赛(Java)

文章目录 T1 找到两个数组中的公共元素代码解释 T2 消除相邻近似相等字符代码解释 T3 最多 K 个重复元素的最长子数组代码解释 T4 关闭分部的可行集合数目代码解释 链接&#xff1a;第 119 场双周赛 - 力扣&#xff08;LeetCode&#xff09; T1 找到两个数组中的公共元素 给你…

Xcode doesn’t support iOS 16.6

xocde版本低&#xff0c;手动放入16.6的依赖文件 https://gitee.com/qiu1993/iOSDeviceSupport/blob/master/iOS16/16.6.zip 路径 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

JAVA全栈开发 day21_JDBC与反射结合、设计模式

一、总结 一阶段 day01 java 发展&#xff0c;java 环境( path, java_home, class_path)&#xff0c;java 原理&#xff0c; java 执行 &#xff0c; jvm , jre , jdk day02 变量 标识符命名规则 数据类型 数据类型的转换 运算符 day03 选择结构 if , switch day04 循环结…

分割回文串

分割回文串 描述 : 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 题目 : LeetCode 131.分割回文串 : 131. 分割回文串 分析 : 字符串如何判断回文本…

20 Redis进阶 - 运维监控

1、理解Redis监控 Redis运维和监控的意义不言而喻&#xff0c;可以以下三个方面入手 1.首先是Redis自身提供了哪些状态信息&#xff0c;以及有哪些常见的命令可以获取Redis的监控信息; 2.一些常见的UI工具可以可视化的监控Redis; 3.理解Redis的监控体系;2、Redis自身状态及命…

Vue3-02-ref() 响应式详解

ref() 是什么 ref() 是一个函数&#xff1b; ref() 函数用来声明响应式的状态&#xff08;就是来声明变量的&#xff09; ref() 函数声明的变量&#xff0c;是响应式的&#xff0c;变量的值改变之后&#xff0c;页面中会自动重新渲染。ref() 有什么特点 1.ref() 可以声明基础…

VUE语法--toRefs与toRef用法

1、功能概述 ref和reactive能够定义响应式的数据&#xff0c;当我们通过reactive定义了一个对象或者数组数据的时候&#xff0c;如果我们只希望这个对象或者数组中指定的数据响应&#xff0c;其他的不响应。这个时候我们就可以使用toRefs和toRef实现局部数据的响应。 toRefs是…

算一算并输出2到正整数n中每个数的质因子(for循环)

计算并输出2到正整数n之间每个数的质因子&#xff0c;并以乘法形式输出。 输入格式: 输入只有1个正整数即n。 输出格式: 把2到正整数n间的每一个数分解成它的质因子&#xff0c;并以乘法的形式输出。例如&#xff0c;输入的正整数n值为10&#xff0c;则应输出如下&#xff…

MIT线性代数笔记-第28讲-正定矩阵,最小值

目录 28.正定矩阵&#xff0c;最小值打赏 28.正定矩阵&#xff0c;最小值 首先正定矩阵是一个实对称矩阵 由第 26 26 26讲的末尾可知正定矩阵有以下四种判定条件&#xff1a; 所有特征值都为正左上角所有 k k k阶子矩阵行列式都为正&#xff08; 1 ≤ k ≤ n 1 \le k \le n …

DDD系列 - 第6讲 仓库Repository及Mybatis、JPA的取舍(一)

目录 一、领域层定义仓库接口1.1 设计聚合1.2 定义仓库Repository接口二 、基础设施层实现仓库接口2.1 设计数据库2.2 集成Mybatis2.3 引入Convetor2.4 实现仓库三、回顾一、领域层定义仓库接口 书接上回,之前通过一个关于拆解、微服务、面向对象的故事,向大家介绍了如何从微…

简单的WEB服务器

优质博文&#xff1a;IT-BLOG-CN 目的&#xff1a; 了解Java Web服务器是如何运行的。Web服务器使用HTTP与其客户端&#xff0c;也就是Web浏览器进行通信。基于Java的Web服务器会使用两个重要类&#xff1a;java.net.Socket类和java.net.ServerSocket类&#xff0c;并通过发送…

详解Keras3.0 Models API: Model class

1、语法 keras.Model() 将不同层组为具有训练/推理特征的对象的模型 2、示例一 inputs keras.Input(shape(37,)) x keras.layers.Dense(32, activation"relu")(inputs) outputs keras.layers.Dense(5, activation"softmax")(x) model keras.Model…

58.Nacos源码分析2

三、服务心跳。 3.服务心跳 Nacos的实例分为临时实例和永久实例两种&#xff0c;可以通过在yaml 文件配置&#xff1a; spring:application:name: order-servicecloud:nacos:discovery:ephemeral: false # 设置实例为永久实例。true&#xff1a;临时; false&#xff1a;永久ser…

MySQL-备份+日志:介质故障与数据库恢复

目录 第1关&#xff1a;备份与恢复 第2关&#xff1a;备份日志&#xff1a;介质故障的发生与数据库的恢复 第1关&#xff1a;备份与恢复 任务描述 本关任务: 备份数据库&#xff0c;然后再恢复它。 test1_1.sh # 你写的命令将在linux的命令行运行 # 对数据库residents作海…

【C/C++笔试练习】多态的概念、虚函数的概念、虚表地址、派生类的虚函数、虚函数的访问、指针引用、动态多态、完全数计算、扑克牌大小

文章目录 C/C笔试练习选择部分&#xff08;1&#xff09;多态的概念&#xff08;2&#xff09;虚函数的概念&#xff08;3&#xff09;虚表地址&#xff08;4&#xff09;派生类的虚函数&#xff08;5&#xff09;虚函数的访问&#xff08;6&#xff09;分析程序&#xff08;7&…

C# WPF上位机开发(会员管理软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 好多同学都认为上位机只是纯软件开发&#xff0c;不涉及到硬件设备&#xff0c;比如听听音乐、看看电影、写写小的应用等等。如果是消费电子&#…

HibernateJPA快速搭建

1. 先创建一个普通Maven工程&#xff0c;导入依赖 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><depe…

Java 匿名内部类使用的外部变量,为什么一定要加 final?

问题描述 Effectively final Java 1.8 新特性&#xff0c;对于一个局部变量或方法参数&#xff0c;如果他的值在初始化后就从未更改&#xff0c;那么该变量就是 effectively final&#xff08;事实 final&#xff09;。 这种情况下&#xff0c;可以不用加 final 关键字修饰。 …

报错:Parsed mapper file: ‘file mapper.xml 导致无法启动

报错 &#xff1a; Logging initialized using class org.apache.ibatis.logging.stdout.StdOutImpl adapter. Registered plugin: com.github.yulichang.interceptor.MPJInterceptor3b2c8bda Parsed mapper file: file [/Mapper.xml] application无法启动 我这边产生原因是项…