FreeRTOS 任务通知

目录

简介

任务通知值的更新方式 

任务通知的优势及劣势

 任务通知的优势:

任务通知的劣势:

任务通知值和通知状态

任务通知值和通知状态

任务通知值

任务通知状态

任务通知相关API函数介绍

任务通知的主要函数

发送通知相关API函数:

 xTaskNotify()

 xTaskNotifyAndQuery()

xTaskNotifyGive()

接收通知相关API函数: 

 ulTaskNotifyTake()

xTaskNotifyWait ()

任务通知模拟信号量实验

二值信号量实验代码

二值信号量实验结果 

计数型信号量实验代码

计数型信号量实验结果 

任务通知模拟消息邮箱实验

实验代码

实验结果 

任务通知模拟事件标志组实验

实验代码

实验结果 


简介

        在FreeRTOS中,任务通知是一种轻量级的任务间通信机制,可以在任务之间传递信息、同步任务或实现事件通知。它比消息队列和信号量更高效,适合于简单的任务同步和状态传递

任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue 就是这个通知值 

  • 使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信! 

  • 使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知" 

任务通知值的更新方式 

  • 不覆盖接受任务的通知值
  • 覆盖接受任务的通知值
  • 更新接受任务通知值的一个或多个bit
  • 增加接受任务的通知值

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

任务通知的优势及劣势

 任务通知的优势:
  • 效率更高:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
  • 使用内存更小:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体
任务通知的劣势:
  • 无法发送数据给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 */
  • 一个是 uint32_t 类型,用来表示通知值 
  • 一个是 uint8_t 类型,用来表示通知状态

任务通知值和通知状态

任务通知值

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

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

任务通知状态

其中任务通知状态共有3种取值:

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

任务通知相关API函数介绍

任务通知的主要函数

  • 发送通知:使用 xTaskNotifyGive()xTaskNotify() 来发送通知。
  • 接收通知:使用 ulTaskNotifyTake() 来接收通知。

注意:发送通知API函数可以用于任务和中断服务函数中;接收通知API函数只能用在任务中。

发送通知相关API函数:

函数

描述

xTaskNotify()

发送通知,带有通知值

xTaskNotifyAndQuery()

发送通知,带有通知值并且保留接收任务的原通知值

xTaskNotifyGive()

发送通知,不带通知值

xTaskNotifyFromISR()

在中断中发送任务通知

xTaskNotifyAndQueryFromISR()

vTaskNotifyGiveFromISR()

 xTaskNotify()
#define	xTaskNotify        ( xTaskToNotify , 
                             ulValue ,
                             eAction  ) 							\   
 		xTaskGenericNotify ( ( xTaskToNotify ) , 
                           ( tskDEFAULT_INDEX_TO_NOTIFY ) ,
                           ( ulValue ) ,
                           ( eAction ) ,
                             NULL  )
 xTaskNotifyAndQuery()
#define  xTaskNotifyAndQuery (xTaskToNotify, 
                              ulValue,  
                              eAction,
                              pulPreviousNotifyValue)	        \   
		 xTaskGenericNotify( ( xTaskToNotify ), 
					       ( tskDEFAULT_INDEX_TO_NOTIFY ), 
					       ( ulValue ), 
					       ( eAction ),
					       ( pulPreviousNotifyValue ) )
xTaskNotifyGive()
#define  xTaskNotifyGive   ( xTaskToNotify  )			 \   
		 xTaskGenericNotify( ( xTaskToNotify ) ,
                             ( tskDEFAULT_INDEX_TO_NOTIFY ) , 
                             ( 0 ) , 
                               eIncrement ,
                               NULL )
BaseType_t	 xTaskGenericNotify(  TaskHandle_t 	xTaskToNotify,
                                  UBaseType_t 	uxIndexToNotify,
                                  uint32_t 		ulValue,
                                  eNotifyAction eAction,
                                  uint32_t * 	pulPreviousNotificationValue  )

形参

描述

xTaskToNotify

接收任务通知的任务句柄

uxIndexToNotify

任务的指定通知(任务通知相关数组成员)

ulValue

任务通知值

eAction

通知方式(通知值更新方式)

pulPreviousNotificationValue

用于保存更新前的任务通知值(为NULL则不保存)

 任务通知方式共有以下几种:

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

接收通知相关API函数: 

函数

描述

ulTaskNotifyTake()

获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一。

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

xTaskNotifyWait()

