STM32第二十八课(Freertos, HAL,cubemx,task notification)

本文探讨了如何利用TaskNotificationVector (TNV) 作为高级IPC手段,替代传统信号量、队列和事件组。通过实例展示如何用TNV模拟二进制信号量、计数信号量、消息盒和事件组,提高任务间通信的效率。
摘要由CSDN通过智能技术生成

task notification是更高级的IPC。
可以替代sem,queue, event group等。
TN是一种更高效率的IPC方式。

每个task都有一个task notification vector,TNV。32bits。

+++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟二值信号量。

void DataProcess_task(void *pvParameters)
{
	u8 len=0;
	u8 CommandValue=COMMANDERR;
	u32 NotifyValue;
	
	u8 *CommandStr;
	POINT_COLOR=BLUE;
	
	for(;;)
	{		
		NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);	//获取任务通知
		
		if(NotifyValue==1)									//清零之前的任务通知值为1,说明任务通知有效
		{
			len=USART_RX_STA&0x3fff;						//得到此次接收到的数据长度
			CommandStr=mymalloc(SRAMIN,len+1);				//申请内存
			sprintf((char*)CommandStr,"%s",USART_RX_BUF);
			CommandStr[len]='\0';							//加上字符串结尾符号
			LowerToCap(CommandStr,len);						//将字符串转换为大写		
			CommandValue=CommandProcess(CommandStr);		//命令解析
			
			if(CommandValue!=COMMANDERR)
			{
				LCD_Fill(10,90,210,110,WHITE);				//清除显示区域
				LCD_ShowString(10,90,200,16,16,CommandStr);	//在LCD上显示命令
				printf("命令为:%s\r\n",CommandStr);
				switch(CommandValue)						//处理命令
				{
					case LED1ON: 
						LED1=0;
						break;
					case LED1OFF:
						LED1=1;
						break;
					case BEEPON:
						BEEP=1;
						break;
					case BEEPOFF:
						BEEP=0;
						break;
				}
			}
			else
			{
				printf("无效的命令,请重新输入!!\r\n");
			}
			
			USART_RX_STA=0;
			memset(USART_RX_BUF,0,USART_REC_LEN);			//串口接收缓冲区清零
			myfree(SRAMIN,CommandStr);						//释放内存
		}
		else 
		{
			vTaskDelay(10);      //延时10ms,也就是10个时钟节拍	
		}
	}
}

任务主体的第一句,就是take,作为进程同步。

extern TaskHandle_t DataProcess_Handler;;	//接收任务通知的任务句柄

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
	BaseType_t xHigherPriorityTaskWoken;
	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
	
	//发送任务通知
	if((USART_RX_STA&0x8000)&&(DataProcess_Handler!=NULL))//接收到数据,并且接收任务通知的任务有效
	{
		vTaskNotifyGiveFromISR(DataProcess_Handler,&xHigherPriorityTaskWoken);//发送任务通知
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
	}
} 

在ISR中,调用give,向对应的TCB发出一个TN。

++++++++++++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟计数信号量。

//释放计数型信号量任务函数
void SemapGive_task(void *pvParameters)
{
	u8 key,i=0;
	for(;;)
	{
			key=KEY_Scan(0);           	        
            switch(key)
            {
                case WKUP_PRES:
					xTaskNotifyGive(SemapTakeTask_Handler);//发送任务通知
                    break;
            }
        
	        i++;
	        if(i==50)
	        {
	            i=0;
	            LED0=!LED0;
	        }
	        
	        vTaskDelay(10);     //延时10ms,也就是10个时钟节拍	
	}
}

这是前台任务,是一个输入任务,在任务中,扫描按键,根据按键状态,调用give这个API,向对应TCB发出TN。

//获取计数型信号量任务函数
void SemapTake_task(void *pvParameters)
{
    u8 num;
    uint32_t NotifyValue;
    
	for(;;)
	{
		NotifyValue=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);//获取任务通知
		
        num++;
        LCD_ShowxNum(166,111,NotifyValue-1,3,16,0);         //显示当前任务通知值
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);   	//刷屏
		LED1=!LED1;
	}
}

这是后台任务,是一个产出任务。在任务中,第一句就是进程同步,当TN有效时,解除阻塞。然后继续执行。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++
使用TNV模拟msgbox。

//task1任务函数
void task1_task(void *pvParameters)
{
	u8 key,i=0;
    BaseType_t err;
	
	for(;;)
	{
		key=KEY_Scan(0);            			//扫描按键
       
		err=xTaskNotify((TaskHandle_t	)Keyprocess_Handler,		//接收任务通知的任务句柄
						(uint32_t		)key,						//任务通知值
						(eNotifyAction	)eSetValueWithOverwrite);	//覆写的方式发送任务通知
		if(err==pdFAIL)
		{
			printf("任务通知发送失败\r\n");
		}
       
        
        i++;
        if(i==50)
        {
            i=0;
            LED0=!LED0;
        }
        
        vTaskDelay(10);           //延时10ms,也就是10个时钟节拍	
	}
}

这是一个前台任务,一个输入任务,将获得的按键状态值,强制转换成一个u32的值,赋值给对应的TCB中TNV。

