从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解

目录

一、创建信号量函数:

1、创建二值信号量 xSemaphoreCreateBinary():

2. 创建计数信号量 xSemaphoreCreateCounting():

二、信号量删除函数:

1、信号量删除函数 vSemaphoreDelete():

三、信号量释放函数:

1、xSemaphoreGive()(任务)

2. xSemaphoreGiveFromISR()(中断)

四、信号量获取函数:

1. xSemaphoreTake()(任务)

2. xSemaphoreTakeFromISR()(中断)

 


一、创建信号量函数:

1、创建二值信号量 xSemaphoreCreateBinary():

       xSemaphoreCreateBinary()用于创建一个二值信号量, 并返回一个句柄其实二值信号量和互斥量都共同使用一个类型 SemaphoreHandle_t 的句柄, 该句柄的原型是一个 void 型的指针。使用该函数创建的二值信号量是空的, 在使用函数 xSemaphoreTake()获取之前必须先调用函数 xSemaphoreGive() 释放后才可以获取 。 
       二值信号量的释放和获取都是通过操作队列结控制块构体成员uxMessageWaiting来实现的,它表示信号量中当前可用的信号量个数。在信号量创建之后,变量uxMessageWaiting的值为0,这说明当前信号量处于无效状态如果为正值,就表明有效。见下图(图文来自野火论坛)

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解_第1张图片

SemaphoreHandle_t xSemaphore = NULL;
void vATask( void * pvParameters )
{
/* 尝试创建一个信号量 */
xSemaphore = xSemaphoreCreateBinary();
if ( xSemaphore == NULL ) {
/* 内存不足,创建失败 */
} else {
/* 信号量现在可以使用,句柄存在变量 xSemaphore 中
这个时候还不能调用函数 xSemaphoreTake()来获取信号量
因为使用 xSemaphoreCreateBinary()函数创建的信号量是空的
在第一次获取之前必须先调用函数 xSemaphoreGive()先提交*/
}
}

2. 创建计数信号量 xSemaphoreCreateCounting():

       间接调用xQueueGenericCreate()函数进行创建

函数原型

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,

 

                                                                                 UBaseType_t uxInitialCount);

功能

创建一个计数信号量。

参数

uxMaxCount

计数信号量的最大值,当达到这个值的时候,信号量不能再被释放。

uxInitialCount

创建计数信号量的初始值。

返回值

如果创建成功则返回一个计数信号量句柄,用于访问创建的计数信号量

 

如果创建不成功则返回NULL

 

 

如果我们创建一个最大计数值为 5,并且默认有效的可用信号量个数为 5 的计数信号量,那么计数信号量创建成功的示意图具体见下图(图文来自野火论坛)

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解_第2张图片

void vATask( void * pvParameters )
{
SemaphoreHandle_t xSemaphore;
/* 创建一个计数信号量, 用于事件计数 */
xSemaphore = xSemaphoreCreateCounting( 5, 5 );
if ( xSemaphore != NULL ) {
/* 计数信号量创建成功 */
}
}

二、信号量删除函数:

1、信号量删除函数 vSemaphoreDelete():

        vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递归互斥量。 如果有任务阻塞在该信号量上,那么不要删除该信号量。

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解_第3张图片

三、信号量释放函数:

1、xSemaphoreGive()(任务)

① 信号量释放就是让信号量变成有效状态。

② 无论是二值信号量还是计数信号量,都要注意可用信号量可用个数的范围,当用作二值信号量的时候,必须确保其可用值在0~1范围内;而用作计数信号量的话,其范围是由用户在创建时指定uxMaxCount,其最大可用信号量不允许超出uxMaxCount。

③ xSemaphoreGive()是一个用于释放信号量的宏,真正的实现过程是调用消息队列通用发送函数xQueueGenericSend()

④ 此外该函数不能在中断中使用

static void Send_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
while (1) {
/* K1 被按下 */
if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {
xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量
if ( xReturn == pdTRUE )
printf("BinarySem_Handle 二值信号量释放成功!\r\n");
else
printf("BinarySem_Handle 二值信号量释放失败!\r\n");
}
/* K2 被按下 */
if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {
xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量
if ( xReturn == pdTRUE )
printf("BinarySem_Handle 二值信号量释放成功!\r\n");
else
printf("BinarySem_Handle 二值信号量释放失败!\r\n");
}
vTaskDelay(20);
}
}

