转载自 https://blog.csdn.net/zhoutaopower/article/details/107359095

在裸机编程中这样使用过一个变量:用于标记某个事件是否发生,或者标志一下某个东西是否正在被使用,如果是被占用了,或者没有发生,我们就不对它进行操作。

信号量  Semaphore

FreeRTOS 中使用信号量来做同步,信号量可以在任务中使用作为任务与任务间的同步,也可以在中断中使用(带 FromISR 的版本)中断与任务间的同步;

针对不同的应用场景,信号量分为两种:

1、二值信号量;

2、计数信号量;

二值信号量 (Binary Semaphores)

为什么叫二值信号量?

因为信号量资源被获取了,信号量值就是0;信号量资源被释放,信号量值就是1,把这种只有0和1两种情况的信号量称之为二值信号量。

在多任务系统中,经常使用到二值信号量。某个任务需要等待一个标记,在任务中轮询这个标记有没有被置位,很消耗CPU,更好的做法是让任务大部分时间处于阻塞状态,让其他任务执行,等到某些事件发生后,该任务才被唤醒去执行,可以使用二值信号量来实现这种同步。任务执行完毕不用归还信号量。

二值信号量在任务与中断同步的应用场景:

比如:在串口接收中,我们不知道啥时候有数据发过来,有一个任务是专门做接收这些数据并处理的。总不能在任务中每时每刻都在查询数据有没有发过来吧(也不是不能,只是这样太傻,浪费CPU资源)。这种情况下使用二值信号量是一个很好的办法,当没有数据到来的时候,任务就进入阻塞态,不参与任务的调度,等到数据到来了,释放一个二值信号量,任务就立即从阻塞态解除,进入就绪态,然后运行的时候处理数据。这样资源利用率更高。

二值信号量的运作机制

创建信号量时: 系统会为创建的信号量对象分配内存,并把可用的信号量初始化为用户自定义的个数,显然二值信号量的最大可用信号量个数是1.

获取二值信号量:任何任务都可以从创建的二值信号量资源中获取一个二值信号量,若获取成功则返回正确,否则就根据设置的阻塞时间来等待其他的任务和中断释放信号量,等待的时间,任务处于阻塞态,任务将被挂到该信号量的阻塞等待列表中。

          

常用信号量接口函数

直接到任务通知代替二值信号量,效果更好,更快,更省内存

上面的情况,可以将二值信号量理解为长度为 1 的 Queue,实际上,它的实现,也是用 Queue;

操作二值信号量,分为两个行为:Give 和 Take:

1、Give:往二值信号量写 1;

2、Take:获取二值信号量;

上面的例子可以理解为下面的顺序:

首先初始化一个二值信号量,任务尝试获取二值信号量,但是获取失败,使得任务进入 Blocked 状态:

中断来了,执行 ISR,往这个二值信号量写 1;

此刻,等待在这个二值信号量上的任务会被解除阻塞,投入运行:

任务获取到二值信号量,开始执行任务:

执行完毕后,再次去 Take 失败,再次进入 Blocked:

1.2、APIs

  1.2.1 定义和产生一个二值信号量

  osSemaphoreDef(myBinarySem01); /*definition of myBinarySem01 */
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);/*creation of myBinarySem01*/

1.2.1、xSemaphoreCreateBinary

应用代码使用 xSemaphoreCreateBinary 来创建二值信号量:

SemaphoreHandle_t xSemaphoreCreateBinary( void );

有一个返回值:

Return:创建成功返回一个信号量的句柄,失败返回 NULL;

1.2.2、xSemaphoreTake / xSemaphoreTakeFromISR

应用代码使用 xSemaphoreTake() 来获取一个二值信号量:

注意:不要在 ISR 中使用 xSemaphoreTake 应该使用对应的 xSemaphoreTakeFromISR;

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

有两个入参,一个返回值:

xSemaphore:创建信号量的句柄;

xTicksToWait:如果获取不到信号量,最大阻塞的时间;如果设置为 0,那么立即返回(不阻塞),如果配置为 portMAX_DELAY,则无限制等待;

