首页 > 其他分享 >FreeRTOS入门教程(任务通知)

FreeRTOS入门教程(任务通知)

时间:2023-11-26 10:01:04浏览次数:31  
标签:FreeRTOS ++ 通知 入门教程 信号量 任务 ulTaskNotifyTake 等待

(文章目录)


前言

本篇文章将带大家学习任务通知的概念和使用方法。

一、什么是任务通知

FreeRTOS中的任务通知(Task Notification)是一种轻量级的同步机制,允许一个任务通知另一个任务已发生的事件或条件。这对于多任务系统中的协作和同步非常有用。以下是有关FreeRTOS任务通知的详细讲解:

任务通知的作用: 任务通知的主要作用是允许一个任务通知其他任务已发生的事件,而无需使用更重的互斥锁或信号量。这可以用于线程间的通信和同步,以及处理任务之间的依赖关系。

通知值(Notification Value): 任务通知包括一个32位的通知值,用于传递信息。通知值可以是整数或位掩码,具体的含义由应用程序自行定义。任务可以等待特定的通知值或位掩码,以便在通知发生时采取相应的行动。

二、任务通知和队列,信号量的区别

任务通知、队列和信号量是FreeRTOS中用于任务间通信和同步的不同机制,它们有不同的特点和适用场景:

1.任务通知(Task Notification):

用途:任务通知主要用于任务之间的事件通知和同步,一个任务向其他任务发送通知,以表明某些事件已发生。 特点:轻量级、高效,通常用于一对一或一对多的任务通信。 通信方式:通知是无数据的,只包含一个32位的通知值,任务可以等待特定的通知值。 适用场景:适用于任务之间的事件通知、依赖关系、同步等情况,以及需要高效且快速的通信。

使用任务通知时发送方可以直接将发送信息给接收方,不需要通过中间的结构体对象(信号量,队列结构体)。 在这里插入图片描述

2.队列(Queue):

用途:队列用于任务之间的数据传递,允许一个任务发送数据给另一个任务。 特点:队列是有缓冲区的,可以传输多个数据元素,支持FIFO(先进先出)顺序。 通信方式:队列是带数据的通信机制,任务可以发送和接收数据。 适用场景:适用于需要任务之间传递数据的情况,如生产者-消费者问题、数据采集等。

3.信号量(Semaphore):

用途:信号量用于控制对共享资源的访问,允许任务对资源的使用进行同步和互斥。 特点:信号量通常用于资源保护和互斥访问,可以是二进制信号量(互斥锁)或计数信号量(资源计数)。 通信方式:信号量通常用于任务之间互斥,以确保只有一个任务可以访问共享资源。 适用场景:适用于共享资源的访问控制、互斥操作等情况,如保护共享内存、硬件设备等。

使用队列,信号量时都需要创建出通信对象结构体,通过这个结构体进行通信。 在这里插入图片描述

总的来说,任务通知适用于事件通知和轻量级的同步,队列适用于任务之间的数据传递,而信号量适用于资源访问的同步和互斥。在选择合适的通信和同步机制时,应根据具体需求和任务之间的关系来决定使用哪种机制。有时,这些机制也可以结合使用,以满足更复杂的任务间通信和同步需求。

三、任务通知的优点和缺点

1.优点

1.轻量级和高效: 任务通知是一种轻量级的通信机制,它不需要大量的内存和处理时间来维护,因此非常高效。

2.适用于一对多通信: 任务通知适用于一对多的任务通信,一个任务可以通知多个等待通知的任务,这在某些场景下非常有用。

3.实时性强: 任务通知可以提供较低的延迟,因为一旦通知被发送,接收通知的任务可以立即响应。

4.支持不同类型的通知: 任务通知可以发送不同类型的通知,任务可以等待特定的通知类型。

5.无需额外的资源: 与消息队列等机制不同,任务通知不需要为数据缓冲区分配额外的内存,因此它更节省资源。

2.缺点

1.无数据传递: 任务通知本身不支持数据传递,只能传递一个32位的通知值。如果需要传递数据,你可能需要结合其他机制来实现。

