FreeRTOS-任务通知

简介

在 FreeRTOS 中,每一个任务都有两个用于任务通知功能的数组,分别为任务通知数组任务通知状态数组。其中任务通知数组中的每一个元素都是一个 32 位无符号类型的通知值;而任务通知状态数组中的元素则表示与之对应的任务通知的状态。
任务通知数组:用于任务到任务,中断到任务发送通知的媒介。为0表示没有任务通知;非0则表示有任务通知,且通知值就是该内容。
任务通知状态数组:用于标记任务通知数组中通知的状态。分别为未等待通知状态、等待通知状态和等待接收通知状态

任务通知的优缺点

优点
1.速度快:任务通知是直接地往任务中发送的通知。
2.占用的内存小:不同于队列、事件标志组和信号量在使用之前都需要被创建,任务通知功能中的每个通知只需要在每个任务中占用固定的 5 字节内存。
缺点
1.不能从任务给中断发生事件或数据。原因是中断不是任务缺少任务控制块中的两个成员变量。
2.每个任务只能接受一个通知。
3.只能用一个任务接收通知消息。因为在发送时已经指定了要通知的任务。

API函数

发送:

任务中:
xTaskNotify();
xTaskNotifyAndQuery() ;
xTaskNotifyGive();
中断中:
xTaskNotifyFromISR();
xTaskNotifyAndQueryFromISR();
vTaskNotifyGiveFromISR();

接收:

ulTaskNotifyTake();
xTaskNotifyWait();

发送

三个发送任务通知函数实际都是通过define将xTaskGenericNotify函数的某些传参设置为不同默认值。
xTaskGenericNotify()函数原型是:

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

参数一:要通知的任务的任务句柄。
参数二:任务的指定通知(任务通知相关数组下标)。
参数三:与通知一起发送的值。这个值可以用来传递信息。
参数四:通知方式。
参数五:用于获取发送通知前的通知值。
返回值:任务通知发送成功返回pdPASS,失败返回pdFALL;

xTaskNotifyGive()

用于在任务中向指定任务发送任务通知,通知方式为将通知值加 1,并且发送任务通知的通知值。(用于二值信号量或计数信号量,实验二/三)

void xTaskNotifyGive( xEventGroupHandle xEvent );

参数一:要通知的任务的任务句柄。
一般与获取ulTaskNotifyTake()函数连用。
在中断中:
带有中断保护功能。

void xTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken );

参数一:要通知的任务的任务句柄。
参数二:退出这个函数时是否进行任务的切换。

xTaskNotify()

用于往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送任务通知前任务通知的通知值。(事件组)

uint32_t xTaskNotify(xTaskHandle xTaskToNotify, uint32_t u32Value, eNotifyAction eAction)

参数一:要通知的任务句柄。
参数二:与通知一起发送的值。这个值可以用来传递信息。
参数三:通知方式。

