STM32移植FreeRTOS系列十八:信号量详细解读

目录

1、信号量的简介

 队列与信号量的对比

2、二值信号量

2.1、二值信号量简介

 2.2、二值信号量相关API函数

创建二值信号量函数:SemaphoreHandle_t   xSemaphoreCreateBinary( void )

获取二值信号量函数:BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime )

2.3、二值信号量实验

3、计数型信号量

3.1、计数型信号量简介

3.1.1、事件计数

3.1.2、资源管理

3.2、计数型信号量创建API函数

3.2.1、创建一个计数型信号量 xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )  

3.2.2、获取信号量当前计数值大小uxSemaphoreGetCount( xSemaphore )     

3.3、计数型信号量实验

4、优先级翻转

4.1优先级翻转概念

4.2优先级翻转实验

5、互斥信号量

5.1、互斥信号量简介

5.2、优先级继承

5.3、互斥信号量相关APi函数

  互斥信号量创建API函数xSemaphoreCreateMutex() 

5.4、互斥信号量实验

6、代码链接


1、信号量的简介

信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步,FreeRTOS 中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量。

信号量常常用于控制对共享资源的访问和任务同步。

信号量用于控制共享资源访问的场景相当于一个上锁机制,代码只有获得了这个锁的钥匙 才能够执行。(这时候可以把等待时间设置为最久,这样想要获取该信号量的任务如果一直获取不到该信号量就会陷入阻塞状态也就是相当于上锁了)

信号量的另一个重要的应用场合就是任务同步,用于任务与任务或中断与任务之间的同步。在执行中断服务函数的时候可以通过向任务发送信号量来通知任务它所期待的事件发生了,当退出中断服务函数以后在任务调度器的调度下同步的任务就会执行。

 当计数值大于0,代表有信号量资源

当释放信号量,信号量计数值(资源数)加一

当获取信号量,信号量计数值(资源数)减一

信号量:用于传递状态

信号量的计数值都有限制:限定最大值。

如果最大值被限定为1,那么它就是二值信号量;

如果最大值不是1,它就是计数型信号量。

 队列与信号量的对比

队列

信号量

可以容纳多个数据;

创建队列有两部分内存:队列结构体+队列项存储空间

仅存放计数值,无法存放其他数据;

创建信号量,只需分配信号量结构体

写入队列:当队列满时,可阻塞;

释放信号量:不可阻塞,计数值++

当计数值为最大值时,返回失败

读取队列:当队列为空时,可阻塞;

获取信号量:计数值--

当没有资源时,可阻塞

2、二值信号量

2.1、二值信号量简介

二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的。

二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一 些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问

 2.2、二值信号量相关API函数

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

函数

描述

xSemaphoreCreateBinary()

使用动态方式创建二值信号量

xSemaphoreCreateBinaryStatic()

使用静态方式创建二值信号量

xSemaphoreGive()

释放信号量

xSemaphoreGiveFromISR()

在中断中释放信号量

xSemaphoreTake()

获取信号量

xSemaphoreTakeFromISR()

在中断中获取信号量

创建二值信号量函数:SemaphoreHandle_t   xSemaphoreCreateBinary( void )

 #define   xSemaphoreCreateBinary( )   						\
 xQueueGenericCreate( 1 ,   semSEMAPHORE_QUEUE_ITEM_LENGTH  ,   queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define  semSEMAPHORE_QUEUE_ITEM_LENGTH      ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_BASE                  	( ( uint8_t ) 0U )	/* 队列 */
#define queueQUEUE_TYPE_SET                  	( ( uint8_t ) 0U )	/* 队列集 */
#define queueQUEUE_TYPE_MUTEX                 	( ( uint8_t ) 1U )	/* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE    	( ( uint8_t ) 2U )	/* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE     	( ( uint8_t ) 3U )	/* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX       		( ( uint8_t ) 4U )	/* 递归互斥信号量 */

返回值

描述

NULL

创建失败

其他值

创建成功返回二值信号量的句柄

返回的首地址也就是句柄

获取二值信号量函数:BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime )

形参

描述

xSemaphore

要获取的信号量句柄

xBlockTime

阻塞时间

返回值

描述

pdTRUE

获取信号量成功

pdFALSE

超时,获取信号量失败

2.3、二值信号量实验

1、实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用

2、实验设计:将设计三个任务:start_tasktask1task2

三个任务的功能如下:

 main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "malloc.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/************************************************
 ALIENTEK 战舰STM32F103开发板 FreeRTOS实验14-1
 FreeRTOS二值信号量操作实验-库函数版本
 技术支持:www.openedv.com
 淘宝店铺:http://eboard.taobao.com 
 关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 广州市星翼电子科技有限公司  
 作者:正点原子 @ALIENTEK
