FreeRTOS-任务管理

news2024/11/24 0:04:06

目录

任务管理

创建任务

创建任务示例1:创建两个同等级的任务

创建任务示例2:使用任务参数

删除任务

删除任务示例:删除任务

挂起任务

任务优先级

优先级实验:修改优先级

Tick

延时函数

延时示例

空闲任务

钩子函数

调度算法


任务管理

创建任务

BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,					// 函数指针,任务函数。退出函数时需调用vTaskDelete(NULL)
						const char * const pcName,					// 任务名称,FreeRTOS内部不使用它,仅起调试作用。长度为configMAX_TASK_NAME_LEN
						const configSTACK_DEPTH_TYPE usStackDepth,	// 栈大小。单位为word(4字节),最大值为uint16_t的最大值。精确确定栈大小的方法是看反汇编码
						void * const pvParameters,					// 调用任务函数时传入的参数,调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters)
						UBaseType_t uxPriority,						// 优先级,范围0~(configMAX_PRIORITIES-1),值超时会调整为最大值。值越小优先级越低。
						TaskHandle_t * const pxCreatedTask );		// 任务句柄。用来保持xTaskCreate输出结果task handle。
// 句柄:如果想操作这个任务(如修改优先级)时就需要这个,如果不用可以设为NULL。
// 返回值
// 		成功:pdPASS
//		失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY,即-1(失败原因只有内存不足)

创建任务示例1:创建两个同等级的任务
int main( void )
{
	prvSetupHardware();
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, NULL);
	
	/* 启动调度器 */
	vTaskStartScheduler();
	
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

void vTask1( void *pvParameters )
{
	const char *pcTaskName = "T1 run\r\n";
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 打印任务1的信息 */
		printf( pcTaskName );
		
		/* 延迟一会(比较简单粗暴) */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{}
	}
}

void vTask2( void *pvParameters )
{
	const char *pcTaskName = "T2 run\r\n";
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 打印任务1的信息 */
		printf( pcTaskName );
		
		/* 延迟一会(比较简单粗暴) */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{}
	}
}

实验现象:task2先运行。

任务开始执行顺序:先看优先级,高等优先级的先执行。同优先级的看任务的创建顺序,后创建的任务先执行。

创建任务示例2:使用任务参数

多个任务可以使用同一个函数,区别是:栈不同,且创建任务时可以传入不同的参数。

static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";

int main( void )
{
	prvSetupHardware();
	xTaskCreate(vTask, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
	xTaskCreate(vTask, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
	
	/* 启动调度器 */
	vTaskStartScheduler();
	
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

void vTask( void *pvParameters )
{
	const char *pcTaskName = pvParameters;
	volatile uint32_t ul; /* volatile用来避免被优化掉 */
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 打印任务1的信息 */
		printf( pcTaskName );
		
		/* 延迟一会(比较简单粗暴) */
		for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
		{}
	}
}

删除任务

void vTaskDelete( TaskHandle_t xTaskToDelete );
// 参数为任务句柄,也可传入NULL。

常用:

        自杀:vTaskDelete( NULL )。

        被杀:其他任务执行vTaskDelete( pvTaskCode ),pvTaskCode是当前任务的句柄。

        杀人:执行vTaskDelete( pvTaskCode ),pvTaskCode是其他任务的句柄。

删除任务示例:删除任务
int main( void )
{
	prvSetupHardware();
	xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
	
	/* 启动调度器 */
	vTaskStartScheduler();
	
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

void vTask1( void *pvParameters )
{
	const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );
	BaseType_t ret;
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 打印任务的信息 */
		printf("Task1 is running\r\n");
		
		ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );
		if (ret != pdPASS)
			printf("Create Task2 Failed\r\n");
		
		// 如果不休眠的话, Idle任务无法得到执行
		// Idel任务会清理任务2使用的内存
		// 如果不休眠则Idle任务无法执行, 最后内存耗尽
		vTaskDelay( xDelay100ms );
	}
}

