当前位置: 首页 > news >正文

FreeRTOS入门(02):任务基础使用与说明

文章目录

  • 目的
  • 创建任务
  • 任务调度
  • 任务控制
    • 延时函数
    • 任务句柄
    • 获取与修改任务优先级
    • 删除任务
    • 挂起与恢复任务
    • 强制任务离开阻塞状态
  • 空闲任务
  • 总结

目的

任务(Task)是FreeRTOS中供用户使用的最核心的功能,本文将介绍任务创建与使用相关的基础内容。

本文接上篇:《FreeRTOS入门(01):基础说明与使用演示》

创建任务

创建任务主要使用 xTaskCreate 这个函数:

// 创建成功会返回pdPASS(1),失败通常返回errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(-1),即内存不足
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,                 // 任务函数const char * const pcName,                 // 任务名称// 长度由FreeRTOSConfig.h中configMAX_TASK_NAME_LEN定义const configSTACK_DEPTH_TYPE usStackDepth, // 该任务栈深度(栈大小),对于32位架构一个深度为四字节void * const pvParameters,                 // 传递给任务的参数UBaseType_t uxPriority,                    // 任务优先级,值越大优先级越高// 最大值为FreeRTOSConfig.h中configMAX_PRIORITIES - 1TaskHandle_t * const pxCreatedTask )       // 任务句柄,后续可以用过该句柄来操作任务

任务需要使用 vTaskStartScheduler(); 来调度运行,通常将该行语句放在程序主循环前面,正常情况下程序将在这里无限循环。

下面是个基础的创建和使用任务的演示,使用CH32V307的FreeRTOS项目模板方式创建项目,替换 main.c 为下面内容:

#include "debug.h"
#include "FreeRTOS.h" // 引入头文件
#include "task.h"     // 引入头文件/* Task1相关参数与任务处理函数 */
void task1_task(void *pvParameters)
{while(1){printf("TickCount: %u\r\n", xTaskGetTickCount()); // 打印FreeRTOS时间刻vTaskDelay(250); // FreeRTOS的延时函数// FreeRTOSConfig.h中configTICK_RATE_HZ为500,即每2ms调度一次// 所以这里的延时时间是 250 * 2 = 500ms}
}/* 主函数 */
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task1_task, "task1", 256, NULL, 5, NULL); // 创建一个任务vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环while(1){} // 程序不会运行到这里
}

在这里插入图片描述

同一个任务的函数可以用来创建不同的任务,因为默认情况下不同任务有不同的堆栈:

#include "debug.h"
#include "FreeRTOS.h" // 引入头文件
#include "task.h"     // 引入头文件/* Task1相关参数与任务处理函数 */
void task(void *pvParameters)
{const char *pcTaskText = pvParameters;while(1){printf("%s\r\n", pcTaskText);vTaskDelay(500);}
}/* 主函数 */
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);xTaskCreate(task, "task1", 256, "task 1 run", 5, NULL); // 创建一个任务xTaskCreate(task, "task2", 256, "task 2 run", 5, NULL); // 创建一个任务vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环while(1){} // 程序不会运行到这里
}

在这里插入图片描述

任务也可以动态的创建:

#include "debug.h"
#include "FreeRTOS.h" // 引入头文件
#include "task.h"     // 引入头文件/* Task2相关参数与任务处理函数 */
void task2(void *pvParameters){while(1){printf("task 2 run.\r\n");vTaskDelay(500);}
}/* Task1相关参数与任务处理函数 */
void task1(void *pvParameters)
{volatile size_t i = 0; // 使用volatile关键词防止编译器优化while(1){printf("task 1 run.\r\n");i++;if (i == 3) {                                        // task1执行3次后xTaskCreate(task2, "task2", 256, NULL, 5, NULL); // 动态创建任务}vTaskDelay(1000);}
}/* 主函数 */
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);SystemCoreClockUpdate();Delay_Init();USART_Printf_Init(115200);printf("FreeRTOS Kernel Version:%s\r\n",tskKERNEL_VERSION_NUMBER); // 打印FreeRTOS内核版本号xTaskCreate(task1, "task1", 256, NULL, 5, NULL); // 创建一个任务vTaskStartScheduler(); // 任务调度,任务将在这里根据情况开始运行,程序将在这里无序循环while(1){} // 程序不会运行到这里
}

在这里插入图片描述

除了使用 xTaskCreate 创建任务,还有一些其它方法可以创建任务,主要是一些使用静态内存分配方式创建任务的方法,目前来说除非要求非常高的场合,一般没必要使用这些方式。

任务调度

