FreeRTOS 信号量

1. 信号量的简介

信号:发送状态
队列:发送数据

量:数量

  • 如果数量是 0 和 1,叫做二值信号量
  • 如果数量很多,大于 1,叫做计数型信号量

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。
在这里插入图片描述

  • 空车位 :信号量资源数(计数值)
  • 让出占用车位 :释放信号量(计数值++)
  • 占用车位 :获取信号量(计数值–)

假设有一个人需要在停车场停车

  1. 首先判断停车场是否还有空车位(判断信号量是否有资源);
  2. 停车场正好有空车位(信号量有资源),那么就可以直接将车开入空车位进行停车(获取信号量成功);
  3. 停车场已经没有空车位了(信号量没有资源),那么这个人可以选择不停车(获取信号量失败);也可以选择等待(任务阻塞)其他人将车开出停车场(释放信号量资源), 然后再将车停入空车位 。

信号量示意图:
在这里插入图片描述
当计数值大于0,代表有信号量资源:

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

信号量:用于传递状态

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

  • 如果最大值被限定为1,那么它就是二值信号量;
  • 如果最大值不是1,它就是计数型信号量。

队列和信号量的区别:

队列信号量
可以容纳多个数据;创建队列有两部分内存:队列结构体+队列项存储空间仅存放计数值,无法存放其他数据;创建信号量,只需分配信号量结构体
写入队列:当队列满时,可阻塞;释放信号量:不可阻塞,计数值++,当计数值为最大值时,返回失败
读取队列:当队列为空时,可阻塞;获取信号量:计数值–,当没有资源时,可阻塞

信号量本质也是使用队列实现的。只不过它存储的是计数值,然后内存空间也比较小,并且释放信号量是不可以阻塞的。

1.1 二值信号量

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空(0)和满(1)两种情况,这就是二值。

二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!

互斥访问:比如说有一个门,你要进入需要一把钥匙,如果小明获得了这把钥匙,那小明可以进去,但小红进不去了,因为她没有钥匙,钥匙就一把;那如果是小红获得了这把钥匙,那小红可以进去,而小明就不行了,这就是互斥,同时只有一个人可以去打开这个门。
任务同步:两个任务,一个任务必须等待其中另一个任务完成了,才能继续进行下去。就比如说我必须先写完这篇文章,那么你们才可以看这篇文章,这个就叫任务同步了。

二值信号量示意图:
在这里插入图片描述

1.1.1 二值信号量相关API函数

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

函数描述
xSemaphoreCreateBinary()使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量
xSemaphoreGive()释放信号量(信号量通用)
xSemaphoreGiveFromISR()在中断中释放信号量(信号量通用,除了互斥)
xSemaphoreTake()获取信号量(信号量通用)
xSemaphoreTakeFromISR()在中断中获取信号量(信号量通用,除了互斥)

1.1.2 创建二值信号量函数

函数原型:

SemaphoreHandle_t   xSemaphoreCreateBinary( void ) 
#define	xSemaphoreCreateBinary( )
		xQueueGenericCreate( 1 ,   semSEMAPHORE_QUEUE_ITEM_LENGTH  ,   queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define	semSEMAPHORE_QUEUE_ITEM_LENGTH      ( ( uint8_t ) 0U )

宏定义是队列通用的创建函数。队列长度为 1,队列项大小为 0。

#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创建失败
其他值创建成功返回二值信号量的句柄

1.1.2 释放二值信号量函数

函数原型:

BaseType_t   xSemaphoreGive( xSemaphore ) 
#define	xSemaphoreGive (  xSemaphore  )
		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore )  ,   NULL  ,   semGIVE_BLOCK_TIME  ,   queueSEND_TO_BACK )
#define	semGIVE_BLOCK_TIME                  ( ( TickType_t ) 0U )

宏定义是队列通用的发送函数。要传入的数据为 NULL,阻塞时间是 0,也就是说信号量释放是不支持阻塞的,当信号量超过最大值了,那么就发送错误,这个就是跟队列的发送有区别了,插入位置为尾部插入。

队列结构体中非空闲的队列项数目变量记录了二值信号量是否有资源,也就是计数值。信号量主要操作的是计数值,计数值++就代表是有信号的。