void vTask2( void *pvParameters )
{
	/* 打印任务的信息 */
	printf("Task2 is running and about to delete itself\r\n");
	
	// 可以直接传入参数NULL, 这里只是为了演示函数用法
	vTaskDelete(xTask2Handle);
}

任务现象:

        Task1 is running

        Task2 is running and about to delete itself

        ...重复上面两行

// 当无法执行延时函数时

        Task1 is running

        Create Task2 Failed

        ...重复上面两行

创建任务1后开始调度,则执行任务1。

在任务1中创建任务2,任务2的优先级高,马上执行,打印并自杀。

任务1继续执行,执行延时函数进入阻塞状态,轮到空闲任务执行,空闲任务释放任务2的内存(TCB、栈)。

延时时间到,任务1继续执行。如此循环。

任务1中如果不调用vTaskDelay,则空闲任务没有机会执行,也就无法释放创建任务2时分配的内存。而任务1不断地创建任务,不断消耗内存,最终会内存耗尽而无法再创建新任务。

挂起任务

void vTaskSuspend( TaskHandle_t xTaskToSuspend );

参数xTaskToSuspend表示要暂停的任务,如果为NULL则表示暂停当前任务。

要退出暂停状态,只能由其他任务或中断程序来操作:

        别的任务调用:vTaskResume

        中断程序调用:xTaskResumeFromISR

实际开发中,暂停状态用得不多。

任务优先级

优先级的取值范围:0~(configMAX_PRIORITIES-1),数值越大优先级越高,值超最大值时默认为最大值。

高优先级的先执行;同优先级则后创建的先执行,然后轮流执行。

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

通用方法:

        使用C函数实现,对所有架构都是同样的代码。对configMAX_PRIORITIES的取值没有限制。

        但configMAX_PRIORITIES的取值最好还是尽量小,因为取值越大越浪费内存,也浪费时间。

        configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为0或未定义时,使用该方法。

架构相关的优化方法:

        架构相关的汇编指令,可以从一个32位的数里快速地找出为1的最高位。使用这些指令,可以快速找出优先级最高、可以运行的任务。

        configMAX_PRIORITIES的取值不能超过32.

        configUSE_PORT_OPTIMISED_TASK_SELECTION被定义为1时,使用该方法。

优先级实验:修改优先级
/* 获取任务的优先级,参数使用时表示指定任务,参数为NULL时表示指定当前任务 */
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );

/* 设置任务的优先级 */
/* 第一个参数:使用时表示指定任务,NULL时表示指定当前任务 */
/* 第一个参数:新优先级,取值范围是0~configMAX_PRIORITIES-1 */
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
int main( void )
{
	prvSetupHardware();
	
	/* Task1的优先级更高, Task1先执行 */
	xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
	
	/* 启动调度器 */
	vTaskStartScheduler();
	
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

void vTask1( void *pvParameters )
{
	UBaseType_t uxPriority;
	
	/* Task1,Task2都不会进入阻塞或者暂停状态,根据优先级决定谁能运行 */
	
	/* 得到Task1自己的优先级 */
	uxPriority = uxTaskPriorityGet( NULL );
	
	for( ;; )
	{
		printf( "Task 1 is running\r\n" );
		printf( "About to raise the Task 2 priority\r\n" );
		
		/* 提升Task2的优先级高于Task1,Task2会即刻执行 */
		vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
		
		/* 如果Task1能运行到这里,表示它的优先级比Task2高,那就表示Task2肯定把自己的优先级降低了 */
	}
}

void vTask2( void *pvParameters )
{
	UBaseType_t uxPriority;
	
	/* Task1,Task2都不会进入阻塞或者暂停状态,根据优先级决定谁能运行 */
	
	/* 得到Task2自己的优先级 */
	uxPriority = uxTaskPriorityGet( NULL );
	
	for( ;; )
	{
		/* 能运行到这里表示Task2的优先级高于Task1,Task1提高了Task2的优先级 */
		printf( "Task 2 is running\r\n" );
		printf( "About to lower the Task 2 priority\r\n" );
		
		/* 降低Task2自己的优先级,让它小于Task1,Task1得以运行 */
		vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
	}
}

Tick

同优先级的任务轮流执行,执行时间是tick。tick也称为节拍、心跳、滴答,是使用定时器产生固定间隔的中断。

时间片的长度由configTICK_RATE_HZ决定,假设configTICK_RATE_HZ为100,则时间片就是10ms。

tick操作:

vTaskDelay(2); // 等待2个Tick,假设configTICK_RATE_HZ=100, Tick周期时10ms, 等待20ms

// 还可以使用pdMS_TO_TICKS宏把ms转换为tick
vTaskDelay(pdMS_TO_TICKS(100)); // 等待100ms

基于tick实现的延时并不精确,如vTaskDelay(2)的本意是延迟2个Tick周期,有可能经过1个tick多一点就返回了。

使用vTaskDelay函数时,建议以ms为单位,使用pdMS_TO_TICKS把时间转换为tick。这样代码就与configTICK_RATE_HZ无关了,即使配置项configTICK_RATE_HZ改变了,我们也不需要去修改代码。

延时函数

vTaskDelay:至少等待指定个数的Tick interrupt才能变为就绪状态。

vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态。

void vTaskDelay( const TickType_t xTicksToDelay ); 	/* xTicksToDelay: 等待多少给Tick */

/* pxPreviousWakeTime: 上一次被唤醒的时间
 * xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement)
 * 单位都是Tick Count
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );

延时示例
int main( void )
{
	prvSetupHardware();
	
	/* Task1的优先级更高, Task1先执行 */
	xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );
	
	/* 启动调度器 */
	vTaskStartScheduler();
	
	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

void vTask1( void *pvParameters )
{
	const TickType_t xDelay50ms = pdMS_TO_TICKS( 50UL );
	TickType_t xLastWakeTime;
	int i;
	
	/* 获得当前的Tick Count */
	xLastWakeTime = xTaskGetTickCount();
	
	for( ;; )
	{
		flag = 1;
		
		/* 故意加入多个循环,让程序运行时间长一点 */
		for (i = 0; i < 5; i++)
			printf( "Task 1 is running\r\n" );
	
##if 1
		vTaskDelay(xDelay50ms);
##else
		vTaskDelayUntil(&xLastWakeTime, xDelay50ms);
##endif
	}
}

void vTask2( void *pvParameters )
{
	for( ;; )
	{
		flag = 0;
		printf( "Task 2 is running\r\n" );
	}
}

空闲任务

在使用vTaskStartScheduler()函数来创建、启动调度器时,函数内部会创建空闲任务:

        空闲任务优先级为0:不能阻碍用户任务运行。

        空闲任务只能处于就绪态或运行态,永远不会阻塞。

注意:如果使用vTaskDelete()删除任务,则需确保空闲任务有机会执行,否则就无法释放被删除任务的内存。

我们可以添加一个空闲任务的钩子函数,空闲任务的循环每执行一次就会调用一次钩子函数。

钩子函数

作用是:

        执行一些低优先级的、后台的、需要连续执行的函数。

        测量系统的空闲时间:空闲时间能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任务占据的时间就可以算出处理器占用率。

        让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要处理,可进入省电模式降低功耗。

空闲任务的钩子函数的限制:

        不能导致空闲任务进入阻塞状态、暂停状态。

        如果使用vTaskDelete()删除任务,则钩子函数要非常高效地执行。如果空闲任务一直卡在钩子函数里,它就无法释放内存。

使用钩子函数的前提是:在FreeRTOS\Source\tasks.c文件中,更改宏,实现函数。

        configUSE_IDLE_HOOK宏定义为1。

        实现vApplicationIdleHook函数。

调度算法

调度算法:确定某个就绪态任务切换为运行态。

通过配置文件FreeRTOSConfig.h的两个配置项来配置调度算法:configUSE_PREEMPTION、configUSE_TIME_SLICING。第三个配置项为高级选项configUSE_TICKLESS_IDLE,用于关闭tick中断来实现省电,一般为0,即不使用。

配置项可抢占+时间片轮询+空闲任务让步可抢占+时间片轮询+空闲任务不让步可抢占+非时间片轮询+空闲任务让步可抢占+非时间片轮询+空闲任务不让步合作调度
configUSE_PREEMPTION11110
configUSE_TIME_SLICING1100x
configIDLE_SHOULD_YIELD1010x
说明常用很少用很少用很少用几乎不用

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

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

相关文章

创意二维码案例:意大利艺术家的最新二维码艺术展!

意大利艺术家——米开朗基罗皮斯特莱托&#xff08;Michelangelo Pistoletto&#xff09;的个人艺术展“二维码‘说’”&#xff08;QR CODE POSSESSION&#xff09;正在北京798艺术区的常青艺术画廊展出&#xff0c;这是一次别出心裁的创意艺术展&#xff01; 主要体现在3个方…

案例036:基于微信小程序的在线课堂系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

2019年3月28日 Go生态洞察:Go 2018调查结果分析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

数据结构与算法编程题29

先序遍历二叉树&#xff08;非递归&#xff09;栈 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;B…

BetaFlight模块设计之三十七:SBUS

BetaFlight模块设计之三十七&#xff1a;SBUS 1. 源由2. sbus启动&动态任务3. 主要函数3.1 sbus初始化3.2 sbusFrameStatus更新3.3 rxFrameTimeUs3.4 sbusDataReceive接收数据 4. 辅助函数4.1 sbusChannelsDecode 5. 参考资料 1. 源由 接着BetaFlight模块设计之三十六&…

西北大学计算机844考研-23年计网计算题详细解析

西北大学计算机844考研-23年计网计算题详细解析 1.计算无传输差错状态下停止—等待ARQ协议效率,电磁波传播速率为2*10^8m/s&#xff0c;链路长为2000m&#xff0c;帧长度为1000比特&#xff0c;计算传输速率10kbps及10Mbps时的协议效率&#xff08;即信道利用率&#xff09; …

医院绩效考核管理系统项目源码

医院绩效考核体系该如何完善 一、合理制定绩效指标 绩效指标是医院绩效考核体系的重要部分&#xff0c; 而医院各科室的工作方法和目的各不相同、职责也不同。因此&#xff0c;医院要重视绩效指标的制定&#xff0c;并且从实际出发&#xff0c;根据各科室情况合理制定个性化的…

Pytorch 基于 deeplabv3_resnet50 迁移训练自己的图像语义分割模型

一、图像语义分割 图像语义分割是计算机视觉领域的一项重要任务&#xff0c;旨在将图像中的每个像素分配到其所属的语义类别&#xff0c;从而实现对图像内容的细粒度理解。与目标检测不同&#xff0c;图像语义分割要求对图像中的每个像素进行分类&#xff0c;而不仅仅是确定物…

华夏ERP信息泄露漏漏洞复现 [附POC]

文章目录 华夏ERP信息泄露漏漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 华夏ERP信息泄露漏漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#x…

深思:C与C++相互调用问题

背景 上周&#xff0c;偶然看到同事愁眉苦脸的样子&#xff0c;便善意咨询了下发生了什么。简单沟通下&#xff0c;才知道他遇到了一个工程编译的问题&#xff0c;一直无法编译通过&#xff0c;困扰了他快一天时间。出于个人的求知欲和知识的渴望&#xff0c;我便主动与他一同分…

动态规划学习——等差子序列问题

目录 一&#xff0c;最长等差子序列 1.题目 2.题目接口 3.解题思路及其代码 二&#xff0c;等差序列的划分——子序列 1.题目 2.题目接口 3.解题思路及其代码 一&#xff0c;最长等差子序列 1.题目 给你一个整数数组 nums&#xff0c;返回 nums 中最长等差子序列的长度…