Return:返回 pdPASS 代表正常获取到信号量,返回 pdFALSE 代表获取失败;

1.2.3、xSemaphoreGive / xSemaphoreGiveFromISR

应用代码使用 xSemaphoreGive() 来设置一个二值信号量:

注意:不要在 ISR 中使用 xSemaphoreGive 应该使用对应的 xSemaphoreGiveFromISR;

  1.  
    BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
  2.  
    BaseType_t *pxHigherPriorityTaskWoken );

两个入参,一个返回值:

xSemaphore:信号量的句柄;

pxHigherPriorityTaskWoken :如果该信号量会导致一个比当前任务优先级更高的任务解除阻塞,那么返回 pdTRUE;

Return:返回是否设置信号量成功,成功返回 pdTRUE,否则返回 pdFALSE;

Example:

有一个周期性的任务,每隔 500ms 定时产生一个软件中断:

/* The number of the software interrupt used in this example. The code shown is from
the Windows project, where numbers 0 to 2 are used by the FreeRTOS Windows port
itself, so 3 is the first number available to the application. */ #define mainINTERRUPT_NUMBER 3
static void vPeriodicTask( void *pvParameters )
{
const TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Block until it is time to generate the software interrupt again. */
vTaskDelay( xDelay500ms );
/* Generate the interrupt, printing a message both before and after
the interrupt has been generated, so the sequence of execution is evident
from the output.
The syntax used to generate a software interrupt is dependent on the
FreeRTOS port being used. The syntax used below can only be used with
the FreeRTOS Windows port, in which such interrupts are only simulated. */
vPrintString( "Periodic task - About to generate an interrupt.\r\n" );
vPortGenerateSimulatedInterrupt( mainINTERRUPT_NUMBER );
vPrintString( "Periodic task - Interrupt generated.\r\n\r\n\r\n" );
}
}

下面是一个期望获取二值信号量的任务:

static void vHandlerTask( void *pvParameters )
{
/* As per most tasks, this task is implemented within an infinite loop. */
for( ;; )
{
/* Use the semaphore to wait for the event. The semaphore was created
before the scheduler was started, so before this task ran for the first
time. The task blocks indefinitely, meaning this function call will only
return once the semaphore has been successfully obtained - so there is
no need to check the value returned by xSemaphoreTake(). */
xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );
/* To get here the event must have occurred. Process the event (in this
Case, just print out a message). */
vPrintString( "Handler task - Processing event.\r\n" );
}
}

接下来是产生中断的那个 ISR,根据传入的 pxHigherPriorityTaskWoken 来判断是否要进行上下文切换:

static uint32_t ulExampleInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken; /* The xHigherPriorityTaskWoken parameter must be initialized to pdFALSE as
it will get set to pdTRUE inside the interrupt safe API function if a
context switch is required. */
xHigherPriorityTaskWoken = pdFALSE; /* 'Give' the semaphore to unblock the task, passing in the address of
xHigherPriorityTaskWoken as the interrupt safe API function's
pxHigherPriorityTaskWoken parameter. */
xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken ); /* Pass the xHigherPriorityTaskWoken value into portYIELD_FROM_ISR(). If
xHigherPriorityTaskWoken was set to pdTRUE inside xSemaphoreGiveFromISR()
then calling portYIELD_FROM_ISR() will request a context switch. If
xHigherPriorityTaskWoken is still pdFALSE then calling
portYIELD_FROM_ISR() will have no effect. Unlike most FreeRTOS ports, the
Windows port requires the ISR to return a value - the return statement
is inside the Windows version of portYIELD_FROM_ISR(). */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

主函数为:

int main( void )
{
/* Before a semaphore is used it must be explicitly created. In this example
a binary semaphore is created. */
xBinarySemaphore = xSemaphoreCreateBinary();
/* Check the semaphore was created successfully. */
if( xBinarySemaphore != NULL )
{
/* Create the 'handler' task, which is the task to which interrupt
processing is deferred. This is the task that will be synchronized with
the interrupt. The handler task is created with a high priority to ensure
it runs immediately after the interrupt exits. In this case a priority of
3 is chosen. */
xTaskCreate( vHandlerTask, "Handler", 1000, NULL, 3, NULL );
/* Create the task that will periodically generate a software interrupt.
This is created with a priority below the handler task to ensure it will
get preempted each time the handler task exits the Blocked state. */
xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, 1, NULL );
/* Install the handler for the software interrupt. The syntax necessary
to do this is dependent on the FreeRTOS port being used. The syntax
shown here can only be used with the FreeRTOS windows port, where such
interrupts are only simulated. */
vPortSetInterruptHandler( mainINTERRUPT_NUMBER, ulExampleInterruptHandler );
/* Start the scheduler so the created tasks start executing. */
vTaskStartScheduler();
}
/* As normal, the following line should never be reached. */
for( ;; );
}

由于 vHandlerTask 优先级为最高,所以他会先运行,并阻塞在二值信号量的获取上;

vHandlerTask 进入阻塞后,vPeriodicTask 会周期性的去拉一个中断,导致进入 ISR;

在 ISR 中设置了信号量,导致 vHandlerTask 被解除阻塞,进入运行,抢占 vPeriodicTask;

vHandlerTask 运行完后,再次进入阻塞;

上述的场景中,二值信号量是可以胜任的,但是试想,真实的系统中,IRQ 是随时都可能来的,如果一种情况下,IRQ 来的比较频繁,当 Task 正在获得二值信号量处理的时候,又连续来了 2 个 IRQ,由于二值信号量只能存储一次事件,那么必然导致事件的丢失,如下所示:

此刻二值信号量显得有点力不从心,接下来就看计数信号量的了

2、Counting Semaphores

2.1、Usage

二值信号量可以看成是只有一个长度的 Queue,计数信号量就是多个长度的 Queue(只关心长度,不关心 Queue 内容);

要使用计数信号量,需要配置 configUSE_COUNTING_SEMAPHORES 为 1;

计数信号量主要可以用作如下两个方面:

1、事件计数:这种场景下,事件通过 Give 来往计数信号量中记录事件发生的次数,另一端的 Task 通过 Take 来进行每一次事件的处理;一般的,信号量的计数被初始化为 0;

2、资源管理:这种场景下,信号量代表可用资源的数目,一般的,这种情况将信号量初始化为一个资源的数目,任务每次获取资源,都将资源减一;如果信号量为 0 说明没有可用的资源了;一旦任务完成,便通过 Give 来释放资源,增加信号量的计数;

同样是之前的例子,当 IRQ 来的过快,任务来不及处理完的情况下,多余的 Event 会在计数信号量中保存,直到任务完成,再次进入阻塞:

2.2、APIs

2.2.1、xSemaphoreCreateCounting

创建一个计数信号量使用 xSemaphoreCreateCounting 接口:

  1.  
    SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount,
  2.  
    UBaseType_t uxInitialCount );

两个入参,一个返回值:

uxMaxCount:指的是信号量的最大计数个数;

uxInitialCount:被初始化的个数;

Return:如果成功,返回信号量的句柄,否则返回 NULL;

其余的 Get 和 Take 和二值信号量一样,不在赘述;

