STM32---FreeRTOS任务通知

news/2025/3/19 16:19:33/

一、简介

1、任务通知简介

任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。

使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!

使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知" 

 2、任务通知值的更新方式

                不覆盖接受任务的通知值

                覆盖接受任务的通知值

                更新接受任务通知值的一个或多个bit

                增加接受任务的通知值

只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!

3、任务通知的优势和劣势

4、任务通知值和通知状态 

任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量:

         一个是 uint32_t 类型:用来表示通知值

        一个是 uint8_t 类型:用来表示通知状态

1、任务通知值的更新方式有多种类型:

2、任务通知状态: 

二、任务通知相关API函数介绍 

 三、实验 

1、模拟二值信号量

 main.c

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_demo.h"
#include "Delay.h"
#include "sys.h"
#include "usart.h"
#include "LED.h"
#include "Key.h"int main(void){	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4 uart_init(115200);	 delay_init();Key_Init();LED_Init();// 创建任务FrrrRTOS_Demo();}

freertos_demo.c

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO					1
//任务堆栈大小	
#define START_TASK_STACK_SIZE 	128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_PRIO							2
//任务堆栈大小	
#define TASK1_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);//任务优先级
#define TASK2_PRIO							3
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);/******************************************************************任务函数****************************************************/void FrrrRTOS_Demo(void)
{//创建开始任务xTaskCreate((TaskFunction_t )start_task,            			//任务函数( char*         )"start_task",          			//任务名称(uint16_t       )START_TASK_STACK_SIZE, 			//任务堆栈大小(void*          )NULL,                  			//传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       			//任务优先级(TaskHandle_t*  )&StartTask_Handler);   			//任务句柄 // 启动任务调度vTaskStartScheduler();}void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建1任务xTaskCreate((TaskFunction_t )task1,     	(const char*    )"task1",   	(uint16_t       )TASK1_STACK_SIZE, (void*          )NULL,				(UBaseType_t    )TASK1_PRIO,	(TaskHandle_t*  )&Task1_Handler); //创建2任务xTaskCreate((TaskFunction_t )task2,     (const char*    )"task2",   (uint16_t       )TASK2_STACK_SIZE, (void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2_Handler);    vTaskDelete(NULL); 							//删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//1 发送任务通知值
void task1(void *pvParameters)
{uint8_t				key = 0;while(1){key = Key_GetNum();if(key == 2){printf("任务通知模拟二值信号释放\r\n");xTaskNotifyGive(Task2_Handler);}vTaskDelay(10);}
}// 任务2 接受任务通知值
void task2(void *pvParameters)
{uint32_t				rev = 0;// 任务主循环while (1){rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);if(rev != 0){printf("接受任务通知成功,模拟获取二值信号量\r\n");}}
}

key.c

#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "Delay.h"/*** 函    数:按键初始化* 参    数:无* 返 回 值:无* 按键:PB4/PB12/PB14*/
void Key_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟/*GPIO初始化*/GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_4 | GPIO_Pin_12 | GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);						
}/*** 函    数:按键获取键码* 参    数:无* 返 回 值:按下按键的键码值,范围:0~3,返回0代表没有按键按下* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手*/
uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0;																				//定义变量,默认键码值为0if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0)			  //读PB4输入寄存器的状态,如果为0,则代表按键1按下{KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);delay_xms(20);																					//延时消抖while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0);	//等待按键松手delay_xms(20);																					//延时消抖KeyNum = 1;																							//置键码为1}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0)			{KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);delay_xms(20);											while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0);	delay_xms(20);									KeyNum = 2;											}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)			{KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);delay_xms(20);											while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0);	delay_xms(20);									KeyNum = 3;											}return KeyNum;																						//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}

1、实验解析 

按下按键,模拟二值信号量的释放和接受;

2、模拟计算型信号量

freertos_demo.c

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO					1
//任务堆栈大小	
#define START_TASK_STACK_SIZE 	128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_PRIO							2
//任务堆栈大小	
#define TASK1_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);//任务优先级
#define TASK2_PRIO							3
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);/******************************************************************任务函数****************************************************/void FrrrRTOS_Demo(void)
{//创建开始任务xTaskCreate((TaskFunction_t )start_task,            			//任务函数( char*         )"start_task",          			//任务名称(uint16_t       )START_TASK_STACK_SIZE, 			//任务堆栈大小(void*          )NULL,                  			//传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       			//任务优先级(TaskHandle_t*  )&StartTask_Handler);   			//任务句柄 // 启动任务调度vTaskStartScheduler();}void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建1任务xTaskCreate((TaskFunction_t )task1,     	(const char*    )"task1",   	(uint16_t       )TASK1_STACK_SIZE, (void*          )NULL,				(UBaseType_t    )TASK1_PRIO,	(TaskHandle_t*  )&Task1_Handler); //创建2任务xTaskCreate((TaskFunction_t )task2,     (const char*    )"task2",   (uint16_t       )TASK2_STACK_SIZE, (void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2_Handler);    vTaskDelete(NULL); 							//删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//1 发送任务通知值
void task1(void *pvParameters)
{uint8_t				key = 0;while(1){key = Key_GetNum();if(key == 2){printf("任务通知模拟二值信号释放\r\n");xTaskNotifyGive(Task2_Handler);}vTaskDelay(10);}
}// 任务2 接受任务通知值
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);}
}

