FreeRTOS操作系统学习


前言

前面了解了优先级翻转在FreeRTOS中是绝对不允许优先级翻转的,而互斥信号量可以完美的解决这个问题。

一、互斥信号量简介

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。

互斥信号量使用和二值信号量相同的API操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。

注意:
互斥信号量不能用于中断服务函数中:

互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。

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

二、API函数

1.创建互斥信号量

在这里插入图片描述
1、函数 xSemaphoreCreateMutex()
此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。

SemaphoreHandle_t xSemaphoreCreateMutex( void )

返回值:
NULL:
其他值:
互斥信号量创建失败。
创建成功的互斥信号量的句柄。

2、函数 xSemaphoreCreateMutexStatic()
此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的
RAM 需要由用户来分配

SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )

参数:
pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2. 释放互斥信号量

释 放 互 斥 信 号 量 的 时 候 和 二 值 信 号 量 、 计 数 型 信 号 量 一 样 , 都 是 用 的 函 数
xSemaphoreGive()实际上完成信号量释放的是函数 xQueueGenericSend()

3. 获取互斥信号量

获取互斥信号量的函数同获取二值信号量和计数型信号量的函数相同,都是
xSemaphoreTake()(实际执行信号量获取的函数是 xQueueGenericReceive())

三、互斥信号量实验

就是把所有二值信号量的地方改成互斥信号量。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


//¿ªÊ¼ÈÎÎñÈÎÎñº¯Êý
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //½øÈëÁÙ½çÇø
	
	//´´½¨»¥³âÐźÅÁ¿
	MutexSemaphore=xSemaphoreCreateMutex();
	
    //´´½¨¸ßÓÅÏȼ¶ÈÎÎñ
    xTaskCreate((TaskFunction_t )high_task,             
                (const char*    )"high_task",           
                (uint16_t       )HIGH_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )HIGH_TASK_PRIO,        
                (TaskHandle_t*  )&HighTask_Handler);   
    //´´½¨ÖеÈÓÅÏȼ¶ÈÎÎñ
    xTaskCreate((TaskFunction_t )middle_task,     
                (const char*    )"middle_task",   
                (uint16_t       )MIDDLE_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )MIDDLE_TASK_PRIO,
                (TaskHandle_t*  )&MiddleTask_Handler); 
	//´´½¨µÍÓÅÏȼ¶ÈÎÎñ
    xTaskCreate((TaskFunction_t )low_task,     
                (const char*    )"low_task",   
                (uint16_t       )LOW_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )LOW_TASK_PRIO,
                (TaskHandle_t*  )&LowTask_Handler);
    vTaskDelete(StartTask_Handler); //ɾ³ý¿ªÊ¼ÈÎÎñ
    taskEXIT_CRITICAL();            //Í˳öÁÙ½çÇø
}

//¸ßÓÅÏȼ¶ÈÎÎñµÄÈÎÎñº¯Êý
void high_task(void *pvParameters)
{
	u8 num;
	
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(5,110,115,314); 	//»­Ò»¸ö¾ØÐÎ	
	LCD_DrawLine(5,130,115,130);		//»­Ïß
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	
	while(1)
	{
		vTaskDelay(500);	//ÑÓʱ500ms£¬Ò²¾ÍÊÇ500¸öʱÖÓ½ÚÅÄ	
		num++;
		printf("high task Pend Sem\r\n");
		xSemaphoreTake(MutexSemaphore,portMAX_DELAY);	//»ñÈ¡»¥³âÐźÅÁ¿
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); 	//Ìî³äÇøÓò
		LED1=!LED1;
		xSemaphoreGive(MutexSemaphore);					//ÊÍ·ÅÐźÅÁ¿
		vTaskDelay(500);	//ÑÓʱ500ms£¬Ò²¾ÍÊÇ500¸öʱÖÓ½ÚÅÄ  
	}
}

//ÖеÈÓÅÏȼ¶ÈÎÎñµÄÈÎÎñº¯Êý
void middle_task(void *pvParameters)
{
	u8 num;
	
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(125,110,234,314); //»­Ò»¸ö¾ØÐÎ	
	LCD_DrawLine(125,130,234,130);		//»­Ïß
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	while(1)
	{
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //Ìî³äÇøÓò
		LED0=!LED0;
        vTaskDelay(1000);	//ÑÓʱ1s£¬Ò²¾ÍÊÇ1000¸öʱÖÓ½ÚÅÄ	
	}
}

//µÍÓÅÏȼ¶ÈÎÎñµÄÈÎÎñº¯Êý
void low_task(void *pvParameters)
{
	static u32 times;

	while(1)
	{
		xSemaphoreTake(MutexSemaphore,portMAX_DELAY);	//»ñÈ¡»¥³âÐźÅÁ¿
		printf("low task Running!\r\n");
		for(times=0;times<5000000;times++)				//Ä£ÄâµÍÓÅÏȼ¶ÈÎÎñÕ¼Óû¥³âÐźÅÁ¿
		{
			taskYIELD();								//·¢ÆðÈÎÎñµ÷¶È
		}
		xSemaphoreGive(MutexSemaphore);					//ÊÍ·Å»¥³âÐźÅÁ¿
		vTaskDelay(1000);	//ÑÓʱ1s£¬Ò²¾ÍÊÇ1000¸öʱÖÓ½ÚÅÄ	
	}
}



现象:

在这里插入图片描述
等待 low_task 任务释放互斥信号量。
但是 middle_task 不会运行。因为由于 low_task 正在使用互斥信号量,所以 low_task 任务优先级暂时提升到了与 high_task 相同的优先级,这个优先级比任务 middle_task 高,所以 middle_task任务不能再打断 low_task 任务的运行了!

互斥信号量只能获取,想多次获取是不可以的,这个时候我们采用递归互斥信号量

四、递归互斥信号量

1.递归互斥信号量简介

递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能
再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可再次获取这个递归互斥信号量,而且次数不限!一个任务使用函数 xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。

递归互斥信号量也有优先级继承的机制,所以当任务使用完递归互斥信号量以后一定要记
得释放。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中。

2.创建递归互斥信号量

在这里插入图片描述
1、函数 xSemaphoreCreateRecursiveMutex()
此函数用于创建一个递归互斥信号量,所需要的内存通过动态内存管理方法分配。

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

2、函数 xSemaphoreCreateRecursiveMutexStatic()
此函数也是创建递归互斥信号量的,只不过使用此函数创建递归互斥信号量的话信号量所
需 要 的 RAM 需 要 由 用 户 来 分 配

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )

参数:
pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

3、释放递归互斥信号量

递归互斥信号量有专用的释放函数:xSemaphoreGiveRecursive()

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )  //函数(任务句柄)

4.获取递归互斥信号量

递归互斥信号量的获取使用函数 xSemaphoreTakeRecursive()

总结

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, //要获取的信号量
TickType_t xTicksToWait )//阻塞时间

5.递归互斥信号量实例

SemaphoreHandle_t RecursiveMutex; //递归互斥信号量句柄
//某个任务中创建一个递归互斥信号量
void vATask( void * pvParameters ) {
//没有创建创建递归互斥信号量之前不要使用!
RecursiveMutex = xSemaphoreCreateRecursiveMutex(); //创建递归互斥信号量
for( ;; )
{ 
/************任务代码**************/
} }
//任务调用的使用递归互斥信号量的功能函数。
void vAFunction( void )
{ /**********其他处理代码*****************/
if( xMutex != NULL )
{
//获取递归互斥信号量,阻塞时间为 10 个节拍
if( xSemaphoreTakeRecursive( RecursiveMutex, 10 ) == pdTRUE )
{
/***********其他处理过程*************/
//这里为了演示,所以是顺序的获取递归互斥信号量,但是在实际的代码中肯定
//不是这么顺序的获取的,真正的代码中是混合着其他程序调用的。
xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
//任务获取了三次递归互斥信号量,所以就得释放三次!
xSemaphoreGiveRecursive( RecursiveMutex);
xSemaphoreGiveRecursive( RecursiveMutex);
xSemaphoreGiveRecursive( RecursiveMutex);
//递归互斥信号量释放完成,可以被其他任务获取了
}
else
{ 
/**********递归互斥信号量获取失败***********/
}
}
}

要注意递归信号量使用几次就要释放几次。

总结

互斥信号量的使用就是把所有的二值信号量变成互斥信号量,也就是讲函数变成创建互斥信号量的函数,互斥信号量多了一个递归互斥信号量,递归互斥量使用方法也是如此,在使用递归互斥信号量时,获取了几次就要释放几次,这个要特别注意。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