通知方式Value
eNoAction对象任务接收任务通知,但任务自身的任务通知值不更新。(ulValue没有用,也就是说只能使接收任务退出阻塞,不能获取发送的信息)
eSetValueWithOverwrite对象任务接收任务通知,且任务自身的任务通知值会无条件的被设置为ulValue。(可以看成函数xQueueOverwrite()一种轻量型的实现,速度跟快。代替信息队列,如实验一)
eSetBits对象任务接收任务通知,同时自身的通知值与ulValue按位或。(如果ulValue设置为0x04,那么任务的通知值的位2被置为一,其他位保持不变;用作事件组
eIncrement对象任务接收任务通知,任务自身通知值加一。(ulValue没有用,同等于xTaskNotifyGive())
eSetValueWithoutOverwrite对象任务接收任务通知,如果对象任务没有通知值,那么通知值被设置为ulValue。有的话(上回通知值没有被取走),通知值保持不变,同时返回pdFALSE.

xTaskNotifyAndQuery()

用于往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任务通知前任务通知的通知值。

获取

用 于 获 取 任 务 通 知 的 API 函 数 有 两 个 , 分 别 为 函 数 ulTaskNotifyTake() 和函数xTaskNotifyWait()。只能在任务中使用,不能在中断里使用。

ulTaskNotifyTake()

用于获取任务通知的通知值,并且在成功获取任务通知的通知值后,可以指定将通知值清零或减 1。
(与xTaskNotifyGive()函数,做轻量型的二值信号量和计数信号量)

ulTaskNotifyTake( xBitsToClear, uxBitsToWait );

参数一:设置为pdFALSE时,函数退出前,任务的通知值减一,用来实现计数量。设置为pdTRUE时,函数退出前,将任务通知值清零,用来实现二值信号量。
参数二:超时时间。
返回值:返回任务的当前通知值,在其减一或清零之前。

ulTaskNotifyWait()

用于等待一个任务通知,并带有超时等待。

xTaskNotifyWait( ulBitsToClearOnEntry,
			   ulBitsToClearOnExit, 
			   pulNotificationValue, 
			   xTicksToWait
			   ) ;

参数一:表示在使用通知之前,将任务通知值的哪些位给清零。
参数二:决定在函数退出前,通知值哪些位被清零。在清零前,通知值被保存在形参*pulNotificationValue中。(如设置为0x03,则位0,位1被清零,其他位保持不变)
参数三:用于保存接收到的任务通知值。如果不需要使用,设置位NULL。
参数四:等待超时时间。
返回值:获取成功则返回pdTRUE,失败则返回pdFALSE.

实验

确保任务通知开启,宏configUSE_TASK_NOTIFICATIONS为一。

//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS    1  

代替信息队列

实验现象:
按下key2,任务ADD_task退出阻塞,接收发来的信息89。
按下key3,任务ADD_task退出阻塞,接收发来的信息23。
按键任务
向接收任务发送23和89。

void KEY_task(void *pvParameters)
{	
	BaseType_t xreturn = pdPASS;
	while(1)
  {		
		key=KEY_Scan();	
		if(key == 2) 
		{
			//发送任务通知  要发送任务句柄,发送信息23,覆盖当前值
			xreturn=xTaskNotify(ADDTask_Handler,23,eSetValueWithOverwrite);
			if(xreturn == pdPASS)
			{
				OLED_ShowString(2,5,"send success");
			}
			else
			{
				OLED_ShowString(2,5,"send false  ");
			}
		}	
		else if(key == 3) 
		{
		//发送任务通知  要发送任务句柄,发送信息89,覆盖当前值
			xreturn=xTaskNotify(ADDTask_Handler,89,eSetValueWithOverwrite);
			if(xreturn == pdPASS)
			{
				OLED_ShowString(2,5,"send success");
			}
			else
			{
				OLED_ShowString(2,5,"send false  ");
			}
			
		}		
		vTaskDelay(20);
  }
}

接收任务
接收显示按键任务传来的消息后进入到阻塞状态。

void ADD_task(void *pvParameters)
{

	BaseType_t xreturn = pdTRUE;
	uint32_t r_num;
	while(1)
	{
//获取任务通知,没获取到一直等待。进入函数前不清除任务bit,退出函数时清除所有bit,将任务通知值保存到r_num,阻塞
		xreturn=xTaskNotifyWait(0x0,clear_all,&r_num,portMAX_DELAY);
		OLED_ShowNum(3,5,num1++,2);
		OLED_ShowNum(4,5,r_num,3);
	}
}

代替二值信号量

实验现象:
按下key2,给任务ADDTask_Handler发送任务通知,其通知值加一。当任务ADDTask_Handler接受到之后退出阻塞状态,并且将通知值给清零。
发送任务:
xreturn=xTaskNotifyGive(ADDTask_Handler);
参数1:要通知的任务的任务句柄。
功能:发送任务通知 ,通知方式为将通知值加1,不发送任务通知的通知值。

void KEY_task(void *pvParameters)
{	
	BaseType_t xreturn = pdPASS;
	while(1)
  {		
		key=KEY_Scan();	

		if(key == 2) 
		{
			//发送任务通知  要发送任务句柄 通知方式为将通知值加1,不发送任务通知的通知值。
			xreturn=xTaskNotifyGive(ADDTask_Handler);
			if(xreturn == pdTRUE)
			{
				OLED_ShowString(2,5,"send success");
			}
			else
			{
				OLED_ShowString(2,5,"send false  ");
			}
		}							
		vTaskDelay(20);
  }
}

接收任务
xreturn=ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
参数一:pdTRUE:退出时通知值清零;pdFALSE;退出时通知值减一;
参数二:等待时间,一直等待。
功能:没有任务通知的时候处于阻塞状态,有通知时任务进入到就绪态,函数运行完将通知值给减一或清零。

void ADD_task(void *pvParameters)
{

	BaseType_t xreturn = pdTRUE;
	uint32_t r_num;
	while(1)
	{
	//获取任务通知 , 没获取到一直等待。pdTRUE:退出时通知值清零;pdFALSE: 退出时通知值减一
		xreturn=ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
		OLED_ShowNum(3,5,r_num++,2);
		OLED_ShowNum(4,5,xreturn,3);
	}
}

代替计数信号量

实验现象:
模拟停车场入库和出库。按键2按下表示有车出去,空闲停车位加一(发送一个通知,通知值加一)。按键3按下表示有人进入,空闲停车位减一(获取通知值,获取完通知值减一)。当有空余停车位的时候(通知值不为零),车辆成功进入。没有空余停车位的时候(通知值为零),车辆不能进入。
出库,发送通知值
xreturn=xTaskNotifyGive(ADDTask_Handler);
参数1:要通知的任务的任务句柄。
功能:发送任务通知 ,通知方式为将通知值加1,不发送任务通知的通知值。

//释放车位(出库)
void KEY_task(void *pvParameters)
{	
	BaseType_t xreturn = pdPASS;
	u8 key=0;
	while(1)
  {		
		key=KEY_Scan();	
		if(key == 2) 
		{
//发送任务通知  要发送任务句柄 通知方式为将通知值加1,不发送任务通知的通知值。
			xreturn=xTaskNotifyGive(KEY2Task_Handler);
			if(xreturn == pdTRUE)
			{
				OLED_ShowString(3,3,"release   ");
			} 
			else
			{
				OLED_ShowString(3,3," false    ");
			}
		}			
		vTaskDelay(20);
  }
}

入库,接收通知值
take_num=ulTaskNotifyTake(pdFAIL,0);
参数一:pdTRUE:退出时通知值清零;pdFALSE;退出时通知值减一;
参数二:等待时间,不等待。
功能:有任务通知时,其通知值在结束之后减一。

//获取车位(入库)
void KEY2_task(void *pvParameters)
{	
	uint32_t take_num = pdTRUE;
	u8 key=0;
	while(1)
  {		
		key=KEY_Scan();	
		if(key == 3) 
		{
			//接收任务通知 不等待  退出时通知值减一
			take_num=ulTaskNotifyTake(pdFAIL,0);
			if(take_num > 0)
			{
				OLED_ShowString(3,3,"Storage  ");
				OLED_ShowNum(4,13,take_num-1,3);		
			}
			else
			{
		  	OLED_ShowString(3,3,"Nowhere  ");
			
			}
		
		}			
		vTaskDelay(20);
  }
}

代替事件组

总结

任务通知通过模拟可以更轻量的实现消息队列,信号量,事件组等。

代替信息队列/事件组:

发送:

信息队列发送:
xTaskNotify(发送任务句柄,信息,eSetValueWithOverwrite);
eSetValueWithOverwrite:如果对象任务没有通知值,那么通知值被设置为ulValue。
						有的话保持不变,同时返回pdFALSE;

事件组发送:
xTaskNotify(发送任务句柄,key1_event,eSetBits);
key1_event:设置哪位为1 #define key1_event (0x01<<0) //设置事件掩码的位0
eSetBits:自身的通知值与ulValue(就是key1_event这个值)按位或

接收

信息队列接收:
xTaskNotifyWait(0x0,clear_all,&接收信息的变量,portMAX_DELAY)
clear_all:为  #define clear_all 0xffffffff
事件组接收:
xTaskNotifyWait(0x0,clear_all,&event,portMAX_DELAY);

几位同时满足
last_event |= event;
if(last_event == (key1_event | key2_event))
{
	last_event=0;
	OLED_ShowString(3,3,"all down ");
}
else
	last_event=event;
相当于
xEventGroupWaitBits(Event_Handle,key1_event|key2_event,pdTRUE,pdTRUE,portMAX_DELAY);

代替二值/计数信号量

发送

xTaskNotifyGive(ADDTask_Handler);
通知值加 1

接收

二值量接收:
ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
pdTRUE:函数退出时,通知值清零。阻塞
计数量接收:
ulTaskNotifyTake(pdFAIL,0);
pdFAIL:函数退出时,通知值减一。不阻塞
  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值