FreeRTOS—任务通知

一、任务通知

1.1.简介

任务通知是用来通知任务的,任务控制块(TCB)中的结构体成员变量ulNotifiedValue就是这个通知值。下面是使用任务通知和其他通信方式的区别:

  • 使用任务通知时,任务结构体就包含了内部对象,可以直接接收别人发过来的通知
    在这里插入图片描述
  • 使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行简介通信
    在这里插入图片描述

1.2.任务通知值的更新方式

  • 覆盖或不覆盖接受任务的通知值(队列)
  • 更新接收任务通知值的一个过多个 bit(事件标志组)
  • 增加接收任务的通知值(信号量)

只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量和事件标志组。

1.3.任务通知的优劣势

优势:

  • 效率高:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
  • 使用内存更小:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体

劣势:

  • 无法发送数据给 ISR(中断服务函数):ISR 没有任务结构体,因此无法给 ISR 发送数据,但 ISR可以使用任务通知的功能发送数据给任务
  • 无法广播给多个任务:任务通知只能是被指定的一个任务接收并处理
  • 无法缓存多个数据:任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据
  • 发送受阻不支持阻塞:发送方无法进入阻塞状态等待

二、任务通知值和通知状态

每个任务都有自己的任务控制块(TCB),它里面有两个结构体成员变量:

typedef struct tskTaskControlBlock
{
	...
	#if(configUSE_TASK_NOTIFICATIONS == 1)	//打开这个宏
	volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];	//通知值
	volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];		//通知状态
	endif
	...
}tskTCB;
#define  configTASK_NOTIFICATION_ARRAY_ENTRIES  1	//定义任务通知数组的大小,默认为1

2.1.任务通知值

任务通知值的更新方式有多种类型:

  • 计数值(数值累加,类似信号量)
  • 相应位置 1(类似事件标志组)
  • 任意数值(支持是否覆写,类似队列)

2.2.任务通知状态

其中任务通知共有三种取值:

#define taskNOT_WAITING_NOTIFICATION   ((uint8_t)0)		//任务未等待通知
#define taskWAITING_NOTIFICATION  	   ((uint8_t)1)		//任务在等待通知
#define taskNOTIFICATION_RECEIVED      ((uint8_t)2)		//任务在等待接收
  • 未等待通知:任务通知默认的初始化状态(事不关己,高高挂起)
  • 等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知
  • 等待接收:发送方已经发送出去(调用了发送任务通知函数),等待接收方接收

三、任务通知相关API函数

任务通知 API 函数主要有两类:发送通知和接收通知。发送通知 API 函数可以用于任务和中断服务函数中;接收通知 API 函数只能用在任务中。

3.1.发送通知相关 API 函数

函数描述
xTaskNotify( )发送任务通知,带有通知值
xTaskNotifyAndQuery( )发送通知值,带有通知值并且保留接受任务的原通知值
xTaskNotifyGive( )发送通知,不带通知值
#define xTaskNotify(xTaskToNotify, ulValue, eAction)            
		xTaskGenericNotify((xTaskToNotify), (tskDEFAULT_INDEX_TO_NOTIFY),       
						   (ulValue), (eAction), NULL) 
						   
#define xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue)      
    	xTaskGenericNotify((xTaskToNotify), (tskDEFAULT_INDEX_TO_NOTIFY),
    					   (ulValue), (eAction), (pulPreviousNotifyValue)) 
            			   
#define xTaskNotifyGive(xTaskToNotify)     
   	 	xTaskGenericNotify((xTaskToNotify), (tskDEFAULT_INDEX_TO_NOTIFY),   
           				   (0), eIncrement, NULL) 

从上面的代码中可以看出,三个用于在任务中发送任务通知的函数,实际上都是调用了函数xTaskGenericNotify( )来发送任务通知的,只是传入了不同的参数。函数xTaskGenericNotify( )的函数原型如下所示:

BaseType_t xTaskGenericNotify(TaskHandle_t   xTaskToNotify, 
            				  UBaseType_t    uxIndexToNotify, 
				              uint32_t  	 ulValue, 
				              eNotifyAction  eAction, 
				              uint32_t *     pulPreviousNotificationValue); 

下面表格是函数xTaskGenericNotify( )的形参描述:

形参描述
xTaskToNotify接收任务通知的任务句柄
uxIndexToNotify任务的指定通知(任务通知相关数组成员)
ulValue通知值
eAction通知方式(通知值更新方式)
pulPreviousNotificationValue用于获取发送通知前的通知值(NULL为不保存)

下面表格是函数xTaskGenericNotify( )的返回值:

返回值描述
pdPASS任务通知发送成功
pdFAIL任务通知发送失败

3.2.接收通知相关 API 函数

