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)
{
//接收前通知值不清零,接收后通知值全部清零,通知值给到rev
xTaskNotifyWait(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);
}
}