目录

STM32-FreeRTOS信号量

STM32—FreeRTOS信号量

一、信号量

1、简介

https://i-blog.csdnimg.cn/direct/3b5c1ff632ee49f984c6d794be6c7390.png

2、信号量用于传递状态

3、队列与信号量对比

二、二值信号量

1、简介

二值信号量的本质是一个队列长度为1的队列,该对列只有空和满两种情况,这就是二值信号量;信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转问题,所以二值信号量更适用于同步!

https://i-blog.csdnimg.cn/direct/4e6cb7aec3574fe0ad3b9602bafeb5e7.png

2、相关API函数

https://i-blog.csdnimg.cn/direct/08e6f43101354853aca726c671ac752c.png

使用二值信号量的过程:创建二值信号量 -> 释放二值信号量 ->获取二值信号量

3、实验

https://i-blog.csdnimg.cn/direct/88a4952149f849d5bce6eaaab63ced72.png

4、代码

#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 "semphr.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);


 



/******************************************************************任务函数****************************************************/

QueueHandle_t		semaphore_handle;					//二值信号量句柄


void FrrrRTOS_Demo(void)
{
		
		semaphore_handle = xSemaphoreCreateBinary();
		if(semaphore_handle != NULL)
		{
			printf("\r\n二值信号量创建成功\r\n");		
		}
			 //创建开始任务
		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;
	BaseType_t err;
	while(1)
	{
		key = Key_GetNum();
		if(key == 2)
		{
			err = xSemaphoreGive(semaphore_handle);
			if(err == pdPASS)
			{
				printf("二值信号量释放成功\r\n");
			}else{printf("二值信号量释放失败\r\n");};
		}
		vTaskDelay(10);
	}
}


// 任务2 获取二值信号量函数
void task2(void *pvParameters)
{
	uint8_t i = 0;
    // 任务主循环
    while (1)
    {
			xSemaphoreTake(semaphore_handle,portMAX_DELAY);				//获取信号量并死等
			printf("二值信号量获取成功:%d\r\n",i++);
    }
}


//不调用系统延时函数,因为xQueueReceive()函数如果读取完队列里面的数据,就会由就绪态转变为阻塞态;

key.c

#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "Delay.h"

//extern	TaskHandle_t LED0Task_Handler;
//volatile uint8_t KeyPressed = 0;  // 用于标记按键事件

/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
	* 按键: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
}

5、 实验解析

程序刚开始执行:

https://i-blog.csdnimg.cn/direct/b797bf8024c54102962bf334a4f1172d.png

按下按键(PB12):

这里其实是先执行的任务2,但是因为没有获取到信号量,一直在阻塞(死等);当按键按下时,信号量得到释放,任务2立马抢占执行并打印信息,然后在进入阻塞;任务1开始打印信息;

6、重点

创建二值信号量:

xSemaphoreCreateBinary() ;

https://i-blog.csdnimg.cn/direct/5d069eb3c417406cacd3e5d1f0edbf8b.png

释放二值信号量:

xSemaphoreGive( xSemaphore ) ;

https://i-blog.csdnimg.cn/direct/767acb110cd8469c808f7b5ba2263f45.png 获取二值信号量:

xSemaphoreTake( xSemaphore, xBlockTime );

https://i-blog.csdnimg.cn/direct/9238d567fd3f47509b472879e76a544e.png

三、计数型信号量

1、简介

计数型信号量:计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建时确定的。

https://i-blog.csdnimg.cn/direct/8dece0aa8daf4224842e545e52fc6eeb.png

2、相关API函数

https://i-blog.csdnimg.cn/direct/59c8bdf184d948a7b17321db68075bdd.png 使用计数型信号量的过程:创建计数型信号量 ->释放信号量 -> 获取信号量

启用下面宏:

#define configUSE_COUNTING_SEMAPHORES    1                                            //启用了计数信号量相关的功能

计数型信号量的释放和获取与二值信号量相同!

3、实验

4、代码

freertos_demo.c

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.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);


 



/******************************************************************任务函数****************************************************/

QueueHandle_t		count_semaphore_handle;					//二值信号量句柄


void FrrrRTOS_Demo(void)
{																	 										
		count_semaphore_handle = xSemaphoreCreateCounting(100,0);								/*创建计数型信号量*/
		if(count_semaphore_handle != NULL)
		{
			printf("\r\n计数型信号量创建成功\r\n");		
		}
			 //创建开始任务
		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)
		{
			if(count_semaphore_handle != NULL)
			{
				xSemaphoreGive(count_semaphore_handle);								/*释放计数型信号量*/
			}
		}
		vTaskDelay(10);
	}
}