形参描述
xSemaphore要释放的信号量句柄
返回值描述
pdPASS释放信号量成功
errQUEUE_FULL释放信号量失败

1.1.3 获取二值信号量函数

函数原型:

BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime )
形参描述
xSemaphore要获取的信号量句柄
xBlockTime阻塞时间

释放二值信号量是不支持设置阻塞时间的,但是获取二值信号量是支持设置阻塞时间的。

返回值描述
pdTRUE获取信号量成功
pdFALSE超时,获取信号量失败

1.1.4 二值信号量实验

实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用
实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:
start_task:用来创建task1和task2任务
task1:用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量
task2:获取二值信号量,当成功获取后打印提示信息

创建二值信号量
#include "semphr.h"

QueueHandle_t semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    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 task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_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); 	/* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);						/* 死等不会触发 */
        
    }
}

1.2 计数型信号量

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

队列长度限定了计数值的最大值。

计数型信号量适用场合:

  • 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0。
  • 资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目。(类似于停车位)

创建计数型信号量要确定两个值:最大值(队列长度)和初始值。

1.2.1 计数型信号量相关API函数

使用计数型信号量的过程:创建计数型信号量 -> 释放信号量 -> 获取信号量

函数描述
xSemaphoreCreateCounting()使用动态方法创建计数型信号量
xSemaphoreCreateCountingStatic()使用静态方法创建计数型信号量
uxSemaphoreGetCount()获取信号量的计数值

计数型信号量的释放和获取与二值信号量相同!(全部 4 个函数)

1.2.2 计数型信号量创建API函数

#define	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )
		xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 

此函数用于创建一个计数型信号量 。

形参描述
uxMaxCount计数值的最大值限定
uxInitialCount计数值的初始值
返回值描述
NULL创建失败
其他值创建成功返回计数型信号量的句柄

1.2.3 计数型信号量获取API函数

#define uxSemaphoreGetCount( xSemaphore )
		uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

此函数用于获取信号量当前计数值大小。

形参描述
xSemaphore信号量句柄
返回值描述
整数当前信号量的计数值大小

1.2.4 计数型信号量实验

实验目的:学习 FreeRTOS 的计数型信号量相关API函数的使用
实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:
start_task:用来创建task1和task2任务
task1:用于按键扫描,当检测到按键KEY0被按下时,释放计数型信号量
task2:每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值

创建计数型信号量
/******************************************************************************************************/
QueueHandle_t count_semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    count_semphore_handle = xSemaphoreCreateCounting(100 , 0);  /* 创建计数型信号量 */
    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 task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_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);
    }
}

1.3 优先级翻转简介

优先级翻转这个现象主要就是由二值信号量导致的,并且在后面要介绍互斥信号量的时候,都是由优先级翻转牵扯出来的,所以说这个可以说是一个承上启下的作用了,所以这里有必要来好好介绍一下。

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

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

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

  • 举个例子:
    在这里插入图片描述
    • 任务 H 的优先级是最高的,任务 M 的优先级是中等的,任务 L 的优先级是最低的,所以这里有三个任务,并且它们优先级是不一样的,那现在再假设任务 H 第一步执行就是获取二值信号量,然后最后就是释放二值信号量,任务 L 也是同样的,头部是获取信号里,然后尾部是释放信号量,这个任务 H 和任务 L 工作的是一模一样的内容,然后任务 M 它里面就执行简简单单的 printf,没有其他操作。
    • 那么现在呢来假设一下,假设一开始任务 M 以及任务 H 都在阻塞状态,那这时候任务 L 就绪了,那它就可以运行。

高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)

1.3.1 优先级翻转实验

实验目的:在使用二值信号量的时候会存在优先级翻转的问题,本实验通过模拟的方式实现优先级翻转,观察优先级翻转对抢占式内核的影响
实验设计:将设计四个任务:start_task、high_task、 middle_task , low_task

四个任务的功能如下:
start_task:用来创建其它任务
high_task:高优先级任务,会获取二值信号量,获取成功以后打印提示信息,处理完后释放信号量
middle_task:中等优先级任务,简单的应用任务
low_task:低优先级任务,同高优先级一样的操作,不同的是低优先级任务占用信号量的时间久一点

