【FreeRtos任务通知详解】

FreeRtos任务通知详解

文章目录

  • FreeRtos任务通知详解
  • 前言
  • 一、任务通知介绍
  • 二、任务通知API函数
    • 2.1 任务通知函数
    • 2.2 获取任务通知函数
  • 三、任务通知模拟二值信号量
    • 3.1 实验需求
    • 3.2 实验结果
  • 四、任务通知模拟计数型信号量
    • 4.1 实验需求
    • 4.2 实验结果
  • 五、任务通知模拟消息邮箱
    • 5.1 不足说明
    • 5.2 实验需求
    • 5.3 实验结果
  • 六、任务通知模拟事件标志组
    • 6.1 实验需求
    • 6.2 实验结果


前言

本次进行分享的是FreeRtos中任务通知相关的知识以及使用,通过此次的学习更能让我们掌握Freertos中的一些任务机制不同的实现方法,下面会用任务通知进行模拟实现前面的二值信号量、计数型信号量、信息邮箱、以及事件标志组的实现。

一、任务通知介绍

任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏
configUSE_TASK_NOTIFICATIONS 定义为 1。
FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量ulNotifiedValue就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
●不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
● 覆盖接收任务的通知值。
● 更新接收任务通知值的一个或多个 bit。
● 增加接收任务的通知值。

二、任务通知API函数

2.1 任务通知函数

【FreeRtos任务通知详解】_第1张图片1、函数 xTaskNotify()

此函数用于发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数 xTaskGenericNotify()

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, 
uint32_t ulValue, eNotifyAction eAction ) 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法,eNotifyAction 是个枚举类型,在文件 task.h
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

typedef enum 
{ 
 eNoAction = 0, 
 eSetBits, //更新指定的 bit 
 eIncrement, //通知值加一 
 eSetValueWithOverwrite, //覆写的方式更新通知值 
 eSetValueWithoutOverwrite //不覆写通知值 
} eNotifyAction; 

2、函数 xTaskNotifyFromISR()

此函数用于发送任务通知,是函数 xTaskNotify()的中断版本,此函数是个宏,真正执行的是函数 xTaskGenericNotifyFromISR()

BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, 
uint32_t ulValue, 
eNotifyAction eAction, 
BaseType_t * pxHigherPriorityTaskWoken ); 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

3、函数 xTaskNotifyGive()

发送任务通知,相对于函数 xTaskNotify(),此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数 xTaskGenericNotify()

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
返回值:
pdPASS: 此函数只会返回 pdPASS。

4、函数 vTaskNotifyGiveFromISR()

此函数为 xTaskNotifyGive()的中断版本,用在中断服务函数中

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, 
BaseType_t * pxHigherPriorityTaskWoken ); 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:

5、函数 xTaskNotifyAndQuery()

此函数和 xTaskNotify()很类似,此函数比 xTaskNotify()多一个参数,此参数用来保存更新前的通知值。此函数是个宏,真正执行的是函数 xTaskGenericNotify()

BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify, 
uint32_t ulValue, 
eNotifyAction eAction 
uint32_t * pulPreviousNotificationValue); 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pulPreviousNotificationValue:用来保存更新前的任务通知值。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

6、函数 xTaskNotifyAndQueryFromISR()

此函数为 xTaskNorityAndQuery()的中断版本,用在中断服务函数中。此函数同样为宏,真正执行的是函数 xTaskGenericNotifyFromISR()

BaseType_t xTaskNotifyAndQueryFromISR ( TaskHandle_t xTaskToNotify, 
uint32_t ulValue, 
eNotifyAction eAction, 
uint32_t * pulPreviousNotificationValue 
BaseType_t * pxHigherPriorityTaskWoken ); 

参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pulPreviousNotificationValue:用来保存更新前的任务通知值。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

2.2 获取任务通知函数

【FreeRtos任务通知详解】_第2张图片

1、函数 ulTaskNotifyTake()

此函数为获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使用此函数来获取信号量。

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, 
TickType_t xTicksToWait ); 