通常来说FreeRTOS使用时都会用到一个定时器(常用SysTick),定时器定时产生中断,FreeRTOS每次中断时累加系统时间 Tick (就是前面例子中使用 xTaskGetTickCount() 获取的内容),并检查和调度任务运行。

FreeRTOSConfig.h 文件中 configTICK_RATE_HZ 参数用于设置系统中断频率。在上面项目中默认值为 500 ,表示每秒中断 500 次,即每秒 Tick 值加 500 ,每两次调度发生间隔为 2ms ,这 2ms 其实就是每次被调度到执行的任务工作的时间。

任务在OS中各个阶段通常使用下面状态转换图表示:
在这里插入图片描述
Running 即表示当前正在运行的任务; Ready 表示已经准备好但没有时间可以分配给它运行的任务; Blocked 表示正在等待时间或者其它资源可用的任务(比如前面的vTaskDelay方法就会使任务进入Blocked状态); Suspended 主要是手动使用函数挂起的任务。

除了手动创建的任务,系统还有个空闲任务(优先级为0),在没有用户任务执行的适合空闲任务将会执行。空闲任务中会进行一些资源释放等操作,通常开发是最好保证空闲任务能够定期被调度到。

这里有一个问题,如果当前有多个任务处于就绪状态( Ready ),那么应该调度哪个任务执行?

在上面项目的默认设定下:

  • configUSE_PREEMPTION = 1 (在 FreeRTOSConfig.h 文件中定义)
    可以抢占,多任务同时就绪时最高优先级任务将执行;
  • configUSE_TIME_SLICING = 1 (没有主动定义,默认值为1)
    轮流执行,多个同优先级的项目将轮流交替执行;
  • configUSE_TICKLESS_IDLE = 0 (没有主动定义,默认值为0)
  • configIDLE_SHOULD_YIELD = 0 (在 FreeRTOSConfig.h 文件中定义)
    空闲任务不让出时间;

除了上面的调度逻辑,还有一个逻辑是在运行 vTaskStartScheduler(); 语句前添加的任务,高优先级的会先运行,同优先级的后添加的将先运行。

任务控制

任务创建后可以通过一些函数来控制任务状态,主要可以实现改变任务优先级、删除任务、挂起与恢复任务、阻塞任务等功能。

需要注意的是下面出现的任务控制功能可能并不会在相关函数执行后立即生效(比如任务删除自身),这种情况下需要等到下一次任务调度或是空闲任务执行后才会真正生效。

延时函数

前面例子中有出现延时函数,FreeRTOS提供了延时函数主要有下面两种:

// 从进入vTaskDelay函数到函数返回总时间至少为xTicksToDelay个系统Tick时间
void vTaskDelay(const TickType_t xTicksToDelay)// 从pxPreviousWakeTime系统时间开始至少经过xTimeIncrement个系统时间
BaseType_t xTaskDelayUntil(TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement)
void vTaskDelayUntil(TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement)
// 上面函数中需要用到的系统时间 pxPreviousWakeTime 可以使用下面两个函数获取
TickType_t xTaskGetTickCount(void) // 在普通任务中使用
TickType_t xTaskGetTickCountFromISR(void) // 在中断服务程序中使用

通常来说如果想要设置任务每次执行完成到下次开始执行的延时可以使用第一种延时方式;如果想要任务每次开始执行时间间隔相等那就需要使用第二种方式(周期性任务)。

任务句柄

下面的各种任务控制功能都需要用到任务句柄。任务句柄可以理解为一个结构体,用来保存任务相关的各种内容,所以我们可以通过它来控制指定的任务。

任务句柄类型是 TaskHandle_t ,可以使用下面方式在创建任务时获得:

TaskHandle_t Task_Handler; // 声明任务句柄对象int main(void)
{xTaskCreate(task_func, "name", 256, NULL, 5, &Task_Handler); // 创建任务并获得任务句柄
}

除了在创建时获得任务句柄,也可以使用下面函数获得已创建的任务的任务句柄:

// 获取当前任务任务句柄
// FreeRTOSConfig.h中INCLUDE_xTaskGetCurrentTaskHandle 设置为1才能使用
TaskHandle_t xTaskGetCurrentTaskHandle(void)// 通过任务名称获取任务句柄
// FreeRTOSConfig.h中INCLUDE_xTaskGetHandle设置为1才能使用
TaskHandle_t xTaskGetHandle(const char *pcNameToQuery)

获取与修改任务优先级

获取与修改任务优先级主要由下面函数进行操作:

// 获取任务优先级
UBaseType_t uxTaskPriorityGet(const TaskHandle_t xTask) // 在普通任务中使用
UBaseType_t uxTaskPriorityGetFromISR(const TaskHandle_t xTask) // 在中断服务程序中使用// 设置任务优先级
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority)// xTask可以填入NULL,表示执行该函数的任务自身
// uxNewPriority为欲设的任务优先级,值越大优先级越高,最大值为FreeRTOSConfig.h中configMAX_PRIORITIES - 1

删除任务

可以使用下面函数来删除任务:

void vTaskDelete(TaskHandle_t xTaskToDelete)
// xTaskToDelete可以填入NULL,表示执行该函数的任务自身

挂起与恢复任务

挂起与恢复任务主要由下面函数进行操作:

// 挂起任务
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
// xTaskToSuspend可以填入NULL,表示执行该函数的任务自身// 恢复任务
void vTaskResume(TaskHandle_t xTaskToResume) // 在普通任务中使用
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume) // 在中断服务程序中使用
// 通常恢复任务只能恢复别人,自己不能恢复自己(因为已经被挂起了)// 暂停所有任务调度
void vTaskSuspendAll(void)
// 恢复所有任务调度
BaseType_t xTaskResumeAll(void)

强制任务离开阻塞状态

强制任务离开阻塞状态主要就下面函数进行操作:

BaseType_t xTaskAbortDelay(TaskHandle_t xTask)
// 如果xTask不在阻塞状态则返回pdFAIL,否则返回pdPASS

空闲任务

FreeRTOS调度器启动时,会自动创建空闲任务,以确保始终存在一个能够运行的任务。空闲任务处于最低优先级(0),以确保如果有更高的优先级应用程序任务处于准备就绪状态时空闲任务不会占用任何CPU时间。

空闲任务负责释放删除自身的任务的内存。除了这个功能,空闲任务还可以有一个由用户实现具体功能的钩子函数 void vApplicationIdleHook(void),该函数会在每次空闲任务运行时被调用。

要使用空闲任务钩子函数,需要在 FreeRTOSConfig.h 文件中定义 configUSE_IDLE_HOOK 值为 1 ,然后实现钩子函数:

void vApplicationIdleHook(void) {// TDOD
}

在这里插入图片描述

必须确保钩子函数不能调用任何可能导致空闲任务阻塞的 API 函数 (例如,vTaskDelay () ,或带有阻塞时间的队列或信号量函数 )

空闲钩子函数比较常用的功能有下面一些:

  • 将 CPU 置于省电模式;
  • 测量空闲时间计算 CPU 占用率;
  • 执行一些低优先级,但需要长时间执行的任务;

总结

FreeRTOS中任务的基础使用还是比较简单的。实际项目中通常会有多个任务,任务间多数时候会需要配合工作,这些时候就需要用到 队列、信号量、互斥量 等功能了,这些内容将在后面的文章中进行介绍。

相关文章:

FreeRTOS入门(02):任务基础使用与说明

文章目录目的创建任务任务调度任务控制延时函数任务句柄获取与修改任务优先级删除任务挂起与恢复任务强制任务离开阻塞状态空闲任务总结目的 任务(Task)是FreeRTOS中供用户使用的最核心的功能,本文将介绍任务创建与使用相关的基础内容。 本…...

ESP通过乐为物联控制灯,微信发送数值,ESP上传传感器数据

暂时放个程序 //ME->>{“method”: “update”,“gatewayNo”: “01”,“userkey”: “2b64c489d5f94237bcf2e23151bb7d01”}&^! //Ser->>{“f”:“message”,“p1”:“ok”}&^! //ME->>{“method”: “upload”,“data”:[{“Name”:“A1C”,“Val…...

Linux:共享内存api使用

代码&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <arpa/inet.h> #include <sys/un.h> #include <sys/ipc.h…...

android9.0 java静态库操作JNI实例 动态注册

一、java层 源码 目录&#xff1a;/Demo/java/com/android/simpleini/SimpleJNI.java package com.example.simplejni;import android.app.Activity; import android.os.Bundle; import android.widget.TextView;public class SimpleJNI {private IpoManagerService(Context …...

自定义复杂图片水印

我的社交能力还不如5岁儿童和狗。 文章目录前言一、主要工具类总结前言 之前写过一些简单的图片压缩和图片加水印&#xff1a;JAVA实现图片质量压缩和加水印 本次主要是针对图片加水印进行一个升级&#xff0c;图片水印自定义&#xff0c;自适应大小。 来&#xff0c;先看几…...

文章读后感——《人间清醒,内容为王》

