0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FreeRTOS信号量使用教程

CHANBAEK 来源:凌云物联网实验室 作者:郭工 2022-12-19 09:22 次阅读

11.1 信号量说明

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

  • 临界资源的锁机制:用于控制共享资源访问的场景相当于一个上锁机制, 代码只有获得了这个锁的钥匙才能够执行。
  • 多个任务同步机制:用于任务与任务或中断与任务之间的同步。

在编写中断服务函数的时候我们都知道一定要快进快出,中断服务函数里面不能放太多的代码,否则的话会影响的中断的实时性。 裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记,然后在其他的地方根据标记来做具体的处理过程。在使用 RTOS 系统的时候我们就可以借助信号量完成此功能, 当中断发生的时候就释放信号量,中断服务函数不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,那么就开始完成相应的处理,这样做的好处就是中断执行时间非常短。 这个例子就是中断与任务之间使用信号量来完成同步,当然了, 任务与任务之间也可以使用信号量来完成同步。

在实际4G/WiFi等网络应用中,一般最简单的方法就是使用一个任务去查询 4G/WiFi 模块是否有数据到来,当有数据的时候就处理这个网络数据。但这样使用轮询的方式是很浪费CPU 资源的,而且也阻止了其他任务的运行。一种理想的解决方法应该是当没有网络数据的时候网络任务就进入阻塞态,把 CPU 让给其他的任务,当有数据的时候网络任务才去执行。现在使用二值信号量就可以实现这样的功能,任务通过获取信号量来判断是否有网络数据,没有的话就进入阻塞态,而网络中断服务函数通过释放信号量来通知任务以太网外设接收到了网络数据,网络任务可以去提取处理了。网络任务只是在一直的获取二值信号量,它不会释放信号量,而中断服务函数是一直在释放信号量,它不会获取信号量。

接下来,我们以二值信号量为例,讲解信号量实现任务同步的大致流程。其实,二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的。二值信号量通常用于互斥访问或任务同步,它与互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。

  1. 任务获取信号量有效
    如下图所示,任务Task通过调用 xSemaphoreTake() 函数获取信号量,但此时二值信号量无效,所以任务Task进入到阻塞态。
    图片
  2. 中断释放信号量
    当有数据到来产生中断后,在中断服务处理程序中通过调用 xSemaphoreGiveFromISR() 函数释放信号量,此后二值信号量有效。
    图片
  3. 任务获取信号量有效
    由于此时信号量已经有效了,所以任务 Task 获取信号量成功,任务从阻塞态解除,开始执行相关的处理过程。
    图片
  4. 任务再次进入阻塞
    由于任务一般是一个大的死循环,所以在任务做完相关处理以后就会再次调用 xSemaphoreTake() 函数获取信号量,但此时信号量已经失效,因此任务将进入到状态1下阻塞等待信号量有效。

11.2 信号量API说明

11.2.1 获取信号量

不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数获取信号量。

/*
 参数
     xSemaphore: 要获取的信号量句柄。
    xBlockTime: 阻塞时间。         
返回值:
     pdTRUE: 获取信号量成功。
     pdFALSE: 超时,获取信号量失败。
 */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)


/*
说明: 此函数用于在中断服务函数中获取信号量, 此函数用于获取二值信号量和计数型信号量,绝对不能使用此函数来获取互斥信号量。
参数: xSemaphore: 要获取的信号量句柄。
      pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,
              用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
回值: pdPASS: 获取信号量成功。
      pdFALSE: 获取信号量失败。   
 */      
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)

11.2.2 释放信号量

释放信号量分为任务级和中断级。不管是二值信号量、计数型信号量还是互斥信号量,它们都使用以下两个函数释放信号量。

/*    
  参数:xSemaphore: 要释放的信号量句柄。
返回值:
       pdPASS: 释放信号量成功; 
       errQUEUE_FULL: 释放信号量失败。
 */  
BaseType_t xSemaphoreGive( xSemaphore )


