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)
实验目的 通过本实验,理解路由器的防火墙工作原理,掌握路由器的防火墙功能配置方法,主要包括网络地址转换功能和数据包过滤功能的配置。 培养根据具体环境与实际需求进行网络地址转换及数据包过滤的能力。 预备知识网络地址转换 网络地址转…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