M3u8视频文件怎么转换成MP4?一分钟解决!

大部分网课平台或者视频平台&#xff0c;都是基于m3u8格式的&#xff0c;这是因为m3u8格式本身的特点&#xff0c;既支持直播又支持点播。但是往往在其他平台或者设备上不兼容&#xff0c;就需要转成MP4格式&#xff0c;那么就像大家介绍3种好用的方法~ 方法一&#xff1a;使用…

详解HTTP协议(介绍--版本--工作过程--Fiddler 抓包显示--请求响应讲解)

目录 一.HTTP协议的介绍 1.1HTTP是什么&#xff1f; 1.2HTTP版本的演变 二.HTTP的工作过程 三.使用Fiddler抓包工具 3.1简单讲解Fiddler 3.2Fiddler工作的原理 3.3抓包结果分析 四.HTTP请求 4.1认识URL 4.2关于URL encode 4.3认识方法 4.3.1认识get和post 4.3.…

Fiddler弱网测试究竟该怎么做?

前言 使用Fiddler对手机App应用进行抓包&#xff0c;可以对App接口进行测试&#xff0c;也可以了解App传输中流量使用及请求响应情况&#xff0c;从而测试数据传输过程中流量使用的是否合理。 抓包过程&#xff1a; 1、Fiddler设置 1&#xff09;启动Fiddler->Tools->…

易点天下携AIGC创新成果KreadoAI亮相数贸会,解锁电商文化出海新可能

11月27日&#xff0c;第二届全球数字贸易博览会&#xff08;以下简称“数贸会”&#xff09;在浙江杭州完美落幕。作为出海营销领域最早一批布局AIGC战略的营销科技公司&#xff0c;易点天下受邀与来自全球800余家境内外数字贸易企业同台参展&#xff0c;并分享了旗下AIGC数字营…

拦截器使用详解

什么是拦截器? 拦截器是 Spring 框架提供的核⼼功能之⼀,主要⽤来拦截⽤户的请求, 在指定⽅法前后,根据业务需要执行预先设定的代码. 也就是说,允许开发⼈员提前预定义⼀些逻辑,在请求访问接口前/后执行.也可以在⽤户请求前阻止其进入接口执行 在拦截器当中&#xff0c;开发⼈…

广西铁塔发布ZETag定位服务,快递物流可视化将成趋势

在万亿的快递、物流红海市场中&#xff0c;可视化或将成为凸显差异化优势的一枚棋子。 11月17日&#xff0c;中国铁塔股份有限公司广西壮族自治区分公司&#xff08;以下简称“广西铁塔”&#xff09;在广西南宁召开“万物智联&#xff0c;贴‘芯’服务”物联网产品发布会&…

UE5富文本框学习(用途:A(名字)用刀(图片)击杀B(名字))

UE5-UMG教程-通用控件&#xff1a;多格式文本块&#xff08;RichTextBlock&#xff09;_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Pu4y1k7Z2/?p54&spm_id_frompageDriver 结果示例&#xff1a; 1.添加富文本框 2.添加文字样式库 点添加&#xff0c;更改每行行…

又又又重新刷题的第一天第一天第一天,这次目标是top100一定要刷完整至少一次两次吧:1/150:两数之和 2/150两数相加 3/150无重复字符的最长字串

题目1/150&#xff1a;两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重…

智慧工厂人员定位系统源码,融合位置物联网、GIS可视化等技术,实现对人员、物资精确定位管理

智慧工厂人员定位系统源码&#xff0c;UWB高精度定位系统源码 随着中国经济发展进入新常态&#xff0c;在资源和环境约束不断强化的背景下&#xff0c;创新驱动传统制造向智能制造转型升级&#xff0c;越发成为企业生存发展的关键。智能工厂作为实现智能制造的重要载体&#xf…