FreeRTOS复习笔记(四) —— 信号量

FreeRTOS复习笔记(四) —— 信号量

一、二值信号量与计数信号量

1.本例程思路:

本篇将二值信号量与计数信号量写入同一段代码,使用 _USE_BINARY_SEMAPHORE 与 _USE_COUNT_SEMAPHORE 进行区分

对于二值信号量,创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当KEY_GPIO(PA0)出现下降沿时释放信号量

对于计数信号量,当KEY_GPIO(PA0)出现下降沿时获取信号量和释放信号量,并打印当前可使用信号量数量

二值信号量的创建可以调用 xSemaphoreCreateBinary 函数,计数信号量的创建可以调用 xSemaphoreCreateCounting 函数

释放信号量使用 xSemaphoreGive,获取信号量使用 xSemaphoreTake

2.代码编写

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.24
  * @简  介: 无
  * @注  意: 无
  ******************************************************************************
  */
#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"

//#define _USE_BINARY_SEMAPHORE
#define _USE_COUNT_SEMAPHORE

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

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

#ifdef _USE_BINARY_SEMAPHORE
/* --- 二值信号量 --- */
SemaphoreHandle_t BinarySemaphore_Handle = NULL;
#endif

#ifdef _USE_COUNT_SEMAPHORE
/* --- 计数信号量 --- */
SemaphoreHandle_t CountSemaphore_Handle = NULL;
#endif

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
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);

#ifdef _USE_BINARY_SEMAPHORE
	/* 创建二值信号量 */
	BinarySemaphore_Handle = xSemaphoreCreateBinary();
	
	if (BinarySemaphore_Handle != NULL)
		printf("--- The BinarySemaphore was created successful ---.\r\n\r\n");
#endif
	
#ifdef _USE_COUNT_SEMAPHORE
	/* 创建计数信号量, 信号量个数 初始化均可被获取 */
	CountSemaphore_Handle = xSemaphoreCreateCounting(2, 2);
	
	if (CountSemaphore_Handle != NULL)
		printf("--- The CountSemaphores was created ---.\r\n\r\n");
#endif
	
	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

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

	for (;;)
	{
	#ifdef _USE_BINARY_SEMAPHORE
		/* 获取二值信号量, 信号量句柄 等待超时时间 */
		xReturn = xSemaphoreTake(BinarySemaphore_Handle, 0);
		
		if (xReturn == pdTRUE)
			printf("The BinarySemaphore has been obtained.\r\n");
	#endif
	
		/* 翻转LED */
		GPIOA->ODR ^= ((uint16_t)0x0100);
		
//		printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
	
		(void)xReturn;
	
		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;
	UBaseType_t CurrentSemaphoreNum = 0;
	
	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)
		{
		#ifdef _USE_BINARY_SEMAPHORE
			/* 释放二值信号量 */
			xReturn = xSemaphoreGive(BinarySemaphore_Handle);
			
			if (xReturn == pdTRUE)
				printf("The BinarySemaphore has been released successful.\r\n");
		#endif
		
		#ifdef _USE_COUNT_SEMAPHORE
			switch(_switch)
			{
			case 0:
				/* 获取计数信号量, 信号量句柄 等待超时时间 */
				xReturn = xSemaphoreTake(CountSemaphore_Handle, 0);
				_switch++;
				break;
				
			case 1:
				/* 获取计数信号量 */
				xReturn = xSemaphoreTake(CountSemaphore_Handle, 0);
				_switch++;
				break;
				
			case 2:
				/* 获取计数信号量 */
				xReturn = xSemaphoreTake(CountSemaphore_Handle, 0);
				_switch++;
				break;
				
			case 3:
				/* 释放计数信号量, 信号量句柄 */
				xReturn = xSemaphoreGive(CountSemaphore_Handle);
				_switch++;
				break;
				
			case 4:
				/* 释放计数信号量 */
				xReturn = xSemaphoreGive(CountSemaphore_Handle);
				_switch = 0;
				break;
				
			default: _switch = 0;
			}
			
			if ( (_switch == 1) || (_switch == 2) || (_switch == 3) )
			{
				if (xReturn == pdTRUE)
					printf("obtained. ");
				else
					printf("#error: no Semaphore. ");
			}
			
			if ( (_switch == 0) || (_switch == 4) )
				printf("released. ");
				
			/* 计数信号量数量 */
			CurrentSemaphoreNum = uxSemaphoreGetCount(CountSemaphore_Handle);

			printf("CurrentSemphoresNum: %lu\r\n", CurrentSemaphoreNum);
		#endif
		}
	
		edge_fall = 0;
		edge_rise = 0;
		
		(void)edge_rise;
		(void)_switch;
		(void)CurrentSemaphoreNum;
	
		vTaskDelay(20);
	}
}

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

