1.任务通知概念
任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值,不需要另外创建一个结构体可以直接接受别人发过来的通知
 
2.任务通知的优势及劣势
任务通知的优势:
 效率更高 :使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
 使用内存更小:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体
任务通知的劣势:
 无法发送数据给ISR:ISR没有任务结构体,所以无法给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
 无法广播给多个任务:任务通知只能是被指定的一个任务接收并处理
 无法缓存多个数据:任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据。
 发送受阻不支持阻塞:发送方无法进入阻塞状态等待
3.任务结构体中任务通知值和通知状态
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 类型,用来表示通知状态
4 任务通知状态
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 任务未等待通知 /
 #define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) / 任务在等待通知 /
 #define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) / 任务在等待接收 */
 未等待通知:初始化状态
 等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知
 等待接收:发送方已经发送出去(调用了发送任务通知函数),等待接收方接收
5.任务通知API函数
1)发送通知相关API函数
 
 注意;发送通知API函数可以用于任务和中断服务函数中
 BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
 UBaseType_t uxIndexToNotify,
 uint32_t ulValue,
 eNotifyAction eAction,
 uint32_t * pulPreviousNotificationValue)
 
 2)接收任务通知相关API函数
 
 1)#define ulTaskNotifyTake( xClearCountOnExit , xTicksToWait ) 
 ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), 
 ( xClearCountOnExit ), 
 \ ( xTicksToWait ) )
 形参: uxIndexToWaitOn 任务的指定通知(任务通知相关数组成员)
 xClearCountOnExit 指定在成功接收通知后,将通知值清零或减 1,
 pdTRUE:把通知值清零;
 pdFALSE:把通知值减一
 xTicksToWait 阻塞等待任务通知值的最大时间
返回值:0 接收失败
 其他 接收成功,返回任务通知的通知值
2)#define xTaskNotifyWait( ulBitsToClearOnEntry, \ 
 ulBitsToClearOnExit, \ 
 pulNotificationValue, \ 
 xTicksToWait) 
 xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, 
 \ ( ulBitsToClearOnEntry ), 
 \ ( ulBitsToClearOnExit ), 
 \ ( pulNotificationValue ), 
 \ ( xTicksToWait ) )
形参: uxIndexToWaitOn 任务的指定通知(任务通知相关数组成员)
 ulBitesToClearOnEntry 等待前清零指定任务通知值的比特位(旧值对应bit清0)
 ulBitsToClearOnExit 成功等待后清零指定的任务通知值比特位(新值对应bit清0)
 pulNotificationValue 用来取出通知值(如果不需要取出,可设为NULL)
 xTicksToWait 阻塞等待任务通知值的最大时间
返回值:0 接收失败
 其他 接收成功,返回任务通知的通知值
总结:
 1)当任务通知用作于信号量时,使用函数获取信号量:ulTaskNotifyTake()
 2)当任务通知用作于事件标志组或队列时,使用此函数来获取: xTaskNotifyWait()
6.任务通知的几种方式
typedef enum
 {
 eNoAction = 0, /* 无操作 /
 eSetBits / 更新指定bit /
 eIncrement / 通知值加一 /
 eSetValueWithOverwrite / 覆写的方式更新通知值 /
 eSetValueWithoutOverwrite / 不覆写通知值 */
 } eNotifyAction;
7.任务通知模拟信号量实验

 代码:
#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"/*1.FreeRTOS配置*//* 1.1 START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 *//* 1.2 TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* 1.3 TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *//*** 2 FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{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();
}/*** 3 start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           /* 进入临界区 *//* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char*    )"task1",(uint16_t       )TASK1_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK1_PRIO,(TaskHandle_t*  )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char*    )"task2",(uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2Task_Handler);vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL();            /* 退出临界区 */
}/*** 3 task1发送任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{uint8_t key = 0;while (1){key = key_scan(0);switch (key){case KEY0_PRES:                                         /* 发送任务通知 */{xTaskNotifyGive(Task2Task_Handler);printf("任务通知发送,模拟二值信号量释放!\r\n");break;}default:{break;}}vTaskDelay(10);}
}/*** 3 task2 接收任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{uint32_t rev = 0;while (1){rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);if(rev != 0){printf("接收任务通知成功,模拟获取二值信号量!\r\n");}}
}8.任务通知模拟计数型信号量实验
void task2(void *pvParameters)
{uint32_t rev = 0;while (1){rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);if(rev != 0){printf("rev:%d\r\n",rev);}vTaskDelay(1000);}
}
9.任务通知模拟事件标志组实验

#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"/******************************************************************************************************/
/*1.FreeRTOS配置*//* 1.1 START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 *//* 1.2 TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* 1.3 TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *///1.4 位0、位1宏定义
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)/*** 2 FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{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();
}/*** 3 start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           /* 进入临界区 *//* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char*    )"task1",(uint16_t       )TASK1_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK1_PRIO,(TaskHandle_t*  )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char*    )"task2",(uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2Task_Handler);vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL();            /* 退出临界区 */
}/*** 3 task1发送任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task1(void *pvParameters)
{uint8_t key = 0;while (1){key = key_scan(0);switch (key){case KEY0_PRES:                                         /* 发送任务通知 */{printf("将bit0位置置1\r\n");xTaskNotify( Task2Task_Handler,EVENTBIT_0, eSetBits);break;}case KEY1_PRES:                                         /* 发送任务通知 */{printf("将bit1位置置1\r\n");xTaskNotify( Task2Task_Handler,EVENTBIT_1, eSetBits);break;}default:{break;}}vTaskDelay(10);}
}/*** 3 task2 接收任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{uint32_t notify_val = 0,event_bit=0;while (1){xTaskNotifyWait( 0, 0xFFFFFFFF,¬ify_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.任务通知模拟消息邮箱实验

#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"#include "./BSP/LED/led.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"/******************************************************************************************************/
/*1.FreeRTOS配置*//* 1.1 START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 *//* 1.2 TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 *//* 1.3 TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 *//******************************************************************************************************//*** 2 FreeRTOS例程入口函数* @param       无* @retval      无*/
void freertos_demo(void)
{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();
}/*** 3 start_task* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL();           /* 进入临界区 *//* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char*    )"task1",(uint16_t       )TASK1_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK1_PRIO,(TaskHandle_t*  )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char*    )"task2",(uint16_t       )TASK2_STK_SIZE,(void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2Task_Handler);vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL();            /* 退出临界区 */
}/*** 3 task1发送任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
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);}
}/*** 3 task2 接收任务通知值* @param       pvParameters : 传入参数(未用到)* @retval      无*/
void task2(void *pvParameters)
{uint32_t notify_val = 0;while (1){xTaskNotifyWait(0,0xFFFFFFFF,¬ify_val,portMAX_DELAY);switch(notify_val){case KEY0_PRES:printf("接收到的通知为:%d\r\n",notify_val);LED0_TOGGLE();break;case KEY1_PRES:printf("接收到的通知为:%d\r\n",notify_val);LED1_TOGGLE();break;default : break;}}
}