freertos_demo.c
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#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      无
 */
void freertos_demo(void)
{    
    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_ms(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_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 						/* 释放信号量 */
        vTaskDelay(1000);
    }
}

1.4 互斥信号量

互斥信号量其实就是一个拥有优先级继承的二值信号量。

在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!

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

优先级继承用于解决优先级翻转问题。

优先级继承示意图:
在这里插入图片描述
此时任务 L 与任务 H 同等优先级,任务 M 就绪,那么中等优先级的优先级高不过高优先级,所以 L 此时它优先级是最高的,所以任务 M 就算是就绪了也抢占不了任务 L,所以此时任务 L 就可以从头执行到尾。

优先级继承好处:中等优先级任务不能抢占与高优先级任务互斥的低优先级任务,不能浪费低优先级任务的时间了,因为低优先级任务要尽快地执行完,然后把信号量给高优先级任务。

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

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

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

  1. 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级,所以互斥信号量只能用与任务中,不能用于中断服务函数。
  2. 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。(中断是一瞬间的事情)

1.4.1 互斥信号量相关API函数

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

使用流程:创建互斥信号量 ->(task)获取信号量 ->(give)释放信号量

  • 创建互斥信号量函数内部是会调用一个主动释放信号量。所以默认创建互斥信号量的时候信号量就是有效的。
  • 与二值信号量有区别。二值信号量创建时默认没有有效资源的,需要手动释放一次二值信号量。
  • 互斥信号量的释放和获取函数与二值信号量相同,只不过互斥信号量不能用在中断里面,所以它就没有中断级的函数,它只有任务级的释放以及获取了。

1.4.2 创建互斥信号量函数

函数描述
xSemaphoreCreateMutex()使用动态方法创建互斥信号量
xSemaphoreCreateMutexStatic()使用静态方法创建互斥信号量

互斥信号量创建API函数

#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

此函数用于创建互斥信号量

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

1.4.3 互斥信号量实验

实验目的:在优先级翻转实验的基础,加入互斥信号量,解决优先级翻转问题
实验设计:将优先级翻转所用到的信号量函数,修改成互斥信号量即可,通过串口打印提示信息

freertos_demo.c
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#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      无
 */
void freertos_demo(void)
{    
    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");
        delay_ms(3000);
        printf("low_task释放信号量\r\n");
        xSemaphoreGive(mutex_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(mutex_semphore_handle,portMAX_DELAY);
        printf("high_task正在运行!!!\r\n");
        delay_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

2. 总结

在这里插入图片描述

FreeRTOS 信号量是一种同步机制,用于在任务之间共享资源。信号量是一个计数器,用于记录可用资源的数量。当一个任务需要使用共享资源时,它会尝试获取信号量。如果信号量计数器的值为正,则任务可以获取该信号量,并将计数器减一。如果信号量的计数器值为零,则任务将进入等待状态,直到有其他任务释放信号量FreeRTOS 中的信号量有两种类型:二进制信号量和计数信号量。 1. 二进制信号量:二进制信号量只有两种状态:可用和不可用。当计数器值为 0 时,信号量不可用;当计数器值为 1 时,信号量可用。 2. 计数信号量:计数信号量允许计数器的值大于 1。当计数器值为 0 时,信号量不可用;当计数器值大于 0 时,信号量可用。 使用 FreeRTOS 信号量需要调用以下函数: 1. `xSemaphoreCreateBinary()`: 创建二进制信号量。 2. `xSemaphoreCreateCounting()`: 创建计数信号量。 3. `xSemaphoreGive()`: 释放信号量。 4. `xSemaphoreTake()`: 获取信号量。 在使用信号量时,需要注意以下几点: 1. 释放信号量的任务必须先获取该信号量。 2. 在获取信号量时,可以设置超时时间,避免任务一直等待。 3. 在使用计数信号量时,需要注意计数器的上限,避免产生资源浪费或死锁的情况。 4. 在使用二进制信号量时,需要注意任务的优先级,避免优先级反转导致的问题。 总之,FreeRTOS 信号量是一种非常实用的同步机制,可以在多任务系统中解决资源共享的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值