获取任务通知,比 ulTaskNotifyTak()更为复杂,可获取通知值和清除通知值的指定位

总结: 

  • 当任务通知用作于信号量时,使用函数获取信号量:ulTaskNotifyTake()
  • 当任务通知用作于事件标志组或队列时,使用此函数来获取: xTaskNotifyWait()
 ulTaskNotifyTake()

         此函数用于接收任务通知值,可以设置在退出此函数的时候将任务通知值清零或者减一

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

形参

描述

uxIndexToWaitOn

任务的指定通知(任务通知相关数组成员)

xClearCountOnExit

指定在成功接收通知后,将通知值清零或减 1,

pdTRUE:把通知值清零;pdFALSE:把通知值减一

xTicksToWait

阻塞等待任务通知值的最大时间

返回值

描述

0

接收失败

非 0

接收成功,返回任务通知的通知值

xTaskNotifyWait ()

        此函数用于获取通知值和清除通知值的指定位值,适用于模拟队列和事件标志组,使用该函数来获取任务通知 。

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

 xTaskGenericNotifyWait()

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

形参

描述

uxIndexToWaitOn

任务的指定通知(任务通知相关数组成员)

ulBitesToClearOnEntry

等待前清零指定任务通知值的比特位(旧值对应bit清0)

ulBitesToClearOnExit

成功等待后清零指定的任务通知值比特位(新值对应bit清0)

pulNotificationValue

用来取出通知值(如果不需要取出,可设为NULL)

xTicksToWait

阻塞等待任务通知值的最大时间

返回值

描述

pdTRUE

等待任务通知成功

pdFALSE

等待任务通知失败

任务通知模拟信号量实验

实验目的:学习使用 FreeRTOS 中的任务通知功能模拟二值信号量和计数型信号量

实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:

  • start_task:用来创建task1和task2任务
  • task1:用于按键扫描,当检测到按键KEY0被按下时,将发送任务通知
  • 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 rev = 0;
    while(1)
    {
        rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("接收任务通知成功,模拟获取二值信号量!\r\n");
        }
    }
}

二值信号量实验结果 

计数型信号量实验代码

/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("任务通知模拟计数型信号量释放!\r\n");
            xTaskNotifyGive(Task2Task_Handler);
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t rev = 0;
    while(1)
    {
        rev = ulTaskNotifyTake(pdFALSE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("任务通知模拟计数型信号量获取次数rev:%d\r\n",rev);
        }
        vTaskDelay(1000);
    }
}

计数型信号量实验结果 

任务通知模拟消息邮箱实验

实验目的:学习使用 FreeRTOS 中的任务通知功能模拟消息邮箱

实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:

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

实验代码

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

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t noyify_val = 0;
    while(1)
    {
        xTaskNotifyWait( 0, 0xFFFFFFFF, &noyify_val, portMAX_DELAY );
        switch(noyify_val)
        {
            case KEY0_PRES:
            {
                printf("接收到的通知值为:%d\r\n",noyify_val);
                LED0_TOGGLE();
                break;
            }
            case KEY1_PRES:
            {
                printf("接收到的通知值为:%d\r\n",noyify_val);
                LED1_TOGGLE();
                break;
            }
            default : break;
        }
		
    }
}

实验结果 

任务通知模拟事件标志组实验

实验目的:学习使用 FreeRTOS 中的任务通知功能模拟事件标志组

实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:

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

实验代码

#define EVENTBIT_0  (1 << 0)
#define EVENTBIT_1  (1 << 1)
/* 任务一,发送任务通知值*/
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("将bit0位置1\r\n");
            xTaskNotify( Task2Task_Handler, EVENTBIT_0, eSetBits );
        }else if(key == KEY1_PRES)
        {
            printf("将bit1位置1\r\n");
            xTaskNotify( Task2Task_Handler, EVENTBIT_1, eSetBits );
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t notify_val = 0,event_bit = 0;
    while(1)
    {
        xTaskNotifyWait( 0, 0xFFFFFFFF, &notify_val, portMAX_DELAY );
        if(notify_val & EVENTBIT_0)
        {
            event_bit |= EVENTBIT_0;
        }
        if(notify_val & EVENTBIT_1)
        {
            event_bit |= EVENTBIT_1;
        }
        if(event_bit == (EVENTBIT_0|EVENTBIT_1)) //位逻辑
        {
            printf("任务通知模拟事件标志组接收成功!!\r\n");
            event_bit = 0;
        }
    }
}

实验结果 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值