目录
任务通知的简介
任务通知值的更新方式
任务通知的优势
任务通知的劣势
任务通知值和通知状态
发送通知相关API函数
接收通知相关API函数
任务通知模拟信号量实验
任务通知模拟消息邮箱实验
任务通知模拟事件标志组实验
任务通知的简介
任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。
使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!
使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"
任务通知值的更新方式
不覆盖接受任务的通知值、覆盖接受任务的通知值、更新接受任务通知值的一个或多个bit、增加接受任务的通知值。
只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!
任务通知的优势
效率更高:使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
使用内存更小:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体
任务通知的劣势
无法发送数据给ISR:ISR没有任务结构体,所以无法给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
无法广播给多个任务:任务通知只能是被指定的一个任务接收并处理
无法缓存多个数据:任务通知是通过更新任务通知值来发送数据的,任务结构休中只有一个任务通知值,只能保持一个数据。
发送受阻不支持阻塞:发送方无法进入阻塞状态等待
任务通知值和通知状态
任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量:
一个是 uint32_t类型,用来表示通知值
一个是 uint8_t 类型,用来表示通知状态
任务通知值
任务通知值的更新方式有多种类型:
计数值(数值累加,类似信号量)
相应位置一(类似事件标记组)
任意数值(支持覆写和不覆写,类似队列)
任务通知状态
其中任务通知状态共有3种取值:
#definetaskNOT_WAITING_NOTIFICATION ((uint8_t) 0) /* 任务未等待通知*/
#definetaskWAITING_NOTIFICATION ((uint8_t) 1) /* 任务在等待通知*/
#definetaskNOTIFICATION_RECEIVED ((uint8_t) 2) /* 任务在等待接收*/
任务未等待通知:任务通知默认的初始化状态
等待通知:接收方已经准备好了(调用了接收任务通知函数) ,等待发送方给个通知
等待接收:发送方已经发送出去(调用了发送任务通知函数) ,等待接收方接收
发送通知相关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),
elncrement,
NULL)
任务通知方式共有以下几种:
接收通知相关API函数
当任务通知用作于信号量时,使用函数获取信号量: 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)
任务通知模拟信号量实验
任务通知功能模拟二值信号量和计数型信号量将设计三个任务: start_task、 task1、task2
start_task:用来创建task1和task2任务
task1:用于按键扫描,当检测到按键KEYO被按下时,将发送任务通知
task2:用于接收任务通知,并打印相关提示信息
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "user_key.h"
#include "queue.h"
#include "event_groups.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 4
//任务堆栈大小
#define TASK1_STK_SIZE 100
//任务句柄
TaskHandle_t TASK1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STK_SIZE 100
//任务句柄
TaskHandle_t TASK2_Handler;
//任务函数
void task2(void *pvParameters);
int main(void)
{
/*配置系统中断分组为4位抢占*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*创建开始任务*/
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(); //开启任务调度
}
/*!
\brief 开始任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建高优先级任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&TASK1_Handler);
//创建中优先级任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&TASK2_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/*!
\brief 任务1发送任务通知
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_Scan(0);
if(key == KEY0_PRES)
{
taskENTER_CRITICAL(); //进入临界区
//printf("任务通知模拟二值信号量释放\r\n\r\n");
printf("任务通知模拟计数型信号量释放\r\n\r\n");
taskEXIT_CRITICAL(); //退出临界区
/*发送任务通知给任务2信号量*/
xTaskNotifyGive(TASK2_Handler);
}
vTaskDelay(10);
}
}
/*!
\brief 任务2接收通知并打印
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task2(void *pvParameters)
{
uint32_t Value = 0;
while(1)
{ /*接受信号量,pdTRUE接收成功后通知值清零(二值)pdFALSE(计数型),死等*/
Value = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
if(Value != 0)
{
// taskENTER_CRITICAL(); //进入临界区
// printf("任务通知模拟二值信号量接受成功\r\n\r\n");
// taskEXIT_CRITICAL(); //退出临界区
taskENTER_CRITICAL(); //进入临界区
printf("任务通知模拟计数信号量接受成功值为%d\r\n\r\n",Value);
taskEXIT_CRITICAL(); //退出临界区
}
vTaskDelay(1000);
}
}
/************************************************************** END OF FILE ****/
任务通知模拟消息邮箱实验
任务通知功能模拟模拟消息邮箱将设计三个任务: start_task、 task1、task2
start_task:用来创建task1和task2任务
task1:用于按键扫描,将按下的按键键值通过任务通知发送给指定任务
task2:用于接收任务通知,并根据接收到的数据做相应动作
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "user_key.h"
#include "queue.h"
#include "event_groups.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 4
//任务堆栈大小
#define TASK1_STK_SIZE 100
//任务句柄
TaskHandle_t TASK1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STK_SIZE 100
//任务句柄
TaskHandle_t TASK2_Handler;
//任务函数
void task2(void *pvParameters);
int main(void)
{
/*配置系统中断分组为4位抢占*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*创建开始任务*/
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(); //开启任务调度
}
/*!
\brief 开始任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建高优先级任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&TASK1_Handler);
//创建中优先级任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&TASK2_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/*!
\brief 任务1发送任务通知
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_Scan(0);
if((key != 0) && (TASK2_Handler != NULL))
{
taskENTER_CRITICAL(); //进入临界区
printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n\r\n",key);
taskEXIT_CRITICAL(); //退出临界区
/*发送任务通知给任务2消息邮箱,按键值,覆写方式*/
xTaskNotify(TASK2_Handler,key,eSetValueWithoutOverwrite);
}
vTaskDelay(10);
}
}
/*!
\brief 任务2接收通知并打印
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task2(void *pvParameters)
{
uint32_t Value = 0;
while(1)
{
/*接受任务通知,初始化时不清0,接受成功后全部清零,死等方式*/
xTaskNotifyWait(0,0xFFFFFFFF,&Value,portMAX_DELAY);
if(Value != 0)
{
taskENTER_CRITICAL(); //进入临界区
printf("任务通知模拟消息邮箱接受成功值为%d\r\n\r\n",Value);
taskEXIT_CRITICAL(); //退出临界区
}
vTaskDelay(1000);
}
}
/************************************************************** END OF FILE ****/
任务通知模拟事件标志组实验
任务通知功能模拟模拟事件标志组将设计三个任务: start_task、 task1、task2
start_task:用来创建task1和task2任务
task1:用于按键扫描,当检测到按键按下时,发送任务通知设置不同标志位
task2:用于接收任务通知,并打印相关提示信息
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "user_key.h"
#include "queue.h"
#include "event_groups.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 4
//任务堆栈大小
#define TASK1_STK_SIZE 100
//任务句柄
TaskHandle_t TASK1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STK_SIZE 100
//任务句柄
TaskHandle_t TASK2_Handler;
//任务函数
void task2(void *pvParameters);
int main(void)
{
/*配置系统中断分组为4位抢占*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*创建开始任务*/
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(); //开启任务调度
}
/*!
\brief 开始任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建高优先级任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&TASK1_Handler);
//创建中优先级任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&TASK2_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/*!
\brief 任务1发送任务通知
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_Scan(0);
if(key == KEY0_PRES)
{
taskENTER_CRITICAL(); //进入临界区
printf("任务通知模拟事件更新bit0位\r\n\r\n");
taskEXIT_CRITICAL(); //退出临界区
/*发送任务通知给任务2事件标志组bit0置一*/
xTaskNotify(TASK2_Handler,0x01,eSetBits);
}else if(key == KEY1_PRES)
{
taskENTER_CRITICAL(); //进入临界区
printf("任务通知模拟事件更新bit1位\r\n\r\n");
taskEXIT_CRITICAL(); //退出临界区
/*发送任务通知给任务2事件标志组bit1置一*/
xTaskNotify(TASK2_Handler,0x02,eSetBits);
}
vTaskDelay(10);
}
}
/*!
\brief 任务2接收通知并打印
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void task2(void *pvParameters)
{
uint32_t Value = 0;
uint32_t Event = 0;
while(1)
{
/*接受任务通知,初始化时不清0,接受成功后全部清零,死等方式*/
xTaskNotifyWait(0,0xFFFFFFFF,&Value,portMAX_DELAY);
if(Value & 0x01)
{
Event |= 0x01;
}
if(Value & 0x02)
{
Event |= 0x02;
}
if(Event == 0x03)
{
Event = 0;
taskENTER_CRITICAL(); //进入临界区
printf("按键0和按键1按下打印提示语句\r\n\r\n");
taskEXIT_CRITICAL(); //退出临界区
}
vTaskDelay(1000);
}
}
/************************************************************** END OF FILE ****/