参数:
xClearCountOnExit: 参数为 pdFALSE 的话在退出函数 ulTaskNotifyTake()的时候任务通知值减一,类似计数型信号量。当此参数为 pdTRUE 的话在退出函数的时候任务任务通知值清零,类似二值信号量。
xTickToWait: 阻塞时间。
返回值:
任何值 : 任务通知值减少或者清零之前的值。

2、函数 xTaskNotifyWait()

函数也是用来获取任务通知的,不过此函数比 ulTaskNotifyTake()更为强大,不管任务通知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取任务通知。但是当任务通知用作位置信号量和计数型信号量的时候推荐使用函数ulTaskNotifyTake()

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

参数:
ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者 ULONG_MAX 的时候就会将任务通知值清零。
ulBitsToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者
ULONG_MAX 的时候就会将任务通知值清零。
pulNotificationValue:此参数用来保存任务通知值。
xTickToWait: 阻塞时间。
返回值:
pdTRUE: 获取到了任务通知。
pdFALSE: 任务通知获取失败。

三、任务通知模拟二值信号量

3.1 实验需求

通过串口中断接收信息后,通过任务中断函数胡向主程序的处理功能发送通知进行数据的打印

usart.c

extern TaskHandle_t DATAPROCESSTask_Handler;
BaseType_t  pxHigherPriorityTaskWoken;

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		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))
			 {
				  vTaskNotifyGiveFromISR(DATAPROCESSTask_Handler,&pxHigherPriorityTaskWoken);
					portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
			 }	
}

3.2 实验结果

【FreeRtos任务通知详解】_第3张图片

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "semphr.h"
#include "string.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO		4
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);



//按键任务

#define DATAPROCESS_TASK_PRIO		2
//任务堆栈大小	
#define DATAPROCESS_STK_SIZE 		50  
//任务句柄
TaskHandle_t DATAPROCESSTask_Handler;
//任务函数
void dataprocess_task(void *pvParameters);



int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_GPIO_INIT();          //按键初始化
	printf("--------------任务通知模拟二值信号量实验测试---------------\r\n");
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);     

		//创建按键任务
		xTaskCreate((TaskFunction_t )dataprocess_task,     
                (const char*    )"dataprocess_task",   
                (uint16_t       )DATAPROCESS_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )DATAPROCESS_TASK_PRIO,
                (TaskHandle_t*  )&DATAPROCESSTask_Handler);    
	
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//LED0任务函数 
void led0_task(void *pvParameters)
{
	u8 key;
	while(1)
    {
        
			key = KEY_Scan();
			if(key == KEY0_Value)  //PB9
			{
				
			}
			LED0=~LED0;
      vTaskDelay(500);
			  //printf("led1 is running\r\n");
    }
}   



//处理函数 
void dataprocess_task(void *pvParameters)
{

	uint32_t value = pdFALSE;
	while(1)
	{
		
			value = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
			if(value == 1)
			{
				printf("value = %d\r\n",value);
				printf("dataprocess_task data = %s\r\n",USART_RX_BUF);
				memset(USART_RX_BUF,0,USART_REC_LEN);
				USART_RX_STA = 0;
			}

	}
}

四、任务通知模拟计数型信号量

不同与二值信号量,计数型信号量值可以大 1,这个最大值在创建信号量的时候可以设置。当计数型信号量有效的时候任务可以获取计数型信号量,信号量值只要大于 0 就表示计数型信号量有效。
当任务通知用作计数型信号量的时候获取信号量相当于获取任务通知值,使用函数
ulTaskNotifyTake()来替代函数 xSemaphoreTake()。函数 ulTaskNotifyTake()的参数 xClearOnExit要设置为 pdFLASE,这样每次获取任务通知成功以后任务通知值就会减一。使用任务通知发送函 数 xTaskNotifyGive() 和 vTaskNotifyGiveFromISR() 来 替 代 计 数 型 信 号 量 释 放 函 数xSemaphoreGive()和 xSemaphoreGiveFromISR()

4.1 实验需求

通过按键进行任务的通知,处理函数获取通知后将value值打印出来

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "semphr.h"
#include "string.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO		4
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);



//按键任务