// 任务2 获取计数型信号量函数
void task2(void *pvParameters)
{

		BaseType_t err;
    // 任务主循环
    while (1)
    {
			err = xSemaphoreTake(count_semaphore_handle,portMAX_DELAY);				//获取信号量并死等
			if(err == pdTRUE)
			{
				printf("信号量计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semaphore_handle));
			}
			vTaskDelay(1000);
    }
}


//不调用系统延时函数,因为xQueueReceive()函数如果读取完队列里面的数据,就会由就绪态转变为阻塞态;

key.c

#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "Delay.h"

//extern	TaskHandle_t LED0Task_Handler;
//volatile uint8_t KeyPressed = 0;  // 用于标记按键事件

/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
	* 按键: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
}

5、实验解析

程序开始运行:

https://i-blog.csdnimg.cn/direct/54843d9f99424adaa7b883083a854b09.png

按键缓慢(PB12)按下:

https://i-blog.csdnimg.cn/direct/133058837282494db910152203695aea.png

按键快速(PB12)按下:

按键快速按下时,释放的资源较多,而获取资源是1秒一次,所以会自己进行每隔一秒获取,直到为:0;

https://i-blog.csdnimg.cn/direct/8e7b17851ee440f29f8ec4a0a07bba08.png

6、重点

宏定义(默认没有,需要置一):

#define configUSE_COUNTING_SEMAPHORES    1                                            //启用了计数信号量相关的功能

创建计数型信号量:

xSemaphoreCreateCounting( uxMaxCount, uxInitialCount );

https://i-blog.csdnimg.cn/direct/e9993c3951154e18b58a0b554172650a.png 获取信号量当前计数值大小:

uxSemaphoreGetCount( xSemaphore )  ;

四、优先级翻转讲解

1、简介

优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行;

优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。

在使用二值信号量的时候,经常会遇到优先级翻转的问题。

举个例子:

前提条件: 任务H/L需要信号量;任务M是单纯的打印函数;

https://i-blog.csdnimg.cn/direct/f167d1fb34624589825ce7273d45feed.png

2、实验

https://i-blog.csdnimg.cn/direct/70ee0b4032dc472aa78a198266c911be.png

3、代码

freertos_demo.c

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.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 TASK2_PRIO							2
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t low_task_Handler;
//任务函数
void low_task(void *pvParameters);


//任务优先级
#define TASK3_PRIO							3
//任务堆栈大小	
#define TASK3_STACK_SIZE 				128  
//任务句柄
TaskHandle_t middle_task_Handler;
//任务函数
void middle_task(void *pvParameters);


//任务优先级
#define TASK4_PRIO							4
//任务堆栈大小	
#define TASK4_STACK_SIZE 				128  
//任务句柄
TaskHandle_t high_task_Handler;
//任务函数
void high_task(void *pvParameters);


 



/******************************************************************任务函数****************************************************/

QueueHandle_t		semaphore_handle;					//二值信号量句柄


void FrrrRTOS_Demo(void)
{
		
		semaphore_handle = xSemaphoreCreateBinary();
		if(semaphore_handle != NULL)
		{
			printf("\r\n二值信号量创建成功\r\n");		
		}
		xSemaphoreGive(semaphore_handle);
			 //创建开始任务
		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 )low_task,     
                (const char*    )"low_task",   
                (uint16_t       )TASK2_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&low_task_Handler);   

    //创建2任务
    xTaskCreate((TaskFunction_t )middle_task,     
                (const char*    )"middle_task",   
                (uint16_t       )TASK3_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&middle_task_Handler);   	

    //创建3任务
    xTaskCreate((TaskFunction_t )high_task,     	
                (const char*    )"high_task",   	
                (uint16_t       )TASK4_STACK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )TASK4_PRIO,	
                (TaskHandle_t*  )&high_task_Handler); 								
		
    vTaskDelete(NULL); 							//删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


//1 低优先级任务函数
void low_task(void *pvParameters)
{

	while(1)
	{
		printf("\r\nlow_task获取信号量\r\n");
		xSemaphoreTake(semaphore_handle,portMAX_DELAY);	
		printf("low_task低优先级任务正在运行!!!\r\n");
		delay_xms(3000);
		printf("low_task释放信号量\r\n");
		xSemaphoreGive(semaphore_handle);
		vTaskDelay(1000);
	}
}


// 任务2 中优先级人物函数
void middle_task(void *pvParameters)
{
    // 任务主循环
    while (1)
    {
			printf("\r\nmiddle_task中优先级任务正在运行!!!\r\n");	
			vTaskDelay(1000);
    }
}

// 任务3 高优先级任务函数
void high_task(void *pvParameters)
{
    // 任务主循环
    while (1)
    {
			printf("\r\nhigh_task获取信号量\r\n");
			xSemaphoreTake(semaphore_handle,portMAX_DELAY);	
			printf("high_task高优先级任务正在运行!!!\r\n");
			delay_xms(1000);
			printf("high_task释放信号量\r\n");
			xSemaphoreGive(semaphore_handle);
			vTaskDelay(1000);
			
    }
}

