1、任务通知
任务通知: 用来通知任务的,任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值
使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!
使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"
也就是说任务通知实现任务之间不同过中间结构体完成消息通讯
任务通知值的更新方式
不覆盖接受任务的通知值
覆盖接受任务的通知值
更新接受任务通知值的一个或多个bit
增加接受任务的通知值
只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!
任务通知的优势及劣势
任务通知的优势:
效率更高:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
使用内存更小 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体
任务通知的劣势:
无法发送数据给ISR:ISR没有任务结构体,所以无法给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
无法广播给多个任务 :任务通知只能是被指定的一个任务接收并处理
无法缓存多个数据:任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据。
发送受阻不支持阻塞:发送方无法进入阻塞状态等待
2,任务通知值和通知状态
任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量:
任务通知值
任务通知值的更新方式有多种类型:
计数值(数值累加,类似信号量)
相应位置一(类似事件标志组)
任意数值(支持覆写和不覆写,类似队列)
任务通知状态
其中任务通知状态共有3种取值:
任务未等待通知 :任务通知默认的初始化状态
等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知
等待接收:发送方已经发送出去(调用了发送任务通知函数),等待接收方接收
3,任务通知相关API函数介绍(熟悉)
任务通知API函数主要有两类:①发送通知 ,②接收通知。
注意:发送通知API函数可以用于任务和中断服务函数中;接收通知API函数只能用在任务中。
①发送通知相关API函数:
#define xTaskNotifyAndQuery( xTaskToNotify , ulValue , eAction , pulPreviousNotifyValue ) \ xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ),( pulPreviousNotifyValue ) )
#define xTaskNotify ( xTaskToNotify , ulValue , eAction ) \ xTaskGenericNotify( ( xTaskToNotify ) , ( tskDEFAULT_INDEX_TO_NOTIFY ) , ( ulValue ) , ( eAction ) , NULL )
#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 )
任务通知方式共有以下几种:
typedef enum
{ eNoAction = 0, /* 无操作 */eSetBits /* 更新指定bit */eIncrement /* 通知值加一 */eSetValueWithOverwrite /* 覆写的方式更新通知值 */eSetValueWithoutOverwrite /* 不覆写通知值 */
} eNotifyAction;
当任务通知用作于信号量时,使用函数获取信号量:ulTaskNotifyTake()
当任务通知用作于事件标志组或队列时,使用此函数来获取: xTaskNotifyWait()
#define ulTaskNotifyTake( xClearCountOnExit , xTicksToWait ) \
ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), \ ( xClearCountOnExit ), \ ( xTicksToWait ) )
此函数用于接收任务通知值,可以设置在退出此函数的时候将任务通知值清零或者减一
#define xTaskNotifyWait( ulBitsToClearOnEntry, \ ulBitsToClearOnExit, \ pulNotificationValue, \ xTicksToWait) \
xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, \ ( ulBitsToClearOnEntry ), \ ( ulBitsToClearOnExit ), \ ( pulNotificationValue ), \ ( xTicksToWait ) )
此函数用于获取通知值和清除通知值的指定位值,适用于模拟队列和事件标志组,使用该函数来获取任务通知 。
BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t * pulNotificationValue, TickType_t xTicksToWait );
4,任务通知模拟信号量实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟二值信号量和计数型信号量
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task 用来创建task1和task2任务
task1 用于按键扫描,当检测到按键KEY0被按下时,将发送任务通知
task2 用于接收任务通知,并打印相关提示信息
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );/*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/void freertos_demo(void)
{ xTaskCreate((TaskFunction_t) start_task,(char *) "start_task",(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *) NULL,(UBaseType_t) START_TASK_PRIO,(TaskHandle_t *)&start_task_handler);//开启任务调度vTaskStartScheduler();}
void start_task( void * pvParameters )
{taskENTER_CRITICAL(); /* 进入临界区 任何任务和中断都不能打断当前程序运行*/xTaskCreate((TaskFunction_t) task1,(char *) "task1",(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK1_PRIO,(TaskHandle_t *)&task1_handler );xTaskCreate((TaskFunction_t) task2,(char *) "task2",(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK2_PRIO,(TaskHandle_t *)&task2_handler );vTaskDelete(NULL);//删除当前任务也就是开始任务taskEXIT_CRITICAL();}
/* 任务一,用于扫描按键,当KEY0按下,发送任务通知值 */
void task1( void * pvParameters )
{uint8_t key;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");printf("rev val is %ld\r\n",rev);}}
}
5,任务通知模拟消息邮箱实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟消息邮箱
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task 用于按键扫描,将按下的按键键值通过任务通知发送给指定任务
task2 用于接收任务通知,并根据接收到的数据做相应动作
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );/******************************************************************************************************/
/*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/
void freertos_demo(void)
{ xTaskCreate((TaskFunction_t ) start_task,(char * ) "start_task",(configSTACK_DEPTH_TYPE ) START_TASK_STACK_SIZE,(void * ) NULL,(UBaseType_t ) START_TASK_PRIO,(TaskHandle_t * ) &start_task_handler );vTaskStartScheduler();
}void start_task( void * pvParameters )
{taskENTER_CRITICAL(); /* 进入临界区 */xTaskCreate((TaskFunction_t ) task1,(char * ) "task1",(configSTACK_DEPTH_TYPE ) TASK1_STACK_SIZE,(void * ) NULL,(UBaseType_t ) TASK1_PRIO,(TaskHandle_t * ) &task1_handler );xTaskCreate((TaskFunction_t ) task2,(char * ) "task2",(configSTACK_DEPTH_TYPE ) TASK2_STACK_SIZE,(void * ) NULL,(UBaseType_t ) TASK2_PRIO,(TaskHandle_t * ) &task2_handler );vTaskDelete(NULL);taskEXIT_CRITICAL(); /* 退出临界区 */
}/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{uint8_t key = 0;while(1) {key = key_scan(0);if((key != 0) && (task2_handler != NULL)){printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",key);xTaskNotify( task2_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;}}
}
任务通知模拟事件标志组实验(掌握)
1、实验目的:学习使用 FreeRTOS 中的任务通知功能模拟事件标志组
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
start_task 用来创建task1和task2任务
task1 用于按键扫描,当检测到按键按下时,发送任务通知设置不同标志位
task2 用于接收任务通知,并打印相关提示信息
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)/*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/void freertos_demo(void)
{ xTaskCreate((TaskFunction_t) start_task,(char *) "start_task",(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *) NULL,(UBaseType_t) START_TASK_PRIO,(TaskHandle_t *)&start_task_handler);//开启任务调度vTaskStartScheduler();}
void start_task( void * pvParameters )
{taskENTER_CRITICAL(); /* 进入临界区 任何任务和中断都不能打断当前程序运行*/xTaskCreate((TaskFunction_t) task1,(char *) "task1",(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK1_PRIO,(TaskHandle_t *)&task1_handler );xTaskCreate((TaskFunction_t) task2,(char *) "task2",(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK2_PRIO,(TaskHandle_t *)&task2_handler );vTaskDelete(NULL);//删除当前任务也就是开始任务taskEXIT_CRITICAL();}
/* 任务一,用于扫描按键,当KEY0按下,发送任务通知值 */
void task1( void * pvParameters )
{uint8_t key;while(1){key=key_scan(0);if(key==KEY0_PRES){printf("将bit0位置为1\r\n");xTaskNotify(task2_handler,EVENTBIT_0,eSetBits);}else if(key==KEY1_PRES){printf("将bit1位置为1\r\n");xTaskNotify(task2_handler,EVENTBIT_1,eSetBits);}vTaskDelay(10);}}
/* 任务二,读取队列集中的消息并打印*/
void task2( void * pvParameters )
{uint32_t rev =0 ;uint32_t event_bit =0;while(1){//接收前通知值不清零,接收后通知值全部清零,通知值给到revxTaskNotifyWait(0,0xFFFFFFFF,&rev,portMAX_DELAY);if(rev&EVENTBIT_0){event_bit|=EVENTBIT_0;}if(rev&EVENTBIT_1){event_bit|=EVENTBIT_1;}if(event_bit==(EVENTBIT_0|EVENTBIT_1)){printf("任务通知模拟事件标志组接收成功!!\r\n");event_bit=0;}}
}
7、模拟计数信号量
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
/* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );/* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );/*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/void freertos_demo(void)
{ xTaskCreate((TaskFunction_t) start_task,(char *) "start_task",(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *) NULL,(UBaseType_t) START_TASK_PRIO,(TaskHandle_t *)&start_task_handler);//开启任务调度vTaskStartScheduler();}
void start_task( void * pvParameters )
{taskENTER_CRITICAL(); /* 进入临界区 任何任务和中断都不能打断当前程序运行*/xTaskCreate((TaskFunction_t) task1,(char *) "task1",(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK1_PRIO,(TaskHandle_t *)&task1_handler );xTaskCreate((TaskFunction_t) task2,(char *) "task2",(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK2_PRIO,(TaskHandle_t *)&task2_handler );vTaskDelete(NULL);//删除当前任务也就是开始任务taskEXIT_CRITICAL();}
/* 任务一,用于扫描按键,当KEY0按下,发送任务通知值 */
void task1( void * pvParameters )
{uint8_t key;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(pdFALSE,portMAX_DELAY);if(rev!=0){printf("rev:%d\r\n",rev);}vTaskDelay(1000);}
}