二值信号量(一个任务释放信号量,另一个任务获取信号量)
SSS004.001
计数信号量(获取后信号量数量减少,释放后增加)
SSS004.002
经验证,程序运行结果与预想一致

二、互斥信号量

1.本例程思路:

创建三个任务,首先使用二值信号量模拟出优先级翻转,随后将二值信号量改为使用互斥信号量,与二值信号量进行对比,展示出互斥信号量的优先级继承机制

本篇使用 _USE_BINARY_SEMAPHORE 与 _USE_MUTEX_SEMAPHORE 区分使用二值信号量与互斥信号量

创建二值信号量调用 xSemaphoreCreateBinary 函数,创建互斥信号量调用 xSemaphoreCreateMutex 函数

2.代码编写

/**
  ******************************************************************************
  * @文件名: xxx.c
  * @作  者: author
  * @版  本: v1.0.0
  * @日  期: 2021.12.26
  * @简  介: 无
  * @注  意: 无
  ******************************************************************************
  */
#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"

//#define _USE_BINARY_SEMAPHORE
#define _USE_MUTEX_SEMAPHORE

void AppTaskCreateTask(void *);
void LowPriorityTask(void *);
void MidPriorityTask(void *);
void HighPriorityTask(void *);

#ifdef _USE_BINARY_SEMAPHORE
/* --- 二值信号量 --- */
SemaphoreHandle_t BinarySemaphore_Handle = NULL;
#else
/* --- 互斥信号量 --- */
SemaphoreHandle_t MutexSemaphore_Handle = NULL;
#endif

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
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", (uint16_t)128, NULL, (UBaseType_t)1, NULL);

	/* 开启任务调度 */
	vTaskStartScheduler();
	
	while(1);
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void AppTaskCreateTask(void *pvParameters)
{
	/* 临界保护 */
	taskENTER_CRITICAL();
	
	/* 创建任务 */
	xTaskCreate(HighPriorityTask, "HighPriorityTask", (uint16_t)128, NULL, (UBaseType_t)4, NULL);
	xTaskCreate(MidPriorityTask, "MidPriorityTask", (uint16_t)128, NULL, (UBaseType_t)3, NULL);
	xTaskCreate(LowPriorityTask, "LowPriorityTask", (uint16_t)128, NULL, (UBaseType_t)2, NULL);
	
#ifdef _USE_BINARY_SEMAPHORE
	/* 创建二值信号量 */
	BinarySemaphore_Handle = xSemaphoreCreateBinary();
	
	if (BinarySemaphore_Handle != NULL)
		printf("--- BinarySemaphore was Created ---.\r\n\r\n");
		
	/* 释放二值信号量 */
	xSemaphoreGive(BinarySemaphore_Handle);
#else
	/* 创建互斥信号量 */
	MutexSemaphore_Handle = xSemaphoreCreateMutex();
	
	if (MutexSemaphore_Handle != NULL)
		printf("--- MutexSemaphore was Created ---.\r\n\r\n");
#endif

	vTaskDelete(NULL);
	
	taskEXIT_CRITICAL();
}

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

	for (;;)
	{
	#ifdef _USE_BINARY_SEMAPHORE
		/* 等待二值信号量 */
		xReturn = xSemaphoreTake(BinarySemaphore_Handle, portMAX_DELAY);
	#else
		/* 等待互斥信号量 */
		xReturn = xSemaphoreTake(MutexSemaphore_Handle, portMAX_DELAY);
	#endif
		
		/* LowPriorityTask 占用信号量 */
		if (xReturn == pdTRUE)
			printf("LowPriorityTask has obtained the semaphore.\r\n");
		
		/* 任务切换 */
		for(i = 0; i < 0x7ffff; i++)
			taskYIELD();
			
	#ifdef _USE_BINARY_SEMAPHORE
		/* 释放二值信号量 */
		xReturn = xSemaphoreGive(BinarySemaphore_Handle);
	#else
		/* 释放互斥信号量 */
		xReturn = xSemaphoreGive(MutexSemaphore_Handle);
	#endif
		
		/* LowPriorityTask 释放信号量 */
		if (xReturn == pdTRUE)
			printf("LowPriorityTask has released the semaphore.\r\n");
			
		vTaskDelay(50);
	}
}