FreeRTOS --(15)信号量之概述的更多相关文章

  1. FreeRTOS 计数信号量

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节开始讲解 FreeRTOS 任务间的同步和资源共享机制,计数信号量. FreeRTOS 中计数信号量的 ...

  2. 嵌入式系统FreeRTOS — 互斥信号量

    互斥信号量可以在资源保护的时候很有帮助.用于控制在两个或多个任务间访问共享资源.任务1里面用互斥,那么任务2只能等任务1访问完再访问同一个变量. 比如全局变量double gADC_value[CH_ ...

  3. stm32中使用cubemx配置freertos的信号量大小

    在配置freertos的情况下,cubemx会自动计算每个任务.信号,队列和软件定时器的使用堆栈大小,因此要合理规划 信号量默认是88byte 任务根据设定来计算,我默认配置是128,则最终是624b ...

  4. FreeRTOS的信号量和互斥量

    1. 理解如下,言简意赅的说,信号量解决同步,互斥量解决竞争. 信号量用于同步,主要任务间和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁. 互斥量具有优先级继承,信 ...

  5. FreeRTOS互斥信号量

    API函数 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define xSemaphoreCreateMutex() xQueueCreateMutex ...

  6. FreeRTOS 任务计数信号量,任务二值信号量,任务事件标志组,任务消息邮箱

    以下基础内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 计数信号量的另一种实现方式----基于任务通知(Task Not ...

  7. FreeRTOS 二值信号量,互斥信号量,递归互斥信号量

    以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量. 二值信号量是计数信号量的一种特殊形式 ...

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

    FreeRTOS的信号量包括二进制信号量.计数信号量.相互排斥信号量(以后简称相互排斥量)和递归相互排斥信号量(以后简称递归相互排斥量).我们能够把相互排斥量和递归相互排斥量看成特殊的信号量. 信号量 ...

  9. FreeRTOS相关转载-(朱工的专栏)

    FreeRTOS系列第1篇---为什么选择FreeRTOS? 1.为什么学习RTOS? 作为基于ARM7.Cortex-M3硬件开发的嵌入式工程师,我一直反对使用RTOS.不仅因为不恰当的使用RTOS ...

随机推荐

  1. 99%的人都搞错了的java方法区存储内容,通过可视化工具HSDB和代码示例一次就弄明白了

    https://zhuanlan.zhihu.com/p/269134063  番茄番茄我是西瓜 那是我日夜思念深深爱着的人啊~ 已关注   6 人赞同了该文章 前言 本篇是java内存区域管理系列教 ...

  2. 如何选择 Linux 操作系统版本?

    一般来讲,桌面用户首选 Ubuntu :服务器首选 RHEL 或 CentOS ,两者中首选 CentOS .根据具体要求:· 安全性要求较高,则选择 Debian 或者 FreeBSD .· 需要使 ...

  3. 为什么以iPhone6为标准的设计稿的尺寸是以750px宽度来设计的呢?

    iPhone6的满屏宽度是375px,而iPhone6采用的视网膜屏的物理像素是满屏宽度的2倍,也就是dpr(设备像素比)为2, 并且设计师所用的PS设计软件分辨率和像素关系是1:1.所以为了做出的清 ...

  4. Java并发机制(7)--线程池ThreadPoolExecutor的使用

    Java并发编程:线程池的使用整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3932921.html 1.什么是线程池,为什么要使用线程池: 1.1. ...

  5. 什么是 Spring 配置文件?

    Spring 配置文件是 XML 文件.该文件主要包含类信息.它描述了这些类是如何 配置以及相互引入的.但是,XML 配置文件冗长且更加干净.如果没有正确规划 和编写,那么在大项目中管理变得非常困难.

  6. hackgame2018_签到

    hackgame2018_签到 进入题目得到如下提示 尝试提交 发现这个输入框长度做了限制我们将前端js修改一下 然后提交以下这样就发现了flag这题比较简单--

  7. word中怎么加入endnote的插件

    首先,打开Microsoft Word 2010,然后点击文件菜单,在弹出的项目中点击选项. 2 弹出Word选项对话框,在左侧导航处点击"加载项"按钮,如图. 3 在右侧内容窗口 ...

  8. led指示灯电路图大全(八款led指示灯电路设计原理图详解)

    led指示灯电路图大全(八款led指示灯电路设计原理图详解) led指示灯电路图(一) 图1所示电路中只有两个元件,R选用1/6--1/8W碳膜电阻或金属膜电阻,阻值在1--300K之间. Ne为氖泡 ...

  9. css3中什么时候用transition什么时候用animation实现动画

    在css3中transition和animation都可以实现动画效果,但是我们什么时候用transition,什么时候用animation. 当有事件触发动画的时候我们就用transition.比如 ...

  10. python-班级人员信息统计

    输入a,b班的名单,并进行如下统计. 输入格式: 第1行::a班名单,一串字符串,每个字符代表一个学生,无空格,可能有重复字符.第2行::b班名单,一串字符串,每个学生名称以1个或多个空格分隔,可能有 ...