官方文档参考
6.4 Binary Semaphores Used for Synchronization
[[FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf#page=218&selection=5,0,7,42|FreeRTOS_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide, page 218]]
官网文档函数参考
209 4.1 vSemaphoreCreateBinary()
[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=209&selection=0,3,4,24|FreeRTOS_Reference_Manual_V10.0.0, page 209]]
上面是旧的信号量创建函数,已不再推荐使用,在新版本中,应使用如下函数:
4.2 xSemaphoreCreateBinary()
[[FreeRTOS_Reference_Manual_V10.0.0.pdf#page=212&selection=2,0,4,24|FreeRTOS_Reference_Manual_V10.0.0, page 212]]
什么是信号量
信号量
如同红绿灯一样,控制任务(Task)进行同步,哪些任务可以优先执行,哪些任务可以暂停。
二进制信号量
二进制信号量只有两个值,要么0
要么1
,1 的时候就开始执行,0 的时候停止执行,经常使用信号量的方式进行同步。
xSemaphoreCreateBinary():
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t xSemaphoreCreateBinary( void );
函数说明:
- 创建一个二进制信号量,返回一个可以引用的信号量句柄。
- RAM needed.
- 信号量创建初始状态为空,意味着要先给它一个信号(Give),信号量才能正常使用。
参数:
None:
无参数。
返回值:
NULL:
FreeRTOS 的堆中没有足够的内存,无法创建信号量句柄。Any other value:
信号量创建成功,返回一个可以引用的信号量句柄。
注意:
- 直接给任务下发通知,通常要比信号量更轻便、更快速。
- 信号量和互斥锁非常相似,但是一些微小的区别。互斥锁包含着优先级继承机制,而二进制信号量没有。这个导致二进制信号量是处理任务同步时的最佳选择。互斥锁是实现简单互斥的更好选择。 ^46986d
- 二进制信号量:
- 用于同步的二进制信号量在成功“获取”后不需要再“归还”。任务同步是通过一个中断或任务“给出“的信号量来实现的,同时别的任务“获取”信号量。请注意,使用直接任务通知,通常可以逢有效地实现任务同步。
- 互斥锁: ^0e1b84
- 如果另一个优先级更高的任务尝试获取互斥锁,则持有互斥锁的任务的优先级会提高。
- 已经获取互斥锁的任务,会“继承”试图获取相同互斥锁的任务的优先级。
也就是说,我持有,但是你的地位高,并且你要,因而我的地位会和你一样高,让你获取不到互斥锁。
- 互斥锁返回后,任务继承的优先级会被还原。
- 任务在使用完互斥锁后,必须始终归还互斥锁。换句话说,任何任务不可能一直持有同一个互斥锁。
- 二进制信号量:
- 互斥锁和二进制信号量均使用
SemaphoreHandle_t
,并且可以在任何用此类型形参的 API 函数中使用。 - 互斥锁和二进制信号量都可以使用函数
xSemaphoreCreateBinary()
创建的信号量。
xSemaphoreTake()
#include "FreeRTOS.h"
#include "semphr.h"
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
函数说明:
- 获取之前用
xSemaphoreCreate(), xSemaphoreCreateCounting(), xSemaphoreCreateMutex()
创建的信号量。
参数:
xSemaphore:
用于获取信号量的句柄。xTicksToWait:
超时等待时间。
返回值:
pdPASS:
成功获取到信号量。pdFAIL:
获取信号量失败。
注意事项:
- 必须在运行中的任务调用此函数。
- 不得在程序关键部分或任务暂停( Suspend )时调用此函数。
xSemaphoreGive()
#include "FreeRTOS.h"
#include "semphr.h"
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
函数说明:
- 创建一个信号量之后,首先要释放这个信号量。
vSemaphoreCreateBinary(), xSemaphoreCreateCounting(), xSemaphoreCreateMutex()
,用这些函数创建信号量之后,均应使用此函数来先释放信号量。
参数:
xSemaphore:
需要被释放的信号量句柄。
返回值:
pdPASS:
释放信号量成功。pdFAIL:
信号量释放失败,因为调用此函数的任务并非信号量的持有者。任何任务必须成功“获取”信号量,然后才能“归还”信号量。
需要在创建信号量之后(
xSemaphoreCreateBinary()...
),用此函数(xSemaphoreGive()
)首先给一个信号量,才能正常锁住数据。
示例代码
此处代码信号量的作用是“锁住”数据,即 一个任务对数据进行修改时,另一个任务不可对相同的数据修改。
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
static SemaphoreHandle_t semaphore_handle; // 创建信号量句柄,公共使用
static int count = 0; // 即将上锁的数据
void task_1(void *pvPara)
{
while (true) {
// 开启(拿走)信号,即表明,“我”要对数据进行更改了,另一个任务不可同时对公共数据修改
xSemaphoreTake(semaphore_handle, portMAX_DELAY);
for (int i = 0; i < 10; i++) {
count++;
printf("[TASK 1]count = %d\n", count);
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 把信号还回去,即 其他任务可以对公共数据修改了
xSemaphoreGive(semaphore_handle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void task_2(void *pvPara)
{
while (true) {
xSemaphoreTake(semaphore_handle, portMAX_DELAY);
for (int i = 0; i < 10; i++) {
count++;
printf("[TASK 2]count = %d\n", count);
vTaskDelay(pdMS_TO_TICKS(1000));
}
xSemaphoreGive(semaphore_handle);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
// 创建信号量
semaphore_handle = xSemaphoreCreateBinary();
// 信号量创建完成后,首先要给予一个信号
xSemaphoreGive(semaphore_handle);
xTaskCreate(task_1, "Task 1", 2048, (void *)semaphore_handle, 1, NULL);
xTaskCreate(task_2, "Task 2", 2048, (void *)semaphore_handle, 1, NULL);
}
示例输出
程序输出表明,两个任务分别对公共数据进行连续读写,在一个任务读写数据时,另一个任务完全不干扰。如果不用信号量对数据上锁,则两个任务会交替续写公共数据。
相当于信号灯,Task 1 绿灯时,有权读写数据,而 Task 2 无权读写;Task 2 绿灯时,有权读写数据, 而 Task 1 无权读写数据。