函数描述
ulTaskNotifyTake( )获取任务通知,可以设置在退出此函数的时候将任务通知清零或减 1,当任务通知用作二值信号量或者计数信号量的时候,使用此函数来获取信号量
xTaskNotifyWait( )获取任务通知,比ulTaskNotifyTak( )更为复杂,可获取通知值和清除通知值的指定位
  • 当任务通知用作与信号量时,使用函数获取信号量:ulTaskNotifyTake( )
  • 当任务通知用作与事件标志组或队列时,使用此函数来获取:xTaskNotifyWait( )

3.2.1.ulTaskNotifyTake( ) 函数

此函数用于获取任务通知的通知值,并且在成功获取任务通知的通知值后,可以指定将通知值清零或减 1。此函数实际上是一个宏定义,具体的代码如下所示:

#define ulTaskNotifyTake(xClearCountOnExit, xTicksToWait)         
		ulTaskGenericNotifyTake((tskDEFAULT_INDEX_TO_NOTIFY),     
								(xClearCountOnExit),       
								(xTicksToWait)) 

从上面的代码中可以看出,函数ulTaskNotifyTake( )实际上是调用了函数ulTaskGenericNotifyTake( ),函数ulTaskGenericNotifyTake( )的函数原型如下所示:

uint32_t ulTaskGenericNotifyTake(UBaseType_t  uxIndexToWaitOn, 
								 BaseType_t   xClearCountOnExit, 
							 	 TickType_t   xTicksToWait); 

下面表格是ulTaskGenericNotifyTake( )函数的形参描述:

形参描述
uxIndexToWaitOn任务的指定通知(任务通知相关数组成员)
xClearCountOnExit指定在成功接收通知后,将通知值清零或减 1 ,pdTRUE:把通知值清零;pdFALSE:把通知值减 1
xTicksToWait阻塞等待任务通知值的最大时间

下面表格是ulTaskGenericNotifyTake( )函数的返回值:

返回值描述
0接收失败
非0接收成功,返回任务通知的通知值

3.2.2.xTaskNotifyWait( )函数

此函数用于等待任务通知通知值中的指定比特位被置 1,此函数可以在等待前和成功等待到任务通知通知值中的指定比特位被置 1 后清零指定比特位,并且还能获取等待超时后任务通知的通知值。此函数实际上是一个宏定义,具体的代码如下所示:

#define xTaskNotifyWait(ulBitsToClearOnEntry,  
			            ulBitsToClearOnExit,    
			            pulNotificationValue,   
			            xTicksToWait)      
   	    xTaskGenericNotifyWait(tskDEFAULT_INDEX_TO_NOTIFY,   
					          (ulBitsToClearOnEntry),      
					          (ulBitsToClearOnExit),     
					          (pulNotificationValue),      
					          (xTicksToWait)) 

从上面的代码中可以看出,函数xTaskNotifyWait( )实际上是调用了函数xTaskGenericNotifyWait( ),函数xTaskGenericNotifyWait( )的函数原型如下所示:

BaseType_t xTaskGenericNotifyWait(UBaseType_t uxIndexToWaitOn, 
					              uint32_t 	  ulBitsToClearOnEntry, 
					              uint32_t 	  ulBitsToClearOnExit, 
					              uint32_t *  pulNotificationValue, 
					              TickType_t  xTicksToWait); 

下面表格是xTaskGenericNotifyWait( )函数的形参描述:

形参描述
uxIndexToWaitOn任务的指定通知(任务通知相关数组成员)
ulBitesToClearOnEntry等待前清零指定任务通知值的比特位(旧值对应 bit 清 0)
ulBitesToClearOnExit成功等待后清零指定的任务通知值比特位(新值对应 bit 清 0)
pulNotificationValue用来取出通知值(如果不需要取出,就设置为NULL)
xTicksToWait阻塞等待任务通知值的最大时间

下面表格是xTaskGenericNotifyWait( )函数的返回值:

返回值描述
pdTRUE等待任务通知成功
pdFALSE等待任务通知失败

四、实验

4.1.模拟信号量实验

4.1.1.实验设计

本实验将设计三个任务:

  • start_task:创建 task1 和 task2
  • task1:用于按键扫描,当检测到按键 KEY0 被按下时,将发送任务通知
  • task2:用于接收任务通知,打印相关信息

4.1.2.软件设计

任务通知不需要创建,是在任务本身的任务控制块里面的,因此直接编写 task1 和 task2 函数:

void task1(void *pvParameters)
{
	uint8_t key = 0;
	while(1)
	{
		key = key_scan(0);
		if(key == KEY0_PRES)
		{
			printf("任务通知模拟信号量释放信号量成功\r\n");
			xTaskNotifyGive(task2_handler);
		}
		vTaskDelay(10);
	}
}

void task2(void *pvParameters)
{
	uint32_t num = 0;
	while(1)
	{
		num = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
		printf("获取信号量成功,获取了%d个资源\r\n",num);
		vTaskDelay(2000);	//每两秒消耗一个资源
	}
}

