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)
实验目的 通过本实验,理解路由器的防火墙工作原理,掌握路由器的防火墙功能配置方法,主要包括网络地址转换功能和数据包过滤功能的配置。 培养根据具体环境与实际需求进行网络地址转换及数据包过滤的能力。 预备知识网络地址转换 网络地址转…...
JAVA无人共享自习室预约小程序源码代码
以下是为您整理的基于UniApp的Java无人共享自习室预约小程序源码实现方案,包含核心模块代码与实现逻辑:后端Java核心模块(SpringBoot)数据库设计(MySQL)Entity Table(name "study_room") public…...
Asian Beauty Z-Image Turbo 风格迁移作品展:将经典名画风格融入现代人像
Asian Beauty Z-Image Turbo 风格迁移作品展:将经典名画风格融入现代人像 最近在玩一个挺有意思的AI图像模型,叫Asian Beauty Z-Image Turbo。听名字就知道,它特别擅长生成亚洲风格的人像。但我发现,它最厉害的地方还不止于此——…...
高性能队列Disruptor:从原理到实战的完整指南
高性能队列Disruptor:从原理到实战的完整指南 【免费下载链接】blog_demos CSDN博客专家程序员欣宸的github,这里有六百多篇原创文章的详细分类和汇总,以及对应的源码,内容涉及Java、Docker、Kubernetes、DevOPS等方面 项目地址…...
FireRedASR Pro代码详解:从音频预处理到文本后处理全流程
FireRedASR Pro代码详解:从音频预处理到文本后处理全流程 1. 引言 如果你对语音识别感兴趣,想知道一段音频是怎么变成文字的,那么这篇文章就是为你准备的。我们这次不聊怎么用现成的工具,而是直接打开一个叫FireRedASR Pro的语音…...
【OpenClaw】测试工程师如何使用 OpenClaw 参与测试流程
测试工程师如何使用 OpenClaw 参与测试流程1.OpenClaw 能帮测试工程师做什么?2.核心能力:Skill 让 AI 具备测试执行能力2.1 API 测试 Skill2.2 浏览器自动化 Skill2.3 数据库操作 Skill3.实战工作流:让 OpenClaw 跑通你的测试流程3.1 工作流 …...
终极指南:探索科幻作品中超越光速的星际旅行可能性
终极指南:探索科幻作品中超越光速的星际旅行可能性 【免费下载链接】awesome-scifi Sci-Fi worth consuming 项目地址: https://gitcode.com/gh_mirrors/aw/awesome-scifi 科幻爱好者们,你们是否曾梦想过穿越星际,在浩瀚宇宙中自由翱翔…...
【学习笔记】C++(2)
C++学习笔记 三、进阶 —— 类和对象 1、概述 2、基础 —— 公有、私有、保护、构造、析构 3、拷贝构造、临时对象不能绑定到非const引用问题 4、浅拷贝、深拷贝、移动拷贝 5、静态 6、内联和外联 7、链表 8、函数模板和类模板 9、友元 10、继承-派生(1) —— 基础 11、继承-…...
智慧教育——解读AI一体化智慧校园解决方案【附全文阅读】
适应人群为学校管理人员、教师、学生、技术运维人员及教育信息化建设相关从业者。主要内容围绕 AI 一体化智慧校园建设,阐述总体规划及革命性意义(提升教学管理水平、降低成本等);介绍八大应用中心(教学管理、物联网管控、校园安全等),涵盖智能选课排课、校园安防监控等…...
STM32 定时器与 PWM 输出:电机调速、LED 呼吸灯实战
在嵌入式开发的世界里,有一个问题困扰着无数初学者:微控制器只能输出0V和3.3V(或5V)的数字信号,但现实世界中的设备——从电机的转速控制到LED的亮度调节——需要的却是连续的模拟信号。如何用数字引脚“模拟”出模拟电…...
OpenClaw个人财务助手:Qwen3-14B分析消费记录生成报表
OpenClaw个人财务助手:Qwen3-14B分析消费记录生成报表 1. 为什么需要AI财务助手 上个月整理支付宝账单时,我盯着密密麻麻的消费记录发了半小时呆。餐饮、购物、交通的金额混在一起,根本分不清钱到底花在哪里。手动分类300多条记录后&#x…...
