14.1 二值信号量

1 信号量简介

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。

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

1)首先判断停车场是否还有空车位(判断信号量是否还有资源);

2)停车场正好有空车位(信号量有资源),那么就可以直接将车开入空车位进行停车(获取信号量成功);

3)停车场已经没有空车位了(信号量没有资源),那么这个人可以选择不停车(获取信号量失败);也可以选择等待(任务阻塞),其他人将车开出停车场(释放信号量资源),然后再将车停入空车位。

空车位:信号量资源数(计数值)

让出占用车位:释放信号量(计数值++)

占用车位:获取信号量(计数值--)

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

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

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

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

信号量:用于传递状态。 

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

队列与信号量的对比:

队列信号量

可以容纳多个数据;

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

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

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

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

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

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

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

获取信号量:计数值--,

当没有时,可阻塞

2 二值信号量

2.1 二值信号量概念

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

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

互斥访问:比如说有一扇门,但是只有一把钥匙,当小红有钥匙,则小红可以进门,而小明则进不去;反之,当小明有钥匙,则小明可以进门,而小红则进不去。

任务同步:两个任务必须等待其中一个任务完成了,另外一个任务才能进行下去。例如老师必须先录完视频,我们才能够观看。

2.2 二值信号量相关API函数

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

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

1)创建二值信号量函数

#define xSemaphoreCreateBinary()    xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
//xQueueGenericCreate 返回值如果为NULL则表示创建失败,其他值则表示创建成功

#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 )  //递归互斥信号量

2)释放二值信号量

#define xSemaphoreGive( xSemaphore )    
        xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME,         
        queueSEND_TO_BACK )
//形参xSemaphore,表示要释放的信号量句柄
//返回值pdPASS表示释放信号量成功,errQUEUE_FULL表示释放信号量失败

3)获取二值信号量

#define xSemaphoreTake( xSemaphore, xBlockTime )    
        xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
//形参xSemaphore表示要获取的型号量句柄,xBlockTime表示阻塞时间
//返回值pdTRUE表示获取信号量成功,pdFALSE表示超时,获取信号量失败

2.3 二值信号量的实验 

实验设计:将设计三个任务:start_task、task1、task2

三个任务的功能如下:

stasrt_task:用来创建task1和task2任务

task1:用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量

task2:获取二值型号量,当成功获取后打印提示信息

#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"
/*******************************88FreeRTOS**********************************************/
#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    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_handler;
/***************************************************************************************/

/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{   		
	  //动态创建二值信号量
		semphore_handler = xSemaphoreCreateBinary();
	  
	  if(semphore_handler != 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 == KEY0_PRES)
			{
				if(semphore_handler != NULL) //判断二值信号量创建是否成功
				{
					err = xSemaphoreGive(semphore_handler);
					if(err == pdPASS)  //判断是否成功释放二值信号量
					{
						printf("二值信号量释放成功");
					}
					else
					{
						printf("二值信号量释放失败");
					}
				}
			}
			vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量*/
void task2( void * pvParameters )
{
    uint32_t i = 0;
    while(1)
    {
		    xSemaphoreTake(semphore_handler,portMAX_DELAY);//获取信号量并死等
			printf("获取信号量成功:%d!!!\r\n",++i);
			 
		}
}

实验结果:

 由于task2的优先级最高,因此会先执行task2获取信号量,由于获取不到信号量,因此会死等,除非按下KEY0,释放信号量,才会打印“获取信号量成功!!!”。当按下KEY0,会马上显示获取信号量成功,然后才会显示信号量释放成功。这是因为task2的抢占优先级高,只有打印完Task2后,task2又变成阻塞态才会返回task1打印信号量释放成功。

若将task2修改成如下模式:

/* 任务二,获取二值信号量*/
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t  err;
    while(1)
    {
			err =  xSemaphoreTake(semphore_handler,1000);//获取信号量并死等
			if(err == pdPASS)  //判断是否成功释放二值信号量
			{
				printf("获取信号量成功!!!\r\n",);
			}
			else
			{
				printf("已超时%d\r\n",++i);
			}
			 
		}
}

实验结果:

注意:没有获取成功,只是过了1s打印一次而已

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值