下面图是运行结果,用任务通知模拟计数型信号量,模拟二值信号量只需要将num = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);函数的pdFALSE改成pdTRUE即可:

在这里插入图片描述

4.2.模拟消息邮箱实验(队列)

4.2.1.实验设计

本实验将设计三个任务:

  • start_task:创建 task1 和 task2
  • task1:用于按键扫描,将按下的按键值通过任务通知发送给指定任务
  • task2:用于接收任务通知,并根据接收到的数据做出相应动作

4.2.2.软件设计

下面是 task1 和 task2 的任务实现,task2 用switch语句实现按下哪一个按键就打印哪一个数值:

void task1(void *pvParameters)
{
	uint8_t key = 0;
	while(1)
	{
		key = key_scan(0);
		if(key != 0)
		{
			printf("任务通知模拟消息邮箱发送,发送的值为:%d\r\n",key);
			xTaskNotify(task2_handler, key, eSetValueWithoutOverwrite);
		}
		vTaskDelay(10);
	}
}

void task2(void *pvParameters)
{
	uint32_t num = 0;	//定义一个变量来存放数值
	while(1)
	{
		xTaskNotifyWait(0, 0xFFFFFFFF, &num, portMAX_DELAY);
		switch(num)
		{
			case KEY0_PRES:
			{
				printf("收到的值为:%d\r\n",num);
				break;
			}
			case KEY1_PRES:
			{
				printf("收到的值为:%d\r\n",num);
				break;
			}
			case WKUP_PRES:
			{
				printf("收到的值为:%d\r\n",num);
				break;
			}
			default:break;
		}
	}
}

下图是运行结果:

在这里插入图片描述

4.3.模拟事件标志组实验

4.3.1.实验设计

本实验将设计三个任务:

  • start_task:创建 task1 和 task2
  • task1:用于按键扫描,当检测到按键按下时,将发送任务通知设置不同标志位
  • task2:用于接收任务通知,打印相关信息

4.3.2.软件设计

本实验需要将 Bit0 和 Bit1 都置 1才能打印 task2 的句子,下面是 task1 和 task2 的任务实现:

void task1(void *pvParameters)
{
	uint8_t key = 0;
	while(1)
	{
		key = key_scan(0);
		if(key == KEY0_PRES)
		{
			printf("将Bit0置1\r\n");
			xTaskNotify(task2_handler, Bit0, eSetBits);
		}
		if(key == KEY1_PRES)
		{
			printf("将Bit1置1\r\n");
			xTaskNotify(task2_handler, Bit1, eSetBits);
		}
	}
}

//按下一次按键,就进来一次task2函数,但是xTaskNotifyWait函数里已经表明了退出该函数,数值就会清空
//因此需要两个变量来存储上一个值和下一个值
void task2(void *pvParameters)
{
	uint32_t notify_val = 0;
	uint32_t event_bit = 0;
	while(1)
	{
		xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);
		if(notify_val & Bit0)	//存储上一个值
		{
			event_bit |= Bit0;
		}
		if(notify_val & Bit1)	//存储下一个值
		{
			event_bit |= Bit1;
		}
		if(event_bit == (Bit0 | Bit1))	//如果上一个值和下一个值都为1,就执行下面代码
		{
			printf("Bit0和Bit1成功置1\r\n");
			event_bit = 0;
		}
	}
}

下图是运行结果图:

在这里插入图片描述

内容概要:本文详细探讨了杯形谐波减速器的齿廓修形方法及寿命预测分析。文章首先介绍了针对柔轮与波发生器装配时出现的啮合干涉问题,提出了一种柔轮齿廓修形方法。通过有限元法装配仿真确定修形量,并对修形后的柔轮进行装配和运转有限元分析。基于Miner线性疲劳理论,使用Fe-safe软件预测柔轮寿命。结果显示,修形后柔轮装配最大应力从962.2 MPa降至532.7 MPa,负载运转应力为609.9 MPa,解决了啮合干涉问题,柔轮寿命循环次数达到4.28×10⁶次。此外,文中还提供了详细的Python代码实现及ANSYS APDL脚本,用于柔轮变形分析、齿廓修形设计、有限元验证和疲劳寿命预测。 适合人群:机械工程领域的研究人员、工程师,尤其是从事精密传动系统设计和分析的专业人士。 使用场景及目标:①解决杯形谐波减速器中柔轮与波发生器装配时的啮合干涉问题;②通过优化齿廓修形提高柔轮的力学性能和使用寿命;③利用有限元分析和疲劳寿命预测技术评估修形效果,确保设计方案的可靠性和可行性。 阅读建议:本文涉及大量有限元分析和疲劳寿命预测的具体实现细节,建议读者具备一定的机械工程基础知识和有限元分析经验。同时,读者可以通过提供的Python代码和ANSYS APDL脚本进行实际操作和验证,加深对修形方法和技术路线的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值