FreeRTOS任务通知

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 );
}

效果:

相关推荐
qq_2518364571 小时前
AI 疾病自查功能SpringbootAI项目实战 · 技术实现文档
java·ai·ai编程
逆境不可逃2 小时前
Pi Agent 学习笔记:对话太长以后,如何压缩上下文
java
MetaLite2 小时前
JDK 8~26 核心特性一览:从 Stream 到 Scoped Values
java
wno7042 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc2 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
君顾12 小时前
上海24小时自助健身房系统开发实战指南:从架构设计到落地部署
java·开发语言·健身房
计算机魔术师3 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
SL_staff4 小时前
别急着买商业规则引擎:90%风控场景根本不需要全量编码能力
java·程序员·groovy
智恒百亿4 小时前
RTX 5090 八卡服务器 vs RTX PRO 6000 整机:AI 推理、微调与渲染选型部署指南
运维·服务器·rtx5090
ManageEngineITSM4 小时前
DevOps和ITIL是什么关系?是替代还是互补一文讲清
java·服务器·资产管理·变更管理