2.适用性有限: 任务通知更适用于简单的事件通知和同步需求,对于复杂的数据交换和同步需求,可能需要使用其他机制,如消息队列或信号量。

3.不适用于多生产者-多消费者问题: 任务通知通常不适合解决多生产者和多消费者问题,因为它不提供数据缓冲区来处理多个生产者和消费者之间的数据共享。

4.不适合长期阻塞: 任务通知通常用于短期同步,如果任务需要长期等待,其他机制如消息队列可能更合适。

总的来说,任务通知是一种非常高效的任务间通信机制,适用于简单的事件通知和同步需求,但对于复杂的数据传递和同步问题,可能需要结合其他FreeRTOS机制来实现。选择合适的通信机制应根据具体的应用需求来决定。

四、任务状态和通知值

每个任务都有一个结构体: TCB(Task Control Block),里面有2个成员。

一个是uint8 t类型,用来表示通知状态。

volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];

ucNotifyState[] 数组:

类型:volatile uint8_t 作用:通常用于存储任务通知的状态。 FreeRTOS允许任务等待多个通知。这个数组可能用于记录每个任务是否已经接收到通知,或者通知的处理状态。每个元素可能对应一个任务的通知状态。

任务通知的三种状态:

#define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 )

含义:任务处于未等待通知的状态。 初始状态或者任务已经完成了对通知的等待,准备进入下一个等待通知的周期。

#define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )

含义:任务正在等待通知的状态。 当任务调用 ulTaskNotifyTake 等待通知时,它的状态将变为等待通知状态。任务会一直保持在这个状态,直到它收到通知或者等待超时。

#define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )

含义:任务已经收到通知的状态。 当任务成功接收到通知时,其状态将从等待通知状态切换到通知已接收状态。任务可以通过调用 ulTaskNotifyTake 函数获取通知的值,并执行相应的操作。 一个是uint32 t类型,用来表示通知值。

volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];

ulNotifiedValue[] 数组:

类型:volatile uint32_t 作用:通常用于存储任务接收到的通知值。 每个任务都可以使用 ulTaskNotifyTake 函数等待通知,并在接收到通知时获得相应的值。这个数组可能被设计为记录多个任务接收到的通知值。数组的每个元素对应一个任务。

五、任务通知相关的函数

发出通知

发出通知有两个函数可以使用,分别是xTaskNotifyGive和xTaskNotify。

xTaskNotifyGive(TaskHandle_t xTaskToNotify);

功能:向指定的任务发送一个通知。 参数:xTaskToNotify 是要通知的任务的句柄(handle)。 返回值:无。 详细说明:这个函数用于向另一个任务发送通知。通知的具体内容可以是一个比特位或者一个32位的值,取决于任务通知的类型。被通知的任务可以通过 ulTaskNotifyTake 函数获取通知的值。

xTaskNotify(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction)

功能:向指定的任务发送一个通知,可以指定通知的值和通知的行为。 参数: xTaskToNotify:要通知的任务的句柄(handle)。 ulValue:通知的值,可以是一个比特位或者32位的值。 eAction:通知的行为,例如覆盖之前的通知值或者增加到之前的通知值。 返回值:无。 详细说明:这个函数允许发送带有值的通知,并且可以选择通知的行为。同样,被通知的任务可以通过 ulTaskNotifyTake 函数获取通知的值。

xTaskNotifyGive和xTaskNotify区别:

xTaskNotifyGive: 用途:向指定的任务发送一个通知,但不提供通知的具体值。 示例用法:xTaskNotifyGive(xTaskHandle); 适用情况:当通知的具体值不关键,只是为了触发目标任务执行某个操作时,使用此函数。

xTaskNotify: 用途:向指定的任务发送一个通知,可以指定通知的具体值和通知的行为。 示例用法:xTaskNotify(xTaskHandle, ulValue, eAction); 适用情况:当通知的具体值对于目标任务的操作非常重要时,或者需要更精细的控制通知的行为时,使用此函数。

取出通知

取出通知有两个函数可以使用,分别是ulTaskNotifyTake和xTaskNotifyWait。

ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait);

功能:等待接收任务通知。 参数: xClearCountOnExit:标志是否在任务等待通知时清零通知计数。 xTicksToWait:等待通知的超时时间。 返回值:接收到的通知的值。 详细说明:任务调用这个函数等待接收通知。如果在超时时间内收到通知,任务将返回通知的值;否则,返回0。通知的具体值和行为由前面的 xTaskNotify 函数设置。

xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait);

功能:等待接收任务通知,并且可以设置在进入和退出时要清零的通知比特位。 参数: ulBitsToClearOnEntry:进入等待通知时要清零的通知比特位。 ulBitsToClearOnExit:退出等待通知时要清零的通知比特位。 pulNotificationValue:指向接收到的通知值的指针。 xTicksToWait:等待通知的超时时间。 返回值:如果在超时时间内收到通知,返回 pdTRUE;否则,返回 pdFALSE。 详细说明:这个函数允许更加细粒度的控制,可以在进入和退出等待通知的时候清零通知的比特位。同样,被通知的任务可以通过 ulTaskNotifyTake 函数获取通知的值。

ulTaskNotifyTake和xTaskNotifyWait区别:

ulTaskNotifyTake: 用途:等待接收任务通知,返回接收到的通知的值。 示例用法:ulNotifiedValue = ulTaskNotifyTake(pdFALSE, xTicksToWait); 适用情况:当只需等待通知并获取其值时,使用此函数。通常用于轻量级的通知接收。

xTaskNotifyWait: 用途:等待接收任务通知,可以设置在进入和退出时要清零的通知比特位,返回是否在超时时间内收到通知。 示例用法:xResult = xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, &ulNotificationValue, xTicksToWait); 适用情况:当需要更灵活的通知等待,并且需要在等待前后清零通知比特位时,使用此函数。通常用于更复杂的通知场景。

六、任务通知具体使用

1.实现轻量级信号量

xTaskNotifyGive函数可以让通知值加1,ulTaskNotifyTake可以让通知值减1,而且可以设置ulTaskNotifyTake的第一个参数来决定,是否清除通知值,设置为pdTURE则清除通知值(实现二进制信号量),设置为pdFALSE则不清除通知值(实现计数型信号量)。

二进制信号量

void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000; i++)
			sum++;
		//printf("1");
		for (i = 0; i < 10; i++)
		{
			// xSemaphoreGive(xSemCalc);
			xTaskNotifyGive(xHandleTask2);
		}
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	int i = 0;
	int val;
	while (1)
	{
		//if (flagCalcEnd)
		flagCalcEnd = 0;
		//xSemaphoreTake(xSemCalc, portMAX_DELAY);
		val = ulTaskNotifyTake(pdTURE, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d, NotifyVal = %d, i = %d\r\n", sum, val, i++);
	}
}

计数型信号量

void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000; i++)
			sum++;
		for (i = 0; i < 10; i++)
		{
			xTaskNotifyGive(xHandleTask2);
		}
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	int i = 0;
	int val;
	while (1)
	{
		val = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
		printf("sum = %d, NotifyVal = %d, i = %d\r\n", sum, val, i++);
	}
}

2.实现轻量级队列

xTaskNotify和xTaskNotifyWait可以实现轻量级队列,用于传输一个uint32_t类型的数值。

void Task1Function(void * param)
{
	volatile int i = 0;
	while (1)
	{
		for (i = 0; i < 10000; i++)
			sum++;
		for (i = 0; i < 10; i++)
		{
			xTaskNotify(xHandleTask2, sum, eSetValueWithOverwrite);
			sum++;
		}
		vTaskDelete(NULL);
	}
}

void Task2Function(void * param)
{
	int val;
	int i = 0;
	
	while (1)
	{
		xTaskNotifyWait(0, 0, &val, portMAX_DELAY);
		printf("sum = %d, i = %d\r\n", val, i++);
	}
}

总结

本篇文章就讲解到这里,下篇文章继续讲解FreeRTOS中的内容。

标签:FreeRTOS,++,通知,入门教程,信号量,任务,ulTaskNotifyTake,等待
From: https://blog.51cto.com/u_16153875/8567242