观后感 人间清醒&#xff0c;内容为王 - 技术er究竟该如何写博客&#xff1f;1024上海嘉年华之敖丙演讲观后感。 致敬愿意带领后生的前辈——哈哥 哈哥&#xff0c;《人间清醒&#xff0c;内容为王》这篇&#xff0c;我的鱼获&#xff0c; 是里面传递出来写技术博客的思维与想法…...

51单片机入门 - 驱动多位数码管

我使用的是普中51单片机开发板A2套件&#xff08;2022&#xff09;&#xff0c;驱动数码管可能需要参考电路原理图。开发环境的搭建教程在本专栏的 51单片机开发环境搭建 - VS Code 从编写到烧录 有过介绍。 关于我的软硬件环境信息&#xff1a; Windows 10STC89C52RCSDCC &am…...

Java进击框架:Spring(一)

Java进击框架&#xff1a;Spring&#xff08;一&#xff09;前言创建Spring项目Spring IoC容器和Beans介绍Bean的概述Spring IoC配置元数据实例化Bean依赖注入循环依赖详细配置生命周期回调Bean定义继承基于注解的容器配置Component和进一步的原型注解自动检测类和注册Bean定义…...

Java笔记(18)

目录 什么是mvc? 什么是SpringMVC Spring MVC的特点: 原理: 什么是Thymeleaf? thymeleaf依赖包:...

【免费教程】地下水环境监测技术规范HJ/T164-2020解读使用教程

地下水环境监测技术规范依据《中华人民共和国环境保护法》第十一条“国务院环境保护行政主管部门建立监测制度、制订监测规范”和《中华人民共和国水污染防治法》的要求&#xff0c;积极开展地下水环境监测&#xff0c;掌握地下水环境质量&#xff0c;保护地下水水质&#xff0…...

Html 代码学习

场景:在页面中插入音频 代码 常见属性: src 音频的路径 controls 显示播放的控件 autoplay 自动播放 loop 循环播放 场景:在页面中插入视频 代码 常见属性: src 路径 controls 显示播放的控件 autoplay 自动播放 要配合muted 例如 autoplay muted loop 循环播放 链接 /…...

如何通过IP找到地址?

在我们印象中&#xff0c;我们都知道可以通过 IP 地址找到某个人。但当我们细想一下&#xff0c;我们会发现其实 IP 地址与地理位置并不是直接相关的。那我们到底是如何通过 IP 地址找到地址的呢&#xff1f;答案是&#xff1a;通过自治系统&#xff08;Autonomous System&…...

业务单据堆积如山?如何提升会计做账效率?

某集团以“创建现代能源体系、提高人民生活品质”为使命&#xff0c;形成了贯通下游分销、中游贸易储运、上游生产的清洁能源产业链和涵盖健康、文化、旅游、置业的生命健康产品链。目前&#xff0c;某集团在全国21个省&#xff0c;为超过2681万个家庭用户、21万家企业提供能源…...

华为OD机试题,用 Java 解【VLAN 资源池】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…...

面试加分项:JVM 锁优化和逃逸分析详解

1 锁优化JVM 在加锁的过程中&#xff0c;会采用自旋、自适应、锁消除、锁粗化等优化手段来提升代码执行效率。1.1 自旋锁和自适应自旋现在大多的处理器都是多核处理器 &#xff0c;如果在多核心处理器&#xff0c;有让两个或者以上的线程并行执行&#xff0c;我们可以让一个等待…...

C++继承、构造函数和析构函数

构造函数 与 析构函数 构造函数代表一个对象的生成&#xff0c;主要作用是初始化类的成员变量&#xff0c;可以被重载 如果没有显式构造函数&#xff0c;则实例化对象时&#xff0c;系统会自动生成一个无参的构造函数 构造函数的名称与类名相同 析构函数代表的是一个对象的销…...

Python如何实现异步并发之async(1)

前言 本文是该专栏的第14篇,后面会持续分享python的各种干货知识,值得关注。 在python中使用async方式,实现异步并发,而本文笔者提到的代码案例仅支持python3.7及以上版本,这主要在于不同的版本之间都更新了异步的使用方法,这点暂时不详述了。 而所谓的异步,通常就是程…...

震撼!阿里首次开源 Java 10万字题库,Github仅一天星标就超60K

程序员面试 现在是程序员找工作、跳槽最重要的月份。随着行业的发展程序员面试也越来越难&#xff0c;面试中都是7分的能力&#xff0c;再加上3分的技巧&#xff1b; 对于应聘者&#xff0c;重中之重的就是简历&#xff0c;面试前一定要将最拿手和最能吸引面试官的技能在简历…...

十三、RESTful API