#define DATAPROCESS_TASK_PRIO		2
//任务堆栈大小	
#define DATAPROCESS_STK_SIZE 		50  
//任务句柄
TaskHandle_t DATAPROCESSTask_Handler;
//任务函数
void dataprocess_task(void *pvParameters);



int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_GPIO_INIT();          //按键初始化
	printf("--------------任务通知模拟计数型信号量实验测试---------------\r\n");
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);     

		//创建按键任务
		xTaskCreate((TaskFunction_t )dataprocess_task,     
                (const char*    )"dataprocess_task",   
                (uint16_t       )DATAPROCESS_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )DATAPROCESS_TASK_PRIO,
                (TaskHandle_t*  )&DATAPROCESSTask_Handler);    
	
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//LED0任务函数 
void led0_task(void *pvParameters)
{
	u8 key;
	while(1)
    {
        
			key = KEY_Scan();
			if(key == KEY0_Value)  //PB9
			{
				if(NULL != DATAPROCESSTask_Handler)
				{					
					xTaskNotifyGive(DATAPROCESSTask_Handler);
				}
					
			}
			LED0=~LED0;
      vTaskDelay(500);
			  //printf("led1 is running\r\n");
    }
}   



//按键任务函数
void dataprocess_task(void *pvParameters)
{

	uint32_t value = pdFALSE;
	while(1)
	{
			value = ulTaskNotifyTake(pdFAIL,portMAX_DELAY);    
			printf("value = %d\r\n",value);
	}
}

4.2 实验结果

下面使用结果全是1 因为我每次只加了一下,内部会自动进行计数减 并保留上次的值

【FreeRtos任务通知详解】_第4张图片

五、任务通知模拟消息邮箱

5.1 不足说明

任务通知也可用来向任务发送数据,但是相对于用队列发送消息,任务通知向任务发送消息会受到很多限制!
1、只能发送 32 位的数据值。
2、消息被保存为任务的任务通知值,而且一次只能保存一个任务通知值,相当于队列长度为 1。
因此说任务通知可以模拟一个轻量级的消息邮箱而不是轻量级的消息队列。任务通知值就是消息邮箱的值。

5.2 实验需求

通过按键来读取键值发送给处理任务进行功能处理

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "key.h"
#include "time.h"
#include "limits.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED0_TASK_PRIO		2
//任务堆栈大小	
#define LED0_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);


//按键处理任务

//任务优先级
#define KEY_TASK_PRIO		3
//任务堆栈大小	
#define KEY_STK_SIZE 		50  
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);



int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_GPIO_INIT();            //按键初始化
	TIM2_Int_Init(5000-1,7200-1);           //定时器初始化  1s
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
    //创建LED0任务
    xTaskCreate((TaskFunction_t )led0_task,     	
                (const char*    )"led0_task",   	
                (uint16_t       )LED0_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED0_TASK_PRIO,	
                (TaskHandle_t*  )&LED0Task_Handler);   
    //创建LED1任务
    xTaskCreate((TaskFunction_t )key_task,     
                (const char*    )"key_task",   
                (uint16_t       )KEY_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )KEY_TASK_PRIO,
                (TaskHandle_t*  )&KEYTask_Handler);         
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}



void led0_task(void *pvParameters)
{
  u8 key; 
	while(1)
    {
			key = KEY_Scan();
			if((KEYTask_Handler != NULL) && (key))  //队列按键都有效
			{
				xTaskNotify(KEYTask_Handler,(uint32_t)key,eSetValueWithOverwrite);
			}
//			LED0=~LED0;
//      vTaskDelay(500);
			// printf("led1 is running\r\n");
    }
}   

//LED1任务函数
void key_task(void *pvParameters)
{
	uint32_t NotifyValue;
	BaseType_t err;
	while(1)
    {
					err = xTaskNotifyWait(0,ULONG_MAX,&NotifyValue,portMAX_DELAY);	 
						if(err == pdPASS)  //获取成功
						{
							printf("NotifyValue = %d\r\n",NotifyValue);
							//下面可以通过按键值执行不同的功能
							switch(NotifyValue)
							{
								case 1:LED0 = 0;printf("LED0 OPEN\r\n");break;
								case 2:LED0 = 1;printf("LED0 CLOSE\r\n");break;
							}
						}
						
    }
}