//Keyprocess_task函数
void Keyprocess_task(void *pvParameters)
{
	u8 num;
	uint32_t NotifyValue;
	BaseType_t err;
	
	for(;;)
	{
		err=xTaskNotifyWait((uint32_t	)0x00,				//进入函数的时候不清除任务bit
							(uint32_t	)ULONG_MAX,			//退出函数的时候清除所有的bit
							(uint32_t*	)&NotifyValue,		//保存任务通知值
							(TickType_t	)portMAX_DELAY);	//阻塞时间
							
		if(err==pdTRUE)				//获取任务通知成功
		{
			switch((u8)NotifyValue)
			{
                case WKUP_PRES:		//KEY_UP控制LED1
                    LED1=!LED1;
					break;
				case KEY2_PRES:		//KEY2控制蜂鸣器
                    BEEP=!BEEP;
					break;
				case KEY0_PRES:		//KEY0刷新LCD背景
                    num++;
					LCD_Fill(6,126,233,313,lcd_discolor[num%14]);
                    break;
			}
		}
	}
}

这是一个后台任务,一个产出任务。在任务中,第一句就是进程同步。当TNV中存在没有被取走的有效数据时,将数据拷贝到指定的buffer中,并将任务解除阻塞。这里,buffer就是任务的栈变量NotifyValue。

++++++++++++++++++++++++++++++++++++++++
使用TNV模拟EG。

void eventsetbit_task(void *pvParameters)
{
	u8 key,i;
	
	for(;;)
	{	
			key=KEY_Scan(0);
			switch(key)
			{
				case KEY1_PRES:
					xTaskNotify((TaskHandle_t	)EventGroupTask_Handler,//接收任务通知的任务句柄
								(uint32_t		)EVENTBIT_1,			//要更新的bit
								(eNotifyAction	)eSetBits);				//更新指定的bit
					break;
				case KEY2_PRES:
					xTaskNotify((TaskHandle_t	)EventGroupTask_Handler,//接收任务通知的任务句柄
								(uint32_t		)EVENTBIT_2,			//要更新的bit
								(eNotifyAction	)eSetBits);				//更新指定的bit
					break;	
			}
		
			i++;
			if(i==50)
			{
				i=0;
				LED0=!LED0;
			}
			
	        vTaskDelay(10); //延时10ms,也就是10个时钟节拍
	}
}

这是一个前台任务,一个输入任务,根据获得的按键状态值,映射到对应的mask,并将mask强制转换成一个u32的值,形成一个EG,赋值给对应的TCB中TNV。

void eventgroup_task(void *pvParameters)
{
	u8 num=0,enevtvalue;
	static u8 event0flag,event1flag,event2flag;
	uint32_t NotifyValue;
	BaseType_t err;
	
	for(;;)
	{
		err=xTaskNotifyWait((uint32_t	)0x00,				//进入函数的时候不清除任务bit
							(uint32_t	)ULONG_MAX,			//退出函数的时候清除所有的bit
							(uint32_t*	)&NotifyValue,		//保存任务通知值
							(TickType_t	)portMAX_DELAY);	//阻塞时间
		
		if(err==pdPASS)	   //任务通知获取成功
		{
			if((NotifyValue&EVENTBIT_0)!=0)			//事件0发生	
			{
				event0flag=1;	
			}				
			else if((NotifyValue&EVENTBIT_1)!=0)	//事件1发生	
			{
				event1flag=1;
			}
			else if((NotifyValue&EVENTBIT_2)!=0)	//事件2发生	
			{
				event2flag=1;	
			}
	
			enevtvalue=event0flag|(event1flag<<1)|(event2flag<<2);	//模拟事件标志组值
			printf("任务通知值为:%d\r\n",enevtvalue);
			LCD_ShowxNum(174,110,enevtvalue,1,16,0);				//在LCD上显示当前事件值
			
			if((event0flag==1)&&(event1flag==1)&&(event2flag==1))	//三个事件都同时发生
			{
				num++;
				LED1=!LED1;	
				LCD_Fill(6,131,233,313,lcd_discolor[num%14]);
				event0flag=0;								//标志清零
				event1flag=0;
				event2flag=0;
			}
		}
		else
		{
			printf("任务通知获取失败\r\n");
		}

	}
}

这是一个后台任务,一个产出任务。在任务中,第一句就是进程同步。当TNV中存在没有被取走的有效数据时,将数据拷贝到指定的buffer中,并将任务解除阻塞。这里,buffer就是任务的栈变量NotifyValue。
TNV取出的数据,就是模拟EG的值。

extern TaskHandle_t EventGroupTask_Handler;

void EXTI4_IRQHandler(void)
{
	BaseType_t xHigherPriorityTaskWoken;
	
	delay_xms(20);	//消抖
	
	if(KEY0==0)	 
	{				 
		xTaskNotifyFromISR((TaskHandle_t	)EventGroupTask_Handler, 	//任务句柄
						   (uint32_t		)EVENTBIT_0, 				//要更新的bit
						   (eNotifyAction	)eSetBits, 					//更新指定的bit
						   (BaseType_t*		)xHigherPriorityTaskWoken);

		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
			 
	 EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位  
}

在前台ISR中,根据获得的按键状态值,映射到对应的mask,并将mask强制转换成一个u32的值,形成一个EG,赋值给对应的TCB中TNV。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值