/*    
说明: 此函数用于在中断中释放信号量, 此函数只能用来释放二值信号量和计数型信号量,绝对不能用来在中断服务函数中释放互斥信号量。
参数: xSemaphore: 要释放的信号量句柄。
      pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这三个函数来设置的,用户不用进行设置,
               用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
      pdPASS: 释放信号量成功。
      errQUEUE_FULL: 释放信号量失败。
*/      
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)

11.2.3 创建二值信号量

在FreeRTOS中,有两个函数可以创建二值信号量:

/*
说明:  使用此函数创建二值信号量的话信号量所需要的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。
返回值:
      NULL: 二值信号量创建失败。
      其他值: 创建成功的二值信号量的句柄。
 */
SemaphoreHandle_t xSemaphoreCreateBinary( void )


/*
说明:  此函数也是创建二值信号量的,只不过使用此函数创建二值信号量的话信号量所需要的RAM需要由用户来分配
参数:  pxSemaphoreBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
      NULL: 二值信号量创建失败。
      其他值: 创建成功的二值信号量句柄。
 */
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )

11.2.4 创建互斥信号量

在FreeRTOS中,有两个函数可以创建互斥信号量:

/*
说明:此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。
参数:无
返回值:
     NULL: 互斥信号量创建失败。
     其他值: 创建成功的互斥信号量的句柄。
 */
SemaphoreHandle_t xSemaphoreCreateMutex( void )


/*
说明: 此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的RAM 需要由用户来分配
参数: pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
      NULL: 互斥信号量创建失败。
      其他值: 创建成功的互斥信号量的句柄。
 */
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )

11.2.5 创建计数信号量

在FreeRTOS中,有两个函数可以创建计数信号量:

/*
说明: 此函数用于创建一个计数型信号量,所需要的内存通过动态内存管理方法分配。
参数:  
      uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
      uxInitialCount: 计数信号量初始值。
返回值:
      NULL: 计数型信号量创建失败。
      其他值: 计数型信号量创建成功,返回计数型信号量句柄。
 */
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount )


/*
说明: 此函数也是用来创建计数型信号量的,使用此函数创建计数型信号量的时候所需要的内存需要由用户分配。
参数: 
      uxMaxCount: 计数信号量最大计数值,当信号量值等于此值的时候释放信号量就会失败。
      uxInitialCount: 计数信号量初始值。
      pxSemaphoreBuffer: 指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。
返回值:
      NULL: 计数型信号量创建失败。
      其他值: 计数型号量创建成功,返回计数型信号量句柄。
 */
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t * pxSemaphoreBuffer )

11.2.6 递归互斥信号量

递归互斥信号量可以看是一种特殊的互斥信号量,已经获取了信号量的任务就不能再次获取这个互斥量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,次数不限。也就是在同一个任务中可以无限次获取递归互斥信号量,中途不需要释放,而互斥信号量获取一次后就失效,需要再次释放才有效。注意:任务获取多少次递归互斥信号量,就要释放多少次递归信号量。

/*
说明:  动态创建递归互斥信号量
参数:  无
返回值:创建失败NULL,创建成功返回信号量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
#define xSemaphoreCreateRecursiveMutex()            xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )


/*
说明:  静态创建递归互斥信号量
参数:  pxMutexBuffer:指向StaticSemaphore_t类型的变量,用来保存信号量结构体。
返回值: 创建失败NULL,创建成功返回信号量句柄。
*/    
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
#define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore )          xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore )


/*
说明:  释放递归互斥信号量
参数:  xMutex:  信号量句柄
返回值:
       pdPASS:  释放信号量成功
       pdFAIL:  释放信号量失败
*/
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
#define xSemaphoreGiveRecursive( xMutex )    xQueueGiveMutexRecursive( ( xMutex ) )


