首页 > 编程知识 正文

什么是信号量,freertos信号量实验

时间:2023-05-05 03:20:22 阅读:113435 作者:664

本章介绍FreeRTOS任务之间的同步和资源共享机制,以及二进制信号量。 二进制信号量是计数信号量的一种特殊形式,即共享资源为1。

FreeRTOS分别提供二值信号量和计数信号量,其中二值信号量可理解为计数

信号是一种特殊形式,即初始化为只有一个资源可用,但自由RTOS为两者提供了API

另外一方面,RTX、uCOS-II、III等函数仅提供一个信号功能,如果设定不同初始值,则能够分别实现二值信道

信号量和计数信号量。 当然,FreeRTOS使用计数信号量也能够实现同样的效果。 另外,为什么叫二值信号

量是多少? 由于获取了信号量资源,所以信号量的值为0,当释放信号量资源时,信号量的值为1,并且只有0

1和2种情况下的信号量称为二值信号量。

函数xSemaphoreCreateBinary

函数原型:

semaphore handle _ txsemaphorecreatebinary (void ) )。

函数说明:

函数xSemaphoreCreateBinary用于创建二值信号量。

如果FreeRTOSConfig.h文件的heap大小不同,则返回值返回二进制信号量句柄(如果成功创建)

如果无法为此二进制信号量提供足够的空间,则返回NULL。

互斥信号量(Mutex或Mutual Exclusion的缩写),是FreeRTOS的重要资源共享机制。

建议初学者先学习前两个信号量,然后再学习本章的排他信号量。

独占信号的主要作用是对资源的独占访问,也有使用二值信号实现独占访问的功能,但相互之间

排斥信号量和二值信号量存在差异。 首先,让我们举一个二进制信号量资源独占(独占访问)的例子

大家都有印象,引出进一步说明的互斥信号量。

执行条件:

在两个任务Task1和Task2上运行串行打印函数printf。 在这里,我们用二进制信号量实现对函数

printf的互斥访问。 如果不独占访问函数printf,串行打印容易发生乱码。

为了通过计数信号量实现二值信号量,将计数信号量的初始值设置为1即可。

代码实现:

有了上面的二值信号量识别后,互斥信号量和二值信号量有什么区别呢? 互斥信号量可以防止优越

电平反转在前面,不支持2值信号量,下面就优先顺序反转问题进行说明。

执行条件:

创建三个任务Task1、Task2和Task3。 优先级分别为3、2、1。 也就是说,任务1的优先顺序最高。

任务Task1和Task3排他地访问串行端口打印printf,通过二值信号实现排他访问。

最初,Task3以二值信号量调用printf,但被任务Task1抢占,开始执行任务Task1,即上图的开始位置。

执行步骤如下。

任务Task1执行的进程需要调用函数printf。 您可以看到任务Task3正在被调用,任务Task1锁定等

等待Task3发行函数printf。

调度程序执行任务Task3并在任务3运行时,由于任务Task2已就绪,任务3已断开

点击优先级反转的问题就在这里。 根据任务的执行现象,任务Task1需要等待任务2的执行

这与抢占时间表正好相反,通常,高优先级任务抢占低优先级任务

在此,任务的执行成为高优先级任务Task1,等待低优先级任务Task2的完成。 所以把这个叫做“

优先级反转问题。

任务Task2的执行完成后,重新开始任务Task3的执行,当Task3释放排他资源时,任务Task1获得排他资源,

可以继续执行。 FreeRTOS排他信号量的实现FreeRTOS排他信号量是如何实现的呢? 实际上,相对于二进制信号量,排他信号量解决了以下优先级

反转问题。 让我们用以下框图说明FreeRTOS互斥信号量的实现,并让他们有一个图像。

执行条件:

分别以1和3的优先顺序创建两个任务Task1和任务Task2。 也就是说,任务Task2的优先级最高。

任务Task1和Task2互斥地串行访问以打印printf。

使用FreeRTOS排他信号

量实现串口打印 printf 的互斥访问。

运行过程描述如下: 低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。 此时任务 Task2 抢占了任务 Task1

的执行,任务 Task1 被挂起。 任务 Task2 得到执行。

 任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优

先级会被提升到与 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority

inheritance),这样就有效地防止了优先级翻转问题。 任务 Task2 被挂起,任务 Task1 有新的优先

级继续执行。

 任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。 由于互斥资源可以使用,任务

Task2 获得互斥资源后开始执行。FreeRTOS 中断方式互斥信号量的实现

互斥信号量仅支持用在 FreeRTOS 的任务中,中断函数中不可使用。互斥信号量 API 函数函数 xSemaphoreCreateMutex函数原型:

SemaphoreHandle_t xSemaphoreCreateMutex( void )

函数描述:

函数 xSemaphoreCreateMutex 用于创建互斥信号量。

 返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不

足,无法为此互斥信号量提供所需的空间会返回 NULL。使用这个函数要注意以下问题:

1. 此函数是基于函数 xQueueCreateMutex 实现的:

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

函数 xQueueCreateMutex 的实现是基于消息队列函数 xQueueGenericCreate 实现的。

2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:

#define configUSE_MUTEXES 1

互斥信号量, xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用

应用举例:

经过测试,互斥信号量是可以被其他任务释放的,但是我们最好不要这么做,因为官方推荐的就是在同一个任务中接收和释放。如果在其他任务释放,不仅仅会让代码整体逻辑变得复杂,还会给使用和维护这套API的人带来困难。遵守规范,总是好的。

裸机编程的时候,我经常想一个问题,就是怎么做到当一个标志位触发的时候,立即执行某个操作,如同实现标志中断一样,在os编程之后,我们就可以让一个优先级最高任务一直等待某个信号量,如果获得信号量,就执行某个操作,实现类似标志位中断的作用(当然,要想正真做到中断效果,那就需要屏蔽所有可屏蔽中断,而临界区就可以做到)。

再说一下递归互斥信号量:递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量

eg:

static void vTaskMsgPro(void *pvParameters)

{

TickType_t xLastWakeTime;const TickType_t xFrequency = 1500;/*获取当前的系统时间*/xLastWakeTime=xTaskGetTickCount();while(1)

{/*递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

{/*--------------------------------------*/

//假如这里是被保护的资源,第1层被保护的资源,用户可以在这里添加被保护资源

/*----------------------------------------------------------------------------*/printf("任务vTaskMsgPro在运行,第1层被保护的资源,用户可以在这里添加被保护资源rn");/*第1层被保护的资源里面嵌套被保护的资源*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

{/*------------------------------------------------------------------------*/

//假如这里是被保护的资源,第2层被保护的资源,用户可以在这里添加被保护资源

/*------------------------------------------------------------------------*/printf("任务vTaskMsgPro在运行,第2层被保护的资源,用户可以在这里添加被保护资源rn");/*第2层被保护的资源里面嵌套被保护的资源*/xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);

{

printf("任务vTaskMsgPro在运行,第3层被保护的资源,用户可以在这里添加被保护资源rn");

bsp_LedToggle(1);

bsp_LedToggle(4);

}

xSemaphoreGiveRecursive(xRecursiveMutex);

}

xSemaphoreGiveRecursive(xRecursiveMutex);

}

xSemaphoreGiveRecursive(xRecursiveMutex);/*vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/vTaskDelayUntil(&xLastWakeTime, xFrequency);

}

}

可以前面的那个官方文档那样用if判断传递共享量:

也可以用我们递归互斥信号量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE时,会一直等待信号量,直到有信号量来才执行后面的语句。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。