相关文章

  • 了解 ESP32 FreeRTOS:初学者指南
    原文:https://www.cnblogs.com/intomcu/p/17297020.html了解ESP32FreeRTOS:初学者指南ESP32FreeRTOS是什么?如何使用FreeRTOS?哪些常用的函数?xTaskCreate()vTaskDelete()vTaskDelay()xTicksToDelay()xSemaphoreCreateBinary()xSemaphoreGive()xSemaphore:要释放的信......
  • FreeRTOS深入教程(信号量源码分析)
    (文章目录)前言本篇文章将为大家讲解信号量,源码分析。在FreeRTOS中,信号量的实现基于队列。这种设计的思想是利用队列的特性来实现信号量,因为信号量可以被视为只能存储0或1个元素的特殊队列。在FreeRTOS中,二进制信号量(BinarySemaphore)通常由一个队列和一个计数器组成......
  • cocos creator新手入门教程:如何绑定参数到编辑器
    很多cocoscreator同学不知道如何绑定组件属性到编辑器上,今天我们来教大家如何绑定1:基本数据属性绑定到编辑器这个非常简单,模板是属性名字:默认的值;Is_debug:false,speed:100,2:系统组件类型与节点绑定到编辑器属性名字:{type:组件类型(cc.Sprite,cc.Label,cc.......
  • Markdown入门教程
    在这个网络时代,每个人多少都得掌握一些互联网编辑语言,今天我就从0开始,带着大家入门一个比较简单的编辑语言——MarkdownMarkdown是一种轻量级标记语言,排版语法简洁,让人们更多地关注内容本身而非排版。它使用易读易写的纯文本格式编写文档,可与HTML混编,可导出HTML、PDF以及本身......
  • Walrus 入门教程:如何创建模板以沉淀可复用的团队最佳实践
    模板是Walrus的核心功能之一,模板创建完成后用户可以重复使用,并在使用过程中逐渐沉淀研发和运维团队的最佳实践,进一步简化服务及资源的部署。用户可以使用HCL语言自定义创建模板,也可以一键复用Terraform社区中上万个成熟的Module。在本文中,我们将以阿里云EC2为例,介绍如何......
  • 回调函数用于通知机制
    相机SDK中一般有这样的回调:当帧采集完毕,自动调用回调函数。回调函数用于通知机制:当某一事件发生时,如果使用者注册过了回调函数,则会自动执行回调函数中的内容。网上很多回调函数的内容都是简单的使用下,没有太多关于通知机制的内容,于是找了一个案例//sdk.htypedefvoid(*REC_CA......
  • AWS Fargate 更新通知:任务修补与退役
    随着AWSFargate的不断演进,为确保安全性和性能,AWS宣布将自动对部分ECS服务中的任务进行更新。这次更新涵盖了安全补丁和其他关键的软件更新,为您的应用程序提供更稳定和安全的运行环境。更新详情更新计划于2023年12月4日星期一14:00:00GMT开始执行。下面是有关此更新的一些......
  • 【Python入门教程】Python中函数的用法和意义
    ​        在Python中,函数是一种可重用的代码块,它可以被多次调用以执行特定的任务。函数可以帮助我们组织代码,使其更易于阅读和调试,同时还可以提高代码的可重用性和可维护性。一、函数的定义        在Python中,函数使用def关键字进行定义,语法如下:deffunctio......
  • 【Python自动化】定时自动采集,并发送微信告警通知,全流程案例讲解!
    目录一、概要二、效果演示三、代码讲解3.1爬虫采集行政处罚数据3.2存MySQL数据库3.3发送告警邮件&微信通知3.4定时机制四、总结一、概要您好!我是@马哥python说,一名10年程序猿。我原创开发了一套定时自动化爬取方案,完整开发流程如下:采集数据->筛选数据->存MySQL数据库......
  • Kafka入门教程与详解(一)
    Kafka入门教程与详解(一)一、Kafka入门教程1.1消息队列(MessageQueue)MessageQueue消息传送系统提供传送服务。消息传送依赖于大量支持组件,这些组件负责处理连接服务、消息的路由和传送、持久性、安全性以及日志记录。消息服务器可以使用一个或多个代理实例。JMS(JavaMessaging......