/*
说明:获取递归互斥信号量
参数:
     xMutex:    信号量句柄
     xBlockTime:阻塞时间
返回值:
     pdPASS:    获取信号量成功
     pdFAIL:    获取信号量失败
*/
xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex, TickType_t xBlockTime );
#define xSemaphoreTakeRecursive( xMutex, xBlockTime )    
        xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

11.3 信号量多任务同步实例

在这里我们将创建两个Led线程,其中蓝色Led线程每隔200ms闪烁一次,而红色Led线程则每隔1200ms闪烁一次。然后我们使用二值信号量的机制,让两个Led线程同步交替闪烁一次。

11.3.1 创建两个线程任务

如下图所示,单击窗格顶部的 “New Thread 按钮,添加两个线程分别命名为 thread_led1 和 thread_led2 ,其它的保持默认配置即可,并重新生成代码。

图片

如下图所示,单击窗口的 “New Object” 按钮,选择 “Binary Semaphore” 添加一个二值信号量,然后修改该信号量的名称为 g_led_semaphore ,并重新生成代码。

图片

11.3.2 修改信号量实例代码

修改 thread_led1_entry.c 源码如下:

#include "thread_led1.h"

/* Led Thread entry function */
/* pvParameters contains TaskHandle_t */
void thread_led1_entry(void *pvParameters)
{
    FSP_PARAMETER_NOT_USED (pvParameters);

    R_BSP_PinAccessEnable(); /* Enable access to the PFS registers. */

    /* TODO: add your own code here */
    while (1)
    {
        xSemaphoreTake( g_led_semaphore, portMAX_DELAY );

        R_BSP_PinWrite(LedRed, BSP_IO_LEVEL_HIGH);
        vTaskDelay (600);
        R_BSP_PinWrite(LedRed, BSP_IO_LEVEL_LOW);
        vTaskDelay (600);
    }
}
  • 如果不加信号量操作部分代码,红色Led将每隔1200ms闪烁一次;
  • 这里调用了xSemaphoreTake() 函数来获取信号量 g_led_semaphore,如果 thread_led2 线程没有运行并释放信号量的话,它将会阻塞;
  • xSemaphoreTake() 函数在获取到信号之后,将会让红色Led闪烁一次;
  • 之后该线程将会再次调用了xSemaphoreTake() 函数,等待 thread_led2 线程释放信号;

修改 thread_led2_entry.c 源码如下:

#include "thread_led2.h"

/* Led Thread entry function */
/* pvParameters contains TaskHandle_t */
void thread_led2_entry(void *pvParameters)
{
    FSP_PARAMETER_NOT_USED (pvParameters);

    R_BSP_PinAccessEnable(); /* Enable access to the PFS registers. */

    /* TODO: add your own code here */
    while (1)
    {
        if( pdTRUE == xSemaphoreGive( g_led_semaphore) )
        {
            R_BSP_PinWrite(LedBlue, BSP_IO_LEVEL_HIGH);
            vTaskDelay (100);
            R_BSP_PinWrite(LedBlue, BSP_IO_LEVEL_LOW);
            vTaskDelay (100);
        }
    }
}
  • 如果不加信号量操作部分代码,蓝色Led将每隔200ms闪烁一次;
  • 这里调用了xSemaphoreGive() 函数释放信号量 g_led_semaphore,用来同步通知 thread_led1 线程运行;
  • xSemaphoreGive() 函数在释放信号之后,如果 thread_led1 没有来得及获取信号量的话,则会返回pdFALSE;
  • 这样,如果 thread_led1 没有运行的话,蓝色Led也不会闪烁了,从而实现了两个线程中的Led同步交替闪烁一次效果了;
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 操作系统
    +关注

    关注

    37

    文章

    6284

    浏览量

    121874
  • FreeRTOS
    +关注

    关注

    12

    文章

    473

    浏览量

    61347
  • 信号量
    +关注

    关注

    0

    文章

    53

    浏览量

    8257