5.3 实验结果

【FreeRtos任务通知详解】_第5张图片

六、任务通知模拟事件标志组

事件标志组其实就是一组二进制事件标志(位),每个事件标志位的具体意义由应用程序编写者来决定。当一个任务等待事件标志组中的某几个标志(位)的时候可以进入阻塞态,当任务因为等待事件标志(位)而进入阻塞态以后这个任务就不会消耗 CPU。

6.1 实验需求

通过按键1和2 都触发后进行事件响应并且值打印出来和触发运行次数打印出来

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "semphr.h"
#include "string.h"
#include "limits.h"


//任务优先级
#define START_TASK_PRIO		1
//任务堆栈大小	
#define START_STK_SIZE 		128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//事件置位任务
#define EVENTSETBITS_TASK_PRIO		2
//任务堆栈大小	
#define EVENTSETBITS_STK_SIZE 		50  
//任务句柄
TaskHandle_t EVENTTSETBITSask_Handler;
//任务函数
void eventsetbits_task(void *pvParameters);

//事件组

#define EVENTGROUP_TASK_PRIO		3
//任务堆栈大小	
#define EVENTGROUP_STK_SIZE 		50  
//任务句柄
TaskHandle_t EVENTGROUPTask_Handler;
//任务函数
void eventgroup_task(void *pvParameters);


#define EVENTBIT0  (1 << 0)
#define EVENTBIT1  (1 << 1)
#define EVENTBIT2  (1 << 2)
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_GPIO_INIT();          //按键初始化
	printf("------------------任务通知模拟事件标志组实验------------------\r\n");
	 
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
	
    //事件置位
    xTaskCreate((TaskFunction_t )eventsetbits_task,     	
                (const char*    )"eventsetbits_task",   	
                (uint16_t       )EVENTSETBITS_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )EVENTSETBITS_TASK_PRIO,	
                (TaskHandle_t*  )&EVENTTSETBITSask_Handler);     

		//事件组
		xTaskCreate((TaskFunction_t )eventgroup_task,     
                (const char*    )"eventgroup_task",   
                (uint16_t       )EVENTGROUP_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )EVENTGROUP_TASK_PRIO,
                (TaskHandle_t*  )&EVENTGROUPTask_Handler);   

  	
	
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}

//eventsetbits_task
void eventsetbits_task(void *pvParameters)
{
	u8 keyvalue = 0;
	while(1)
    {
      if(NULL != EVENTGROUPTask_Handler)
			{
				
					keyvalue = KEY_Scan();
					switch(keyvalue)
					{
						case 1:
							xTaskNotify(EVENTGROUPTask_Handler,EVENTBIT1,eSetBits);
							
							break;
						case 2:
							xTaskNotify(EVENTGROUPTask_Handler,EVENTBIT2,eSetBits);
							break;			
					}
				
			}
		
	//		printf("eventsetbits_task \r\n");
      vTaskDelay(10);
			
    }
}   

void eventgroup_task(void *pvParameters)
{
	BaseType_t err;
	uint32_t NotifyValue;
	static u8 eventvalue1,eventvalue2,eventvalue;
	u8 num = 0;
	while(1)
    {
			
			err = xTaskNotifyWait(0,ULONG_MAX,&NotifyValue,portMAX_DELAY);
			if(err == pdPASS)
			{
					if(NotifyValue & EVENTBIT1)
					{
						eventvalue1 = 1;
					}
					else if(NotifyValue & EVENTBIT2)
					{
						eventvalue2= 1;
					}
			}
			
			eventvalue = ((eventvalue1 <<1) |(eventvalue2 <<2));
			printf("eventvalue = %d\r\n",eventvalue);
			if(eventvalue == 0x06)
			{
				num++;
				eventvalue1 = 0,eventvalue2 = 0;
				printf("event running = %d\r\n",num);
			}
   }
}   

6.2 实验结果

【FreeRtos任务通知详解】_第6张图片

以上就是FreeRtos关于任务通知的一些简单使用  加油

你可能感兴趣的:(FreeRTOS,单片机,stm32,FreeRtos,c语言)