4、实验解析

https://i-blog.csdnimg.cn/direct/0594abe5a1e5492ba07dd698d80875e8.png

五、互斥信号量

1、简介

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!

优先级继承: 当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

优先级继承案例:

https://i-blog.csdnimg.cn/direct/eb33f61f51fa455b9eacdbf8982aaaf2.png

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响。

注意:互斥信号量不能用于中断服务函数中,原因如下:

(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。

(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

2、相关API函数

使用互斥信号量:首先将宏configUSE_MUTEXES置一

#define configUSE_MUTEXES             1

https://i-blog.csdnimg.cn/direct/5bfcb714faf44d809f6333f495358403.png 使用流程:创建互斥信号量 -> (task)获取信号量 ->(give)释放信号量

3、实验

https://i-blog.csdnimg.cn/direct/ac42d33d97bd42348289230d644c7eea.png

4、代码

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.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 TASK2_PRIO							2
//任务堆栈大小	
#define TASK2_STACK_SIZE 				128  
//任务句柄
TaskHandle_t low_task_Handler;
//任务函数
void low_task(void *pvParameters);


//任务优先级
#define TASK3_PRIO							3
//任务堆栈大小	
#define TASK3_STACK_SIZE 				128  
//任务句柄
TaskHandle_t middle_task_Handler;
//任务函数
void middle_task(void *pvParameters);


//任务优先级
#define TASK4_PRIO							4
//任务堆栈大小	
#define TASK4_STACK_SIZE 				128  
//任务句柄
TaskHandle_t high_task_Handler;
//任务函数
void high_task(void *pvParameters);


 



/******************************************************************任务函数****************************************************/

QueueHandle_t		mutex_semaphore_handle;					//二值信号量句柄


void FrrrRTOS_Demo(void)
{
		
		mutex_semaphore_handle = xSemaphoreCreateMutex();				//创建互斥信号量;主动释放一次
		if(mutex_semaphore_handle != NULL)
		{
			printf("\r\n互斥信号量创建成功\r\n");		
		}

			 //创建开始任务
		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 )low_task,     
                (const char*    )"low_task",   
                (uint16_t       )TASK2_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&low_task_Handler);   

    //创建2任务
    xTaskCreate((TaskFunction_t )middle_task,     
                (const char*    )"middle_task",   
                (uint16_t       )TASK3_STACK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )TASK3_PRIO,
                (TaskHandle_t*  )&middle_task_Handler);   	

    //创建3任务
    xTaskCreate((TaskFunction_t )high_task,     	
                (const char*    )"high_task",   	
                (uint16_t       )TASK4_STACK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )TASK4_PRIO,	
                (TaskHandle_t*  )&high_task_Handler); 								
		
    vTaskDelete(NULL); 							//删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


//1 低优先级任务函数
void low_task(void *pvParameters)
{

	while(1)
	{
		printf("\r\nlow_task获取信号量\r\n");
		xSemaphoreTake(mutex_semaphore_handle,portMAX_DELAY);	
		printf("low_task低优先级任务正在运行!!!\r\n");
		delay_xms(3000);
		printf("low_task释放信号量\r\n");
		xSemaphoreGive(mutex_semaphore_handle);
		vTaskDelay(1000);
	}
}


// 任务2 中优先级人物函数
void middle_task(void *pvParameters)
{
    // 任务主循环
    while (1)
    {
			printf("\r\nmiddle_task中优先级任务正在运行!!!\r\n");	
			vTaskDelay(1000);
    }
}

// 任务3 高优先级任务函数
void high_task(void *pvParameters)
{
    // 任务主循环
    while (1)
    {
			printf("\r\nhigh_task获取信号量\r\n");
			xSemaphoreTake(mutex_semaphore_handle,portMAX_DELAY);	
			printf("high_task高优先级任务正在运行!!!\r\n");
			delay_xms(1000);
			printf("high_task释放信号量\r\n");
			xSemaphoreGive(mutex_semaphore_handle);
			vTaskDelay(1000);
			
    }
}

5、实验解析

https://i-blog.csdnimg.cn/direct/bdda198d7c1d402f8c0d866da414df28.png

6、重点

使用互斥信号量,先将下面宏置1;

#define configUSE_MUTEXES             1

创建互斥信号量:

xSemaphoreCreateMutex() ;

https://i-blog.csdnimg.cn/direct/03d91cd3ba764d8cb5aed495d21c2341.png

六、总结

https://i-blog.csdnimg.cn/direct/0f77bc81a7e74e8e86582e0eb63b617c.png