2. xSemaphoreGiveFromISR()(中断)

        用于释放一个信号量,带中断保护被释放的信号量可以是二进制信号量和计数信号量。和普通版本的释放信号量 API 函数有些许不同,它不能释放互斥量,这是因为互斥量不可以在中断中使用, 互斥量的优先级继承机制只能在任务中起作用,而在中断中毫无意义。带中断保护的信号量释放其实也是一个宏,真正调用的函数是 xQueueGiveFromISR (),
        如果可用信号量未满,控制块结构体成员uxMessageWaiting就会加1,然后判断是否有阻塞的任务,如果有的话就会恢复阻塞的任务,然后返回成功信息(pdPASS),如果恢复的任务优先级比当前任务优先级高,那么在退出中断要进行任务切换一次;如果信号量,则返回错误代码(err_QUEUE_FULL),表示信号量

void vTestISR( void )
{
BaseType_t pxHigherPriorityTaskWoken;
uint32_t ulReturn;
/* 进入临界段,临界段可以嵌套 */
ulReturn = taskENTER_CRITICAL_FROM_ISR();
/* 判断是否产生中断 */
{
/* 如果产生中断,清除中断标志位 */
//释放二值信号量,发送接收到新数据标志,供前台程序查询
xSemaphoreGiveFromISR(BinarySem_Handle,&
pxHigherPriorityTaskWoken);
//如果需要的话进行一次任务切换,系统会判断是否需要进行切换
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
}
/* 退出临界段 */
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}

四、信号量获取函数:

        当信号量有效的时候,任务才能获取信号量,当任务获取了某个信号量的时候,该信号量的可用个数就减一,当它减到0的时候,任务就无法再获取了,并且获取的任务会进入阻塞态。

1. xSemaphoreTake()(任务)

        xSemaphoreTake()函数用于获取信号量,不带中断保护。获取的信号量对象可以是二值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个 API 函数获取。其实获取信号量是一个宏,真正调用的函数是 xQueueGenericReceive ()。 该宏不能在中断使用。

函数原型

#define    xSemaphoreTake( xSemaphore, xBlockTime

 

     xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ),

 

             NULL, ( xBlockTime ), pdFALSE )

功能

获取一个信号量,可以是二值信号量、计数信号量、互斥量。

参数

xSemaphore

信号量句柄。

xBlockTime

等待信号量可用的最大超时时间,单位为tick(即系统节拍周期)。如果

 

 INCLUDE_vTaskSuspend定义为1且形参xTicksToWait 设置为portMAX_DELAY

 

任务将一直阻塞在该信号量上。

返回值

获取成功则返回pdTRUE,在指定的超时时间中没有获取成功则返回errQUEUE_EMPTY

       从宏定义可以看出释放信号量实际上是一次消息出队操作,阻塞时间由用户指定xBlockTime,当有任务试图获取信号量的时候,当且仅信号量有效的时候,任务才能获取到信号量。如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量效。当其它任务或中断释放了有效的信号量,该任务将自动由阻塞态转移为就绪态。当任务等待的时间超过了指定的阻塞时间,即使信量中还是没有可用信号量,任务也会自动从阻塞态转移为就绪态

       通过前面消息队列出队过程分析,我们可以将获取一个信号量的过程简化:如果有可用信号量,控制块结构体成员uxMessageWaiting就减1,然后返回获取成功信息(pdPASS);如果信号量无效并且阻塞时间为0,则返回错误代码(errQUEUE_EMPTY);如果信号量无并且用户指定了阻塞时间,则任务会因为等待信号量而进入阻塞状态,任务会被挂接到延时列表中。

static void Receive_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
while (1) {
//获取二值信号量 xSemaphore,没获取到则一直等待
xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */
portMAX_DELAY); /* 等待时间 */
if (pdTRUE == xReturn)
printf("BinarySem_Handle 二值信号量获取成功!\n\n");
LED1_TOGGLE;
}
}

2. xSemaphoreTakeFromISR()(中断)

       xSemaphoreTakeFromISR()是函数xSemaphoreTake()的中断版本,用于获取信号量,是一个不带阻塞机制获取信号量的函数,获取对象必须由是已经创建的信号量,信号量类型可以是二值信号量和计数信号量,它与xSemaphoreTake()函数不同,它不能用于获取互斥

从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解_第4张图片

 

你可能感兴趣的:(从0到1攻破FreeRTOS)