************************************************/

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define configSTACK_DEPTH_TYPE uint16_t 
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/******************************************************************************************************/
QueueHandle_t semphore_handle;

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	 
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_Init();							//初始化按键

	my_mem_init(SRAMIN);            	//初始化内部内存池


    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();      //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();             //退出临界区
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;

    while(1) 
    {

        key = KEY_Scan(0);
        if(key == KEY1_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
            
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;	
    while(1)
    {   

        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等,这里进入阻塞后运行TASK1 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
        
    }
}

3、计数型信号量

3.1、计数型信号量简介

有些资料中也将计数型信号量叫做数值信号量,二值信号量相当于长度为 1 的队列,那么 计数型信号量就是长度大于 1 的队列。同二值信号量一样,用户不需要关心队列中存储了什么 数据,只需要关心队列是否为空即可。计数型信号量通常用于如下两个场合:

3.1.1、事件计数

在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数 值),其他任务会获取信号量(信号量计数值减一,信号量值就是队列结构体成员变量 uxMessagesWaiting)来处理事件。在这种场合中创建的计数型信号量初始计数值为 0。

当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0

3.1.2、资源管理

在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。 一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减 一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量, 释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量, 比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。

信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目

3.2、计数型信号量创建API函数

函数

描述

xSemaphoreCreateCounting()

使用动态方法创建计数型信号量。

xSemaphoreCreateCountingStatic()

使用静态方法创建计数型信号量

uxSemaphoreGetCount()

获取信号量的计数值

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

3.2.1、创建一个计数型信号量 xSemaphoreCreateCounting(  uxMaxCount  uxInitialCount  )  

#define 	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )   		

xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 

形参

描述

uxMaxCount

计数值的最大值限定

uxInitialCount

计数值的初始值

返回值

描述

NULL

创建失败

其他值

创建成功返回计数型信号量的句柄

3.2.2、获取信号量当前计数值大小uxSemaphoreGetCount( xSemaphore )     

#define 	uxSemaphoreGetCount( xSemaphore ) 								

uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

形参

描述

xSemaphore

信号量句柄

返回值

描述

整数

当前信号量的计数值大小

3.3、计数型信号量实验

1、实验目的:学习 FreeRTOS 的计数型信号量相关API函数的使用

2、实验设计:将设计三个任务:start_tasktask1task2

这里的TASK2一直在判断是否有可获取的计数型信号量,获取到的话就会进行打印当前信号量计数值。并且因为获取到的信号量数值会减一。每按KEY0一次计数型信号量加一。

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "malloc.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/************************************************
 ALIENTEK 战舰STM32F103开发板 FreeRTOS实验14-1
 FreeRTOS二值信号量操作实验-库函数版本
 技术支持:www.openedv.com
 淘宝店铺:http://eboard.taobao.com 
 关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 广州市星翼电子科技有限公司  
 作者:正点原子 @ALIENTEK
************************************************/

/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define configSTACK_DEPTH_TYPE uint16_t 
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

/******************************************************************************************************/
QueueHandle_t count_semphore_handle;

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	 
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_Init();							//初始化按键

	my_mem_init(SRAMIN);            	//初始化内部内存池
    count_semphore_handle = xSemaphoreCreateCounting(100 , 10);  /* 创建计数型信号量 */
    if(count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();             //退出临界区
}

/* 任务一,释放计数型信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = KEY_Scan(0);
        if(key == KEY1_PRES)
        {
            if(count_semphore_handle != NULL)
            {
                xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取计数型信号量 */
void task2( void * pvParameters )
{
    BaseType_t err = 0;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
        }
        vTaskDelay(1000);
    }
}

4、优先级翻转

4.1优先级翻转概念