收藏 人收藏

    评论

    相关推荐

    实时操作系统FreeRTOS信号量应用

    二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常相似,但还是有细微差别,互斥信号量拥有优先级继承机制,二值信号没有。因此二值信量
    的头像 发表于 06-08 09:24 3261次阅读
    实时操作系统<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>应用

    FreeRTOS信号量的使用与实例

    在嵌入式系统中,任务管理是一个重要的部分,它涉及到任务之间的通信和同步,信号量,队列,互斥锁和事件标志组等概念。本文将以 FreeRTOS 为例,详细讲解这些内容。
    的头像 发表于 12-12 15:25 625次阅读

    转:freeRTOS信号量学习

    信号量同样是RTOS学习中很重要的一节,信号量可以用在共享资源或者同步任务中,对执行权的控制,谁拥有信号量谁拥有执行权,在freeRTOS信号量
    发表于 08-12 18:29

    【NUCLEO-F412ZG试用体验】FreeRTOS_信号量实现任务和中断的同步

    上一期是用事件标志组实现的任务和中断同步,使用信号量一样可以实现任务之间以及任务和中断的同步。首先说明,本例程参考了官方例程。实验现象:1、3个LED闪烁。2、按键按下,发送相关信息。步骤:1、使能信号量。2、编写测试代码。新建信号量
    发表于 01-03 19:24

    FreeRTOS信号量不能删除的原因?

    定义了两个任务,第一个任务是使用串口收到Task字符则删除信号量,但是经过测试,并不能删除,发送两次Task字符之后,程序回来死在信号量删除部分,求解答为什么?
    发表于 07-27 08:00

    【转载】AT32 FreeRTOS应用笔记

    ..................................................52FreeRTOS 信号量 ......................................59什么是信号量
    发表于 08-16 11:56

    FreeRTOS信号量介绍

    FreeRTOS信号量 & ESP32实战阅读建议:有一定操作系统基础知识。FreeRTOS信号量1. 二值信号量  二值
    发表于 01-27 07:28

    FreeRTOS信号量的相关资料推荐

    一、互斥信号量简介互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量
    发表于 02-28 13:39

    McuXpresso的静态库和IRQ处理程序问题求解

    我想制作一个静态库来处理一些 IRQ 和使用 FreeRTOS 信号量来处理具体处理的主程序。有些事情逃避了我,我不明白如何正确地做到这一点。我试图制作一个静态库,但它有他的 SDK 和外围设备定义。不调用 IRQ Handler 和 lib 中的初始化。解决此问题的最佳
    发表于 03-22 08:18

    FreeRTOS信号量 & ESP32实战

    FreeRTOS信号量 & ESP32实战阅读建议:有一定操作系统基础知识。FreeRTOS信号量1. 二值信号量  二值
    发表于 12-03 18:06 1次下载
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b> & ESP32实战

    FreeRTOS高级篇6---FreeRTOS信号量分析

    FreeRTOS信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量
    发表于 01-26 17:39 7次下载
    <b class='flag-5'>FreeRTOS</b>高级篇6---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>分析

    FreeRTOS系列第20篇---FreeRTOS信号量API函数

    FreeRTOS信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量
    发表于 01-26 17:44 4次下载
    <b class='flag-5'>FreeRTOS</b>系列第20篇---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>API函数

    FreeRTOS信号量计数示例分享

    信号量通常用来协调对资源的访问,其中信号计数会初始化为可用资源的数目。
    的头像 发表于 09-15 11:45 700次阅读

    在Arduino IDE中使用FreeRTOS信号量

    电子发烧友网站提供《在Arduino IDE中使用FreeRTOS信号量.zip》资料免费下载
    发表于 01-04 10:18 0次下载
    在Arduino IDE中使用<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>

    FreeRTOS信号量介绍

    1、信号量用于控制对共享资源的访问 举一个很常见的例子,某个停车场有100个停车位,这 100 个停车位大家都可以用,对于大家来说这 100 个停车位就是共享资源。假设现在这个停车场正常运行,你要把
    的头像 发表于 07-06 17:09 400次阅读