1.任务通知
什么是任务通知,任务通知就是直接的通信,不需要中间量(通信对象)比如:队列,信号量,事件组...,可以让任务与任务,中断服务程序进行同步(注意:中断服务程序可以直接发给任务,但任务不可以直接发给中断服务程序)。
configUSE_TASK_NOTIFICATIONS被设置1时,每一个任务至少有一个"通知状态",可以是"待定"或"未定",以及一个32位无符号整数的"通知值"。当一个任务接收到通知时,其通知状态被设置为待定,当一个任务读取通知值时,其通知状态被设置为未定。
2.任务通知的使用
任务通知具有非常强大的功能,通常可以用来替代二进制信号量、计数信号量、事件组,有时甚至是代替队列。通过使用xTaskNotify() API 函数发送任务通知,以及使用xTaskNotifyWait() API 函数接收任务通知,可以适应广泛的使用场景。
然而,在大多数情况下,不需要xTaskNotify()和xTaskNotifyWait() API 函数提供的全部灵活性功能,更简单的函数就足够了。因此,提供了xTaskNotifyGive() API 函数作为xTaskNotify()函数的更简单但灵活性较小的替代函数,并提供了ulTaskNotifyTake() API 函数作为xTaskNotifyWait()函数的更简单但灵活性较小的替代函数。
任务通知系统不仅仅限于单个通知事件。configTASK_NOTIFICATION_ARRAY_ENTRIES的参数默认设置为1。如果将其设置为大于1的值,则会在每个任务内部创建一个通知数组。这允许通过索引来管理通知。每个任务通知的API函数都有一个带索引的版本。使用非索引版本将访问notification0(数组中的第一个)。每个API函数的indexed版本通过后缀Indexed进行标识,因此xTaskNotify()函数名变为xTaskNotifyIndexed。为了简单起见,教程中仅使用任务通知函数的非索引版本。
3.函数

cs
/* 由 main() 创建的两个任务的原型。 */
static void prvTask1( void *pvParameters );
static void prvTask2( void *pvParameters );
/* 由 main() 创建的任务的句柄。 */
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;
/* 创建两个任务,这两个任务相互发送通知,然后启动RTOS调度器。*/
void main( void )
{
xTaskCreate( prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1 );
xTaskCreate( prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2 );
vTaskStartScheduler();
}
/*-----------------------------------------------------------*/
/* prvTask1() 使用 'indexed' 版本的 API。*/
static void prvTask1( void *pvParameters )
{
for( ;; )
{
/* 向 prvTask2() 发送通知,使其从阻塞状态变为就绪状态。 */
xTaskNotifyGiveIndexed( xTask2, 0 );
/* 阻塞等待 prvTask2() 向此任务发送通知。*/
ulTaskNotifyTakeIndexed( 0, pdTRUE, portMAX_DELAY );
}
}
/*-----------------------------------------------------------*/
/* prvTask2() 使用 API 的原始版本(不带 'Indexed')。*/
static void prvTask2( void *pvParameters )
{
for( ;; )
{
/* 阻塞等待 prvTask1() 向此任务发送通知。 */
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
/* 向 prvTask1() 发送一个通知,将其从阻塞状态中转出。*/
xTaskNotifyGive( xTask1 );
}
}






4.实验1

CubeMX的配置:

代码:
cs
main.c
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern void ulExampleInterruptHandler(void);
/* USER CODE END PV */
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
UNUSED (GPIO_Pin);
if(HAL_GPIO_ReadPin (KEY4_GPIO_Port,KEY4_Pin)==GPIO_PIN_SET)
{
ulExampleInterruptHandler();
}
}
/* USER CODE END 4 */
cs
user_app.h
#ifndef __USER_APP_H__
#define __USER_APP_H__
#include "usart.h"
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os2.h"
#include "string.h"
#include "Timers.h"
#include "semphr.h"
#include <stdlib.h>
#include "event_groups.h"
void vHandlerTask(void* pvParameters);
void vPeriodicTask(void * pvParameters);
void ulExampleInterruptHandler(void);
void vprintfStringAndNumber(const char *pcString,uint32_t ulValue);
void vprintfString(const char *pcString);
#endif
cs
user_app.c
#include "user_app.h"
#include "queue.h"
extern osThreadId_t Task1Handle;
static const TickType_t xInterruptFrequency=pdMS_TO_TICKS (500);
void vprintfStringAndNumber(const char *pcString,uint32_t ulValue)
{
//临近保护
taskENTER_CRITICAL();
{
printf("%s=%d\n\r",pcString,ulValue);
}
taskEXIT_CRITICAL();
}
void vprintfString(const char *pcString)
{
taskENTER_CRITICAL();
{
printf("%s",pcString);
}
taskEXIT_CRITICAL();
}
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit (&huart1 ,(uint8_t*)&ch,1,0xffff);
return ch;
}
void vHandlerTask(void* pvParameters)
{
const TickType_t xMaxExpectedBlockTime=xInterruptFrequency +pdMS_TO_TICKS (10);
uint32_t ulEventsToProcess;
for(;;)
{
//收
ulEventsToProcess =ulTaskNotifyTake (pdTRUE,xMaxExpectedBlockTime );
if(ulEventsToProcess !=0)
{
while(ulEventsToProcess !=0)
{
vprintfString ("Handler task - processing event.\n\r");
ulEventsToProcess--;
}
}
else
{
/* 如果函数执行到这一部分,则表示在预期时间内没有接收到中断,并且(在真实的
应用中)可能需要执行一些错误恢复操作。 */
}
}
}
void vPeriodicTask(void * pvParameters)
{
const TickType_t Delay500ms=pdMS_TO_TICKS (500UL);
for(;;)
{
vTaskDelay (xInterruptFrequency );
vprintfString ("Periodic task - About to generate an interrupt\n\r");
__HAL_GPIO_EXTI_GENERATE_SWIT (KEY4_Pin); //软触发
vprintfString ("Periodic task _ Interruptgenerated.\n\r\n\r\n\r");
}
}
void ulExampleInterruptHandler(void)
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken =pdFALSE;
//给
vTaskNotifyGiveFromISR (Task1Handle , //正在发送通知的任务的句柄,这个句柄是在任务创建时保存的。
&xHigherPriorityTaskWoken //使用的方法与其他的相似
);
vTaskNotifyGiveFromISR (Task1Handle ,&xHigherPriorityTaskWoken );
vTaskNotifyGiveFromISR (Task1Handle ,&xHigherPriorityTaskWoken );
portYIELD_FROM_ISR (xHigherPriorityTaskWoken );
}
效果:
