STM32---FreeRTOS任务通知

一、简介

1、任务通知简介

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

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

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

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

不覆盖接受任务的通知值

覆盖接受任务的通知值

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

增加接受任务的通知值

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

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

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

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

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

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

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

2、任务通知状态:

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

三、实验

1、模拟二值信号量

main.c

cs 复制代码
#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

cs 复制代码
#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

cs 复制代码
#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;																				//定义变量,默认键码值为0
	
	if (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

cs 复制代码
#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

cs 复制代码
#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

cs 复制代码
#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、实验解析

相关推荐
charlie1145141915 分钟前
IMX6ULL学习整理篇——Linux驱动开发的基础3:向新框架迁移
linux·驱动开发·嵌入式硬件·学习·教程
IDIOT___IDIOT1 小时前
51单片机和STM32 入门分析
stm32·嵌入式硬件·51单片机
风起逸尘1 小时前
硬件驱动——51单片机:独立按键、中断、定时器/计数器
单片机·嵌入式硬件·51单片机
二年级程序员1 小时前
51单片机的寻址方式(完整)
单片机·嵌入式硬件·51单片机
蓝桥_吹雪1 小时前
【备赛】遇到的小问题-1
笔记·stm32·单片机
长安第一美人2 小时前
ModBus TCP/RTU互转(主)(从)|| Modbus主动轮询下发的工业应用 || 基于智能网关的串口服务器进行Modbus数据收发的工业应用
网络·嵌入式硬件·网络协议·嵌入式·modbus数据收发
小雀丝6 小时前
电机控制常见面试问题(十五)
单片机·嵌入式硬件·电机·电机控制
czhaii13 小时前
STM32配套程序接线图
stm32·单片机·嵌入式硬件
F1331689295714 小时前
WD5202L超低成本 Buck 电源芯片的特性与应用电路解析, 将市电转换为 5V 电压
单片机·嵌入式硬件·物联网