1、实验解析 

快速点击按键,模拟计数型信号量的接受和释放;

3、模拟信号邮箱实验 

freertos_demo.c 

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO					1
//任务堆栈大小	
#define START_TASK_STACK_SIZE 	128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_PRIO							2
//任务堆栈大小	
#define TASK1_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);//任务优先级
#define TASK2_PRIO							3
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);/******************************************************************任务函数****************************************************/void FrrrRTOS_Demo(void)
{//创建开始任务xTaskCreate((TaskFunction_t )start_task,            			//任务函数( char*         )"start_task",          			//任务名称(uint16_t       )START_TASK_STACK_SIZE, 			//任务堆栈大小(void*          )NULL,                  			//传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       			//任务优先级(TaskHandle_t*  )&StartTask_Handler);   			//任务句柄 // 启动任务调度vTaskStartScheduler();}void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建1任务xTaskCreate((TaskFunction_t )task1,     	(const char*    )"task1",   	(uint16_t       )TASK1_STACK_SIZE, (void*          )NULL,				(UBaseType_t    )TASK1_PRIO,	(TaskHandle_t*  )&Task1_Handler); //创建2任务xTaskCreate((TaskFunction_t )task2,     (const char*    )"task2",   (uint16_t       )TASK2_STACK_SIZE, (void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2_Handler);    vTaskDelete(NULL); 							//删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//1 发送任务通知值
void task1(void *pvParameters)
{uint8_t				key = 0;while(1){key = Key_GetNum();if((key != 0) && (Task2_Handler != NULL)){printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",key);xTaskNotify( Task2_Handler, key, eSetValueWithOverwrite );// 发送任务通知给任务2,使用 eSetValueWithOverwrite 模式// Task2_Handler: 任务2的句柄// key: 发送的值// eSetValueWithOverwrite: 覆盖模式,新的值会覆盖旧的值}vTaskDelay(10);}
}// 任务2 接受任务通知值
void task2(void *pvParameters)
{uint32_t				rev = 0;// 任务主循环while (1){xTaskNotifyWait(0,0xFFFFFFFF,&rev,portMAX_DELAY );// 等待任务通知,portMAX_DELAY 表示无限等待// 0: 不清除任何通知位// 0xFFFFFFFF: 接收所有通知值// &rev: 存储接收到的通知值// portMAX_DELAY: 无限等待,直到收到通知switch(rev){case 2:{printf("接收到的任务通知值为:%d\r\n",rev);LED1_Turn();break;}case 3:{printf("接收到的任务通知值为:%d\r\n",rev);LED2_Turn();break;}};}
}

1、实验解析

 4、模拟时间标志组

freertosc_demo.c 

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO					1
//任务堆栈大小	
#define START_TASK_STACK_SIZE 	128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_PRIO							2
//任务堆栈大小	
#define TASK1_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);//任务优先级
#define TASK2_PRIO							3
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);#define		EVENTBIT0 	(1<<0)
#define		EVENTBIT1 	(1<<1)/******************************************************************任务函数****************************************************/void FrrrRTOS_Demo(void)
{//创建开始任务xTaskCreate((TaskFunction_t )start_task,            			//任务函数( char*         )"start_task",          			//任务名称(uint16_t       )START_TASK_STACK_SIZE, 			//任务堆栈大小(void*          )NULL,                  			//传递给任务函数的参数(UBaseType_t    )START_TASK_PRIO,       			//任务优先级(TaskHandle_t*  )&StartTask_Handler);   			//任务句柄 // 启动任务调度vTaskStartScheduler();}void start_task(void *pvParameters)
{taskENTER_CRITICAL();           //进入临界区//创建1任务xTaskCreate((TaskFunction_t )task1,     	(const char*    )"task1",   	(uint16_t       )TASK1_STACK_SIZE, (void*          )NULL,				(UBaseType_t    )TASK1_PRIO,	(TaskHandle_t*  )&Task1_Handler); //创建2任务xTaskCreate((TaskFunction_t )task2,     (const char*    )"task2",   (uint16_t       )TASK2_STACK_SIZE, (void*          )NULL,(UBaseType_t    )TASK2_PRIO,(TaskHandle_t*  )&Task2_Handler);    vTaskDelete(NULL); 							//删除开始任务taskEXIT_CRITICAL();            //退出临界区
}//1 发送任务通知值
void task1(void *pvParameters)
{uint8_t				key = 0;while(1){key = Key_GetNum();if(key ==2){printf("更新bit0位,将其置1\r\n");xTaskNotify(Task2_Handler,EVENTBIT0,eSetBits);}else if(key ==3){printf("更新bit1位,将其置1\r\n");xTaskNotify(Task2_Handler,EVENTBIT1,eSetBits);		}vTaskDelay(10);}
}// 任务2 接受任务通知值
void task2(void *pvParameters)
{uint32_t				rev = 0;uint32_t				bit = 0;// 任务主循环while (1){xTaskNotifyWait(0,0xFFFFFFFF,&rev,portMAX_DELAY );// 等待任务通知,portMAX_DELAY 表示无限等待// 0: 不清除任何通知位// 0xFFFFFFFF: 接收所有通知值// &rev: 存储接收到的通知值// portMAX_DELAY: 无限等待,直到收到通知if(rev&EVENTBIT0){bit|= EVENTBIT0;}if(rev&EVENTBIT1){bit|= EVENTBIT1;}if(bit == (EVENTBIT0|EVENTBIT1)){printf("任务通知模拟事件标志组接收成功\r\n");bit = 0;}			}
}

1、实验解析


http://www.ppmy.cn/news/1580357.html

相关文章

python 提取视频中的音频

在Python中提取视频中的音频&#xff0c;你可以使用moviepy库&#xff0c;这是一个非常强大且易于使用的库&#xff0c;专门用于视频编辑。以下是如何使用moviepy来提取视频中的音频的步骤&#xff1a; 安装moviepy 首先&#xff0c;你需要安装moviepy。你可以通过pip安装它&a…

【数据库】掌握MySQL事务与锁机制-数据一致性的关键

在数据库的世界里&#xff0c;数据就是一切。而确保数据的准确性和一致性&#xff0c;则是数据库系统的核心任务之一。想象一下&#xff0c;如果没有合适的机制&#xff0c;当多个用户同时试图修改同一条数据时&#xff0c;会发生什么&#xff1f; chaos&#xff08;混乱&#…

批量压缩与优化 Excel 文档,减少 Excel 文档大小

当我们在 Excel 文档中插入图片资源的时候&#xff0c;如果我们插入的是原图&#xff0c;可能会导致 Excel 变得非常的大。这非常不利于我们传输或者共享。那么当我们的 Excel 文件非常大的时候&#xff0c;我们就需要对文档做一些压缩或者优化的处理。那有没有什么方法可以实现…

【数据分享】2000—2024年我国省市县三级逐年归一化植被指数(NDVI)数据(年最大值/Shp/Excel格式)

之前我们分享过2000-2024年我国逐年的归一化植被指数&#xff08;NDVI&#xff09;栅格数据&#xff0c;该逐年数据是取的当年月归一化植被指数&#xff08;NDVI&#xff09;的年最大值。&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;该数据来源于NASA定期发布…

Android主流架构模式对比分析

Android主流架构模式对比分析 一、引言 在Android应用开发中&#xff0c;选择合适的架构模式对于提高代码质量、可维护性和可测试性至关重要。本文将深入分析MVC、MVP和MVVM三种主流架构模式的原理、优缺点及实践应用。 二、MVC架构模式 2.1 MVC基本概念 MVC&#xff08;M…

Ruby 字符串(String)

Ruby 字符串&#xff08;String&#xff09; 在Ruby编程语言中&#xff0c;字符串&#xff08;String&#xff09;是处理文本数据的基本数据类型。它是一种可变的字符序列&#xff0c;由一个或多个字符组成。在Ruby中&#xff0c;字符串被广泛用于数据存储、格式化和文本处理。…

Qt 中工具窗体与普通窗体在任务栏中的区别

文章目录 Qt 中工具窗体与普通窗体在任务栏中的区别1. 什么是工具窗体和普通窗体&#xff1f;**普通窗体&#xff08;Main Window&#xff09;****工具窗体&#xff08;Tool Window&#xff09;**总结&#xff1a; 2. 工具窗体与普通窗体在任务栏的区别**普通窗体&#xff08;M…

基于python+django+mysql的小区物业管理系统源码+运行步骤

该系统是基于pythondjango开发的小区物业管理系统。适用场景&#xff1a;大学生、课程作业、毕业设计。学习过程中&#xff0c;如遇问题可以在github给作者留言。主要功能有&#xff1a;业主管理、报修管理、停车管理、资产管理、小区管理、用户管理、日志管理、系统信息。源码…