在使用二值信号量的时候会遇到很常见的一个问题——优先级翻转,优先级翻转在可剥夺 内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能 会导致严重的后果

 (1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。

(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。

(3) 任务 L 获得信号量并开始使用该共享资源。

(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。

(5) 任务 H 开始运行。

(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务 L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。

(7) 任务 L 继续运行。

(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务 L 的 CPU 使用权。

(9) 任务 M 处理该处理的事。

(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。

(11) 任务 L 继续运行。

(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高 优先级的任务在等待这个信号量,故内核做任务切换。

(13) 任务 H 得到该信号量并接着运行。 在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直 等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使 得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级翻转。

4.2优先级翻转实验

1、实验目的:在使用二值信号量的时候会存在优先级翻转的问题,本实验通过模拟的方式实现优先级翻转,观察优先级翻转对抢占式内核的影响

2、实验设计:将设计四个任务:start_taskhigh_task middle_task low_task

 main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "malloc.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define configSTACK_DEPTH_TYPE uint16_t 
#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    low_task_handler;
void low_task( void * pvParameters );


/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    middle_task_handler;
void middle_task( void * pvParameters );


/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    high_task_handler;
void high_task( void * pvParameters );

/******************************************************************************************************/
QueueHandle_t semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	 
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	KEY_Init();							//初始化按键

	my_mem_init(SRAMIN);            	//初始化内部内存池
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xSemaphoreGive(semphore_handle);        /* 释放一次信号量 */
    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   low_task,
                (char *                 )   "low_task",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &low_task_handler );
                
    xTaskCreate((TaskFunction_t         )   middle_task,
                (char *                 )   "middle_task",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &middle_task_handler );
                
    xTaskCreate((TaskFunction_t         )   high_task,
                (char *                 )   "high_task",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &high_task_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

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

/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
    while(1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

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



实验现象
这里刚开始用的2.6版本的串口调试助手有乱码,,换个2.0版本就好了,还不清楚原因。

5、互斥信号量

5.1、互斥信号量简介

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

5.2、优先级继承

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

 此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低

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

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

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

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

5.3、互斥信号量相关APi函数

使用互斥信号量:首先将宏configUSE_MUTEXES置一
使用流程:创建互斥信号量 ——task)获取信号量 ——give)释放信号量

创建互斥信号量函数:

函数

描述

xSemaphoreCreateMutex()

使用动态方法创建互斥信号量。

xSemaphoreCreateMutexStatic()

使用静态方法创建互斥信号量。

互斥信号量的释放和获取函数与二值信号量相同 !只不过互斥信号量不支持中断中调用

注意:创建互斥信号量时,会主动释放一次信号量

  互斥信号量创建API函数xSemaphoreCreateMutex() 

#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

返回值

描述

NULL

创建失败

其他值

创建成功返回互斥信号量的句柄

5.4、互斥信号量实验

1、实验目的:在优先级翻转实验的基础,加入互斥信号量,解决优先级翻转问题

2、实验设计:将优先级翻转所用到的信号量函数,修改成互斥信号量即可,通过串口打印提示信息

这里注意,在互斥信号量中的延时函数使用delay_xms而不是delay_ms()!!!!!!!

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "malloc.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define configSTACK_DEPTH_TYPE uint16_t
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task(void *pvParameters);

/* TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t low_task_handler;
void low_task(void *pvParameters);

/* TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t middle_task_handler;
void middle_task(void *pvParameters);

/* TASK3 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK3_PRIO 4
#define TASK3_STACK_SIZE 128
TaskHandle_t high_task_handler;
void high_task(void *pvParameters);

/******************************************************************************************************/
QueueHandle_t mutex_semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组4
    delay_init();                                   // 延时函数初始化
    uart_init(115200);                              // 初始化串口
    LED_Init();                                     // 初始化LED
    KEY_Init();                                     // 初始化按键

    my_mem_init(SRAMIN);                             // 初始化内部内存池
    mutex_semphore_handle = xSemaphoreCreateMutex(); /* 创建互斥信号量,并且主动释放一次信号量 */
    if (mutex_semphore_handle != NULL)
    {
        printf("互斥信号量创建成功!!!\r\n");
    }

    xTaskCreate((TaskFunction_t)start_task,
                (char *)"start_task",
                (configSTACK_DEPTH_TYPE)START_TASK_STACK_SIZE,
                (void *)NULL,
                (UBaseType_t)START_TASK_PRIO,
                (TaskHandle_t *)&start_task_handler);
    vTaskStartScheduler();
}

void start_task(void *pvParameters)
{
    taskENTER_CRITICAL(); /* 进入临界区 */
    xTaskCreate((TaskFunction_t)low_task,
                (char *)"low_task",
                (configSTACK_DEPTH_TYPE)TASK1_STACK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIO,
                (TaskHandle_t *)&low_task_handler);

    xTaskCreate((TaskFunction_t)middle_task,
                (char *)"middle_task",
                (configSTACK_DEPTH_TYPE)TASK2_STACK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK2_PRIO,
                (TaskHandle_t *)&middle_task_handler);

    xTaskCreate((TaskFunction_t)high_task,
                (char *)"high_task",
                (configSTACK_DEPTH_TYPE)TASK3_STACK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK3_PRIO,
                (TaskHandle_t *)&high_task_handler);

    vTaskDelete(NULL);
    taskEXIT_CRITICAL(); /* 退出临界区 */
}

/* 任务一,低优先级任务 */
void low_task(void *pvParameters)
{
    while (1)
    {
        printf("low_task想要获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
        printf("low_task获取信号量成功\r\n");
        printf("low_task正在运行!!!\r\n");
        delay_xms(3000);
        printf("low_task想要释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle);
        printf("low_task释放信号量成功\r\n");
        vTaskDelay(1000);
    }
}

/* 任务二,中优先级任务 */
void middle_task(void *pvParameters)
{
    while (1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task(void *pvParameters)
{
    while (1)
    {
        printf("high_task想要获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle, portMAX_DELAY);
        printf("high_task获取信号量成功\r\n");
        printf("high_task正在运行!!!\r\n");
        delay_xms(1000);
        printf("high_task想要释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle);
        printf("high_task释放信号量成功\r\n");
        vTaskDelay(1000);
    }
}

6、代码链接

链接:https://pan.baidu.com/s/1oSB2cpBuZ6InQaoABUe7Nw?pwd=37m7 
提取码:37m7

写了好几天,点个赞亲

猜你喜欢

转载自blog.csdn.net/qq_51519091/article/details/131839252