/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void MidPriorityTask(void *pvParameters)
{
	for (;;)
	{
		/* 翻转LED */
		GPIOA->ODR ^= ((uint16_t)0x0100);
		
		printf("MidPriorityTask is running.\r\n");
		
		vTaskDelay(50);
	}
}
 
/**
  * @简  介: 无
  * @参  数: 无
  * @返回值: 无
  */
void HighPriorityTask(void *pvParameters)
{
	BaseType_t xReturn = pdFALSE;
	
	for (;;)
	{
	#ifdef _USE_BINARY_SEMAPHORE
		/* 等待二值信号量 */
		xReturn = xSemaphoreTake(BinarySemaphore_Handle, portMAX_DELAY);
	#else
		/* 等待互斥信号量 */
		xReturn = xSemaphoreTake(MutexSemaphore_Handle, portMAX_DELAY);
	#endif
	
		/* HighPriorityTask 占用信号量 */
		if (xReturn == pdTRUE)
			printf("HighPriorityTask has obtained the semaphore.\r\n");
			
	#ifdef _USE_BINARY_SEMAPHORE
		/* 释放二值信号量 */
		xReturn = xSemaphoreGive(BinarySemaphore_Handle);
	#else
		/* 释放互斥信号量 */
		xReturn = xSemaphoreGive(MutexSemaphore_Handle);
	#endif
		
		/* HighPriorityTask 释放信号量 */
		if (xReturn == pdTRUE)
			printf("HighPriorityTask has released the semaphore.\r\n");
		
		vTaskDelay(50);
	}
}

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

二值信号量(发生了优先级翻转,打断了高优先级任务)
SSS004.003
互斥信号量(体现出了互斥量的优先级继承机制,提升了低优先级任务的优先级)
SSS004.004
经验证,程序运行结果与预想一致

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

生成海报
点赞 0

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

暂无评论

发表评论

相关推荐

GD32F205在IAR上移植FreeRTOS

简述: 1、主芯片:GD32F205VET6 2、编译器:IAR V8.40.1 3、系统:FreeRTOS V9.0 第一步:准备最简工程与系统 1、准备一个GD32

freertos与rtthread内核实现的不同处

一直在使用rtos作为主要开发内容,却没有详细了解过rtos的内核实现机制。最近一个月,抽了点时间将freertos和rtthread的内核代码看了下,了解了实时系统的实现机制和设计思想。这里学习fre

树莓派PICO+freeRTOS

这是pico板子的两个点灯实验,并非自己写的代码,仅是作为临摹的记录 pico的micropython使用 工具:Thonny, Python IDE for beginners 设置解释器为mic