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使用
代码: #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层 源码 目录:/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岁儿童和狗。 文章目录前言一、主要工具类总结前言 之前写过一些简单的图片压缩和图片加水印:JAVA实现图片质量压缩和加水印 本次主要是针对图片加水印进行一个升级,图片水印自定义,自适应大小。 来,先看几…...
文章读后感——《人间清醒,内容为王》
观后感 人间清醒,内容为王 - 技术er究竟该如何写博客?1024上海嘉年华之敖丙演讲观后感。 致敬愿意带领后生的前辈——哈哥 哈哥,《人间清醒,内容为王》这篇,我的鱼获, 是里面传递出来写技术博客的思维与想法…...

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

Java进击框架:Spring(一)
Java进击框架:Spring(一)前言创建Spring项目Spring IoC容器和Beans介绍Bean的概述Spring IoC配置元数据实例化Bean依赖注入循环依赖详细配置生命周期回调Bean定义继承基于注解的容器配置Component和进一步的原型注解自动检测类和注册Bean定义…...
Java笔记(18)
目录 什么是mvc? 什么是SpringMVC Spring MVC的特点: 原理: 什么是Thymeleaf? thymeleaf依赖包:...

【免费教程】地下水环境监测技术规范HJ/T164-2020解读使用教程
地下水环境监测技术规范依据《中华人民共和国环境保护法》第十一条“国务院环境保护行政主管部门建立监测制度、制订监测规范”和《中华人民共和国水污染防治法》的要求,积极开展地下水环境监测,掌握地下水环境质量,保护地下水水质࿰…...

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

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

业务单据堆积如山?如何提升会计做账效率?
某集团以“创建现代能源体系、提高人民生活品质”为使命,形成了贯通下游分销、中游贸易储运、上游生产的清洁能源产业链和涵盖健康、文化、旅游、置业的生命健康产品链。目前,某集团在全国21个省,为超过2681万个家庭用户、21万家企业提供能源…...

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

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

C++继承、构造函数和析构函数
构造函数 与 析构函数 构造函数代表一个对象的生成,主要作用是初始化类的成员变量,可以被重载 如果没有显式构造函数,则实例化对象时,系统会自动生成一个无参的构造函数 构造函数的名称与类名相同 析构函数代表的是一个对象的销…...
Python如何实现异步并发之async(1)
前言 本文是该专栏的第14篇,后面会持续分享python的各种干货知识,值得关注。 在python中使用async方式,实现异步并发,而本文笔者提到的代码案例仅支持python3.7及以上版本,这主要在于不同的版本之间都更新了异步的使用方法,这点暂时不详述了。 而所谓的异步,通常就是程…...

震撼!阿里首次开源 Java 10万字题库,Github仅一天星标就超60K
程序员面试 现在是程序员找工作、跳槽最重要的月份。随着行业的发展程序员面试也越来越难,面试中都是7分的能力,再加上3分的技巧; 对于应聘者,重中之重的就是简历,面试前一定要将最拿手和最能吸引面试官的技能在简历…...
十三、RESTful API
RESTful API 什么是RESTful REST一词,是Roy Thomas Fielding在他2000年的博士论文中提出的。 Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所…...

路由器防火墙配置(14)
实验目的 通过本实验,理解路由器的防火墙工作原理,掌握路由器的防火墙功能配置方法,主要包括网络地址转换功能和数据包过滤功能的配置。 培养根据具体环境与实际需求进行网络地址转换及数据包过滤的能力。 预备知识网络地址转换 网络地址转…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...

前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...