FreeRTOS复习笔记(六) —— 任务通知

FreeRTOS复习笔记(六) —— 任务通知

1.本例程思路:

创建两个任务,一个任务用于发送通知,另一个任务用于接收通知,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时发送通知,接收任务接收到通知消息后打印输出

任务通知无需创建,直接调用函数 xTaskNotify 发送消息,xTaskNotifyWait 用于接收消息

2.代码编写

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替消息队列
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t rev1;

	BaseType_t xReturn = pdFALSE;

	for (;;)
	{
		/* 清除位置1, 否则清0 */
		/* 等待通知, 进入时不清除位 退出时清除位 接收数据 等待超时时间 */
		xReturn = xTaskNotifyWait(0, ULONG_MAX, &rev1, portMAX_DELAY);

		if (xReturn == pdTRUE)
			printf("obtained, %x.\r\n", rev1);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;
	uint8_t _switch    = 0;

	uint32_t data1 = 0x404;
	uint32_t data2 = 0x5a5;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{

			if (_switch == 0)
			{
				_switch = 1;
				
				/**
					eNoAction,				     通知任务, 不更新通知值
					eSetBits,					 设置通知值中的位                 --- 事件组
					eIncrement,					 增加通知值                       --- 信号量
					eSetValueWithOverwrite,		 直接更新通知值                   --- 消息队列
					eSetValueWithoutOverwrite	 若前一个通知值被读取才更新通知值 --- 消息队列
				*/
				/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
				xReturn = xTaskNotify(LEDTask_Handle, data1, eSetValueWithOverwrite);
				
				if (xReturn == pdTRUE)
					printf("data1 sent successful.\r\n");
			}
			else
			{
				if (_switch == 1)
				{
					_switch = 0;

					/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
					xReturn = xTaskNotify(LEDTask_Handle, data2, eSetValueWithOverwrite);
					
					if (xReturn == pdTRUE)
						printf("data2 sent successful.\r\n");
				}
			}
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

上述例程为将任务通知代替消息队列使用,还可以使用其代替信号量、事件组。代替信号量时可以直接调用函数 xTaskNotifyGive 释放信号量,使用 ulTaskNotifyTake 获取信号量

本例程思路(代替二值信号量):

创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时释放信号量,接收任务接收到信号量后打印输出

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替二值信号量
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t NumCount = 0;
	
	for (;;)
	{
		/* pdTRUE: 退出清零, pdFALSE: 退出减一 */
		/* 等待通知, 退出清零 等待超时时间 */
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

		printf("obtained, %d times.\r\n", ++NumCount);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;

	uint32_t NumCount = 0;
	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			NumCount++;
		
			/* 任务通知, 任务句柄 */
			xReturn = xTaskNotifyGive(LEDTask_Handle);
			
			if (xReturn == pdTRUE)
				printf("notification has been sent, %d times.\r\n", NumCount);
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

本例程思路(代替计数信号量):

创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现一次下降沿时释放一个信号量,接收任务只要检测到存在信号量就一直获取

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替计数信号量
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t xReturn = 0;

	for (;;)
	{
		/* pdFALSE: 退出减一, pdTRUE: 退出清零 */
		/* 等待通知, 退出减一 等待超时时间 */
		xReturn = ulTaskNotifyTake(pdFALSE, 0);

		printf("there are %d values.\r\n", xReturn);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(3000);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			/* 任务通知, 任务句柄 */
			xReturn = xTaskNotifyGive(LEDTask_Handle);
			
			if (xReturn == pdTRUE)
				printf("released.\r\n");
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

可以使用任务通知代替事件组,使用 xTaskNotify 标记事件,调用 xTaskNotifyWait 获取事件

本例程思路(代替事件组):

创建两个任务,一个任务用于标记事件,另一个任务用于获取事件,当发送任务中检测到KEY_GPIO(PA0)出现第一次下降沿时标记事件1,第二个下降沿标记事件2,第三个下降沿清除所有事件标记,接收任务检测到事件后打印提示,但不清除事件位(可以通过设置清除)

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替事件组
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t _event = 0;
	
	for (;;)
	{
		/* 等待通知, 进入是否清除 退出是否清除 事件 等待超时时间 */
		xTaskNotifyWait(0, 0, &_event, portMAX_DELAY);

		if (_event == 0)
			printf("obtained, default.\r\n");

		if (_event & 0x0001)
			printf("obtained, _event1.\r\n");
			
		if (_event & 0x0002)
			printf("obtained, _event2.\r\n");
		
		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;
	uint8_t _switch    = 0;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			if (_switch == 0)
			{
				_switch = 1;
			
				/* 任务通知, 任务句柄 0x0001 置位 */
				xReturn = xTaskNotify(LEDTask_Handle, 0x0001, eSetBits);
				
				if (xReturn == pdTRUE)
					printf("set1 successful.\r\n");
			}
			else if (_switch == 1)
			{
				_switch = 2;
				
				/* 任务通知, 任务句柄 0x0002 置位 */
				xReturn = xTaskNotify(LEDTask_Handle, 0x0002, eSetBits);
				
				if (xReturn == pdTRUE)
					printf("set2 successful.\r\n");
			}
			else
			{
				if(_switch == 2)
				{
					_switch = 0;
					
					/* 任务通知, 任务句柄 0 覆盖 */
					xReturn = xTaskNotify(LEDTask_Handle, 0, eSetValueWithOverwrite);
					
					if (xReturn == pdTRUE)
						printf("set defualt.\r\n");
				}
			}
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

3.使用串口观察输出信息

代替消息队列
SSS006.001
代替二值信号量(一个释放信号量,一个获取信号量,此处计数代表进行释放/获取的次数)
SSS006.002
代替计数信号量(一个任务释放信号量,一个任务获取信号量)
SSS006.003
代替事件组(获取到对应事件后打印提示信息,因第二次获取到事件时并未清除第一次获取的事件位,所以会同时打印出 _event1 和 _event2)
SSS006.004
经验证,程序运行结果与预想一致

版权声明:本文为CSDN博主「SSS&10&01」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45691873/article/details/122201629

FreeRTOS复习笔记(六) —— 任务通知

1.本例程思路:

创建两个任务,一个任务用于发送通知,另一个任务用于接收通知,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时发送通知,接收任务接收到通知消息后打印输出

任务通知无需创建,直接调用函数 xTaskNotify 发送消息,xTaskNotifyWait 用于接收消息

2.代码编写

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替消息队列
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t rev1;

	BaseType_t xReturn = pdFALSE;

	for (;;)
	{
		/* 清除位置1, 否则清0 */
		/* 等待通知, 进入时不清除位 退出时清除位 接收数据 等待超时时间 */
		xReturn = xTaskNotifyWait(0, ULONG_MAX, &rev1, portMAX_DELAY);

		if (xReturn == pdTRUE)
			printf("obtained, %x.\r\n", rev1);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;
	uint8_t _switch    = 0;

	uint32_t data1 = 0x404;
	uint32_t data2 = 0x5a5;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{

			if (_switch == 0)
			{
				_switch = 1;
				
				/**
					eNoAction,				     通知任务, 不更新通知值
					eSetBits,					 设置通知值中的位                 --- 事件组
					eIncrement,					 增加通知值                       --- 信号量
					eSetValueWithOverwrite,		 直接更新通知值                   --- 消息队列
					eSetValueWithoutOverwrite	 若前一个通知值被读取才更新通知值 --- 消息队列
				*/
				/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
				xReturn = xTaskNotify(LEDTask_Handle, data1, eSetValueWithOverwrite);
				
				if (xReturn == pdTRUE)
					printf("data1 sent successful.\r\n");
			}
			else
			{
				if (_switch == 1)
				{
					_switch = 0;

					/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
					xReturn = xTaskNotify(LEDTask_Handle, data2, eSetValueWithOverwrite);
					
					if (xReturn == pdTRUE)
						printf("data2 sent successful.\r\n");
				}
			}
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

上述例程为将任务通知代替消息队列使用,还可以使用其代替信号量、事件组。代替信号量时可以直接调用函数 xTaskNotifyGive 释放信号量,使用 ulTaskNotifyTake 获取信号量

本例程思路(代替二值信号量):

创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时释放信号量,接收任务接收到信号量后打印输出

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替二值信号量
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t NumCount = 0;
	
	for (;;)
	{
		/* pdTRUE: 退出清零, pdFALSE: 退出减一 */
		/* 等待通知, 退出清零 等待超时时间 */
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

		printf("obtained, %d times.\r\n", ++NumCount);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;

	uint32_t NumCount = 0;
	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			NumCount++;
		
			/* 任务通知, 任务句柄 */
			xReturn = xTaskNotifyGive(LEDTask_Handle);
			
			if (xReturn == pdTRUE)
				printf("notification has been sent, %d times.\r\n", NumCount);
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

本例程思路(代替计数信号量):

创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现一次下降沿时释放一个信号量,接收任务只要检测到存在信号量就一直获取

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替计数信号量
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t xReturn = 0;

	for (;;)
	{
		/* pdFALSE: 退出减一, pdTRUE: 退出清零 */
		/* 等待通知, 退出减一 等待超时时间 */
		xReturn = ulTaskNotifyTake(pdFALSE, 0);

		printf("there are %d values.\r\n", xReturn);

		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(3000);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			/* 任务通知, 任务句柄 */
			xReturn = xTaskNotifyGive(LEDTask_Handle);
			
			if (xReturn == pdTRUE)
				printf("released.\r\n");
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

可以使用任务通知代替事件组,使用 xTaskNotify 标记事件,调用 xTaskNotifyWait 获取事件

本例程思路(代替事件组):

创建两个任务,一个任务用于标记事件,另一个任务用于获取事件,当发送任务中检测到KEY_GPIO(PA0)出现第一次下降沿时标记事件1,第二个下降沿标记事件2,第三个下降沿清除所有事件标记,接收任务检测到事件后打印提示,但不清除事件位(可以通过设置清除)

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.27
  * @简  介: 任务通知代替事件组
  * @注  意: 无
  ******************************************************************************
  */
#include "stm32f10x.h"

#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"

void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);

/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
int main(void)
{
	/* --------- --------- --------- --------- --------- --------- ---------  */
	
	/* 中断分组4 */
	NVIC_Priority_Group_Config();                                        /* * */
	
	/* 串口1配置 */
	USART1_Config();                                                     /* * */
	
	/* LED(PA8, PA6)配置 */
	LED_GPIO_Config();                                                   /* * */
	
	/* KEY(PA0)配置 */
	KEY_GPIO_Config();
	
	/* --------- --------- --------- --------- --------- --------- ---------  */

	/* 创建任务 */
	xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
	
	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界区 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
	
	xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void LEDTask(void *pvParameters)
{
	uint32_t _event = 0;
	
	for (;;)
	{
		/* 等待通知, 进入是否清除 退出是否清除 事件 等待超时时间 */
		xTaskNotifyWait(0, 0, &_event, portMAX_DELAY);

		if (_event == 0)
			printf("obtained, default.\r\n");

		if (_event & 0x0001)
			printf("obtained, _event1.\r\n");
			
		if (_event & 0x0002)
			printf("obtained, _event2.\r\n");
		
		GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");

		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void KEYTask(void *pvParameters)
{
	uint8_t level_high = 0;
	uint8_t level_low  = 0;
	uint8_t edge_fall  = 0;
	uint8_t edge_rise  = 0;
	uint8_t _switch    = 0;

	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
		/* 高电平/上升沿检测 */
		if ( (GPIOA->IDR & 0x0001) != 0 )
		{
			if (level_low == 1)   edge_rise = 1;
			
			level_low  = 0;      level_high = 1;
		}
		
		/* 低电平/下降沿检测 */
		if ( (GPIOA->IDR & 0x0001) == 0 )
		{
			if (level_high == 1) edge_fall  = 1;
			
			level_high = 0;      level_low  = 1;
		}

		if (edge_fall) /* 出现下降沿 */
		{
			if (_switch == 0)
			{
				_switch = 1;
			
				/* 任务通知, 任务句柄 0x0001 置位 */
				xReturn = xTaskNotify(LEDTask_Handle, 0x0001, eSetBits);
				
				if (xReturn == pdTRUE)
					printf("set1 successful.\r\n");
			}
			else if (_switch == 1)
			{
				_switch = 2;
				
				/* 任务通知, 任务句柄 0x0002 置位 */
				xReturn = xTaskNotify(LEDTask_Handle, 0x0002, eSetBits);
				
				if (xReturn == pdTRUE)
					printf("set2 successful.\r\n");
			}
			else
			{
				if(_switch == 2)
				{
					_switch = 0;
					
					/* 任务通知, 任务句柄 0 覆盖 */
					xReturn = xTaskNotify(LEDTask_Handle, 0, eSetValueWithOverwrite);
					
					if (xReturn == pdTRUE)
						printf("set defualt.\r\n");
				}
			}
		}
		
		edge_fall = 0;
		edge_rise = 0;
		(void)edge_rise;

		vTaskDelay(20);
	}
}

3.使用串口观察输出信息

代替消息队列
SSS006.001
代替二值信号量(一个释放信号量,一个获取信号量,此处计数代表进行释放/获取的次数)
SSS006.002
代替计数信号量(一个任务释放信号量,一个任务获取信号量)
SSS006.003
代替事件组(获取到对应事件后打印提示信息,因第二次获取到事件时并未清除第一次获取的事件位,所以会同时打印出 _event1 和 _event2)
SSS006.004
经验证,程序运行结果与预想一致

版权声明:本文为CSDN博主「SSS&10&01」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45691873/article/details/122201629

生成海报
点赞 0

SSS&10&01

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

freertos临界段保护

中断的基础知识 嵌套: 嵌套向量中断控制器 NVIC(Nested Vectored Interrupt Controller与内核是紧耦合的。提供如下的功能:可嵌套中断支持、向量中断支持、动态优先级调整支持、中