RESTful API 什么是RESTful REST一词&#xff0c;是Roy Thomas Fielding在他2000年的博士论文中提出的。 Fielding是一个非常重要的人&#xff0c;他是HTTP协议&#xff08;1.0版和1.1版&#xff09;的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所…...

路由器防火墙配置(14)

实验目的 通过本实验&#xff0c;理解路由器的防火墙工作原理&#xff0c;掌握路由器的防火墙功能配置方法&#xff0c;主要包括网络地址转换功能和数据包过滤功能的配置。 培养根据具体环境与实际需求进行网络地址转换及数据包过滤的能力。 预备知识网络地址转换 网络地址转…...

灰狼算法优化VMD对时序信号分析python

VMD算法变分模态分解(VMD)算法是一种根据变分方程计算,将信号分析过程转换成求解变分方程的过程,具体分析过程可见前面写的另外一篇博客VMD,这里不多介绍。 VMD算法在进行信号分析时,将一段时序信号分解成不同频段的几个子信号,但其分解效果的好坏由其两个参数影响较大—…...

微服务架构中的多级缓存设计还有人不懂?

今天我们来聊聊缓存这个话题&#xff0c;看看在微服务环境下如何设计有效的多级缓存架构。主要涉及三方面内容&#xff1a; Web 应用的客户端缓存&#xff1b;应用层静态资源缓存&#xff1b;服务层多级缓存。 首先&#xff0c;咱们先讲解微服务架构的多级缓存设计。 微服务…...

【图神经网络 医学/药物/目标/分子/(结构/相互作用)预测】用于药物-目标相互作用预测的元集合(Metapath)异构图神经网络(MHGNN)

May the immensity of the universe, guide us to meet again. 我个人觉得这篇Paper很好。哈哈! 本次学习的Paper于2023年1月15日索引于 Web of Science,是比较新的。 作者:中国天津,南开大学计算机科学学院。 本篇Paper的研究方向(类别/分类):生物化学 & 分子生物学…...

《Java核心技术》笔记——第六章

文章目录CH6.接口、lambda表达式与内部类1.接口基本2.常用接口3.lambda表达式4.内部类5.服务加载器与代理前章&#xff1a; 第三章~第五章的学习笔记CH6.接口、lambda表达式与内部类 1.接口基本 接口基本——interface声明&#xff0c;方法无需指明public&#xff08;默认都是…...

假设检验的基本思想

假设检验 首先了解参数估计&#xff0c;比如有服从正态分布的数据集X∼N(μ,σ2)X\sim N(\mu,\sigma^{2})X∼N(μ,σ2)&#xff0c;我们希望根据样本x1,...xnx_{1},...x_{n}x1​,...xn​估计出参数μ,σ\mu,\sigmaμ,σ&#xff0c;这些参数可以是一个具体值&#xff0c;也可以…...

c语言机试练习

1.打印日期 给出年分m和一年中的第n天&#xff0c;算出第n天是几月几号。 输入描述&#xff1a; 输入包括两个整数y(1<y<3000)&#xff0c;n(1<n<366)。 输出描述&#xff1a; 可能有多组测试数据&#xff0c;对于每组数据&#xff0c; 按 yyyy-mm-dd的格式将输入中…...

Python的PyQt框架的使用-资源文件夹的使用

Python的PyQt框架的使用-资源文件夹的使用一、前言二、Qt Designer加载资源文件三、资源文件的转换一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡&#xff0c;小伙伴们&#xff0c;让我们一起来学习Python的PyQt框架的使用。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三…...

如何遍历HashMap

文章目录1.Iterator EntrySet2.Iterator keySet3.forEach EntrySet4.forEach keySet5.lambda6.Streams API单线程7.Streams API 多线程1.Iterator EntrySet Iterator<Map.Entry<Integer,String>> iteratormap.entrySet().iterator; while(iterator.hasNext()){Map…...

11技术太卷我学APEX-数据加载

11技术太卷我学APEX-数据加载 0 所谓的数据加载 就是导入数据到数据库表中&#xff0c;本示例就采用Excel导入数据到《技术太卷我学APEX》的apex_learn表。表结构大概是这样的 CREATE TABLE "APEX_LEARN" ( "P_ID" NUMBER(17,0) NOT NULL ENABLE, &quo…...

JVM记录

一、JVM体系结构&#xff1a; 类装载器ClassLoader&#xff1a;用来装载.class文件执行引擎&#xff1a;执行字节码&#xff0c;或者执行本地方法运行时数据区&#xff1a;方法区、堆、Java栈、程序计数器、本地方法栈1、方法区&#xff1a; 也称“永久代”&#xff0c;“非堆”…...