ESP32学习笔记_FreeRTOS(4)——Semaphore

摘要(From AI):
这篇博客详细介绍了 FreeRTOS 中二值信号量和计数信号量的基本概念、API 使用方法及实际应用场景,辅以完整的示例代码,适合初学者学习

前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。

上一篇:ESP32学习笔记_FreeRTOS(3)——SoftwareTimer

参考资料
Michael_ee视频教程
freeRTOS官网
espressif 在线文档


Binary Semaphore

Semaphore:信号量,用于控制 Task 进行同步,Binary 表示这个信号量只有两个值 0/1

xSemaphoreCreateBinary()

创建一个二值信号量,并返回一个句柄供引用。

#include “FreeRTOS.h”
#include “semphr.h”

SemaphoreHandle_t xSemaphoreCreateBinary( void );

关键点
内存分配

  • 自动从 FreeRTOS 堆中分配所需内存。
  • 如果需要静态分配内存,可使用 xSemaphoreCreateBinaryStatic()
    初始状态
  • 信号量以空状态创建。
  • 必须先通过 xSemaphoreGive() 给予信号量,才能通过 xSemaphoreTake() 获取

参数

返回值

  • NULL信号量创建失败(堆内存不足)

  • 非空句柄:信号量创建成功,返回的句柄可用于引用该信号量

过时 API

  • 旧的 vSemaphoreCreateBinary() 宏已被废弃,且不应在新应用中使用。
  • xSemaphoreCreateBinary() 创建的信号量默认为“空状态”,而 vSemaphoreCreateBinary() 默认为“非空状态”

使用前提

  • 配置要求
    • 确保在 FreeRTOSConfig.h 中启用 configSUPPORT\_DYNAMIC\_ALLOCATION

xSemaphoreGive()

释放(或“给予”)一个先前创建并已成功“获取”的信号量

#include “FreeRTOS.h”
#include “semphr.h”

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

参数

  • xSemaphore:要释放的信号量,类型为 SemaphoreHandle\_t,必须在使用前显式创建(如通过 vSemaphoreCreateBinary()xSemaphoreCreateCounting()xSemaphoreCreateMutex()

返回值

  • pdPASS:操作成功,信号量被成功释放

  • pdFAIL:操作失败,因为调用任务不是信号量的持有者。任务必须先成功“获取”信号量后才能释放

xSemaphoreTake()

获取(或“占用”)一个先前通过 vSemaphoreCreateBinary()xSemaphoreCreateCounting()xSemaphoreCreateMutex() 创建的信号量

#include “FreeRTOS.h”
#include “semphr.h”

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数

  • xSemaphore:要获取的信号量,类型为 SemaphoreHandle_t,必须在使用前显式创建
  • xTicksToWait:任务等待信号量可用的最大时间(以 FreeRTOS 系统时钟节拍为单位)
    • 0:若信号量不可用,立即返回
    • portMAX_DELAY:任务将无限期等待信号量可用(需在 FreeRTOSConfig.h 中启用 INCLUDE_vTaskSuspend
    • 使用 pdMS_TO_TICKS() 宏可将毫秒时间转换为节拍时间

返回值

  • pdPASS:信号量获取成功
    • 如果指定了等待时间( xTicksToWait 非零),任务可能被阻塞,直到信号量可用
  • pdFAIL:信号量获取失败
    • 如果指定了等待时间,任务会阻塞等待,但超时后仍未获取到信号量

注意事项

  • 必须从运行中的任务中调用,不能在调度器启动前或初始化阶段调用

  • 不应在临界区或调度器暂停时调用

Example Code: Binary Semaphore for Task Synchronization

  • 由于任务的优先级相同,FreeRTOS 调度器会以时间片轮转方式调度两个任务
    信号量保证了两个任务不会同时操作共享变量 iCount ,从而避免数据竞争

Task1Task2 都会试图对共享变量 iCount 进行递增操作:

  • 每次操作前,任务会调用 xSemaphoreTake() 获取信号量,以独占访问资源
  • 操作完成后,调用 xSemaphoreGive() 释放信号量,允许其他任务访问
#include <stdio.h>  
#include <inttypes.h>  
#include "sdkconfig.h"  
#include "freertos/FreeRTOS.h"  
#include "freertos/task.h"  
#include "esp_chip_info.h"  
#include "esp_flash.h"  
#include "esp_system.h"
#include "esp_log.h"
  
#include "freertos/semphr.h" // 信号量头文件


int iCount = 0;
SemaphoreHandle_t semaphoreHandle = NULL;

void myTask1(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(semaphoreHandle, portMAX_DELAY);// 锁住信号量
        for(int i = 0; i < 10; i++)
        {
            iCount++;
            ESP_LOGI("Task1", "iCount = %d\n", iCount);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
        xSemaphoreGive(semaphoreHandle);// 释放信号量
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}


void myTask2(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(semaphoreHandle, portMAX_DELAY);// 锁住信号量
        for(int i = 0; i < 10; i++)
        {
            iCount++;
            ESP_LOGI("Task2", "iCount = %d\n", iCount);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
        xSemaphoreGive(semaphoreHandle);// 释放信号量
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}
  
void app_main(void)
{
    semaphoreHandle = xSemaphoreCreateBinary();
    xSemaphoreGive(semaphoreHandle);
    xTaskCreate(myTask1, "Task1", 2048, NULL, 5, NULL);
    xTaskCreate(myTask2, "Task2", 2048, NULL, 5, NULL);
}


Count Semaphore

计数型信号量(Counting Semaphore) 用于在任务或中断之间实现资源的共享与同步。它与二值信号量的主要区别在于其内部维护了一个计数器,允许多个任务或中断“获取”信号量,而无需严格的互斥限制

xSemaphoreCreateCounting()

创建一个计数型信号量,返回一个句柄用于引用该信号量。信号量可用于事件计数或资源管理
用途

  1. 事件计数
    • 用于记录事件发生的次数
    • 每次事件发生时调用 xSemaphoreGive() 增加计数,任务通过 xSemaphoreTake() 处理事件并减少计数
    • 初始计数值应为 0
  2. 资源管理
    • 用于管理一定数量的共享资源
    • 每次任务获取资源时调用 xSemaphoreTake(),计数值减少;释放资源时调用 xSemaphoreGive(),计数值增加
    • 初始计数值应为可用资源的数
#include “FreeRTOS.h”
#include “semphr.h”

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, 
 											UBaseType_t uxInitialCount );

内存分配

  • 如果使用 xSemaphoreCreateCounting(),所需内存会自动从 FreeRTOS 堆中分配

  • 如果使用 xSemaphoreCreateCountingStatic(),则需要用户提供内存,实现静态分配

参数

  • uxMaxCount:信号量的最大计数值。当信号量达到此值时,不能再“给予”信号量
  • uxInitialCount:信号量的初始计数值

返回值

  • NULL:创建失败,通常由于堆内存不足
  • 非空句柄:信号量创建成功,可通过返回的句柄引用信号量

注意事项

  • 必须在 FreeRTOSConfig.h 中启用 configSUPPORT_DYNAMIC_ALLOCATION

uxSemaphoreGetCount()

返回信号量的当前计数值

#include “FreeRTOS.h”
#include “semphr.h”

UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore );

参数

  • xSemaphore:信号量的句柄,用于指定需要查询的信号量

返回值
返回指定信号量的当前计数值
此功能可用于实时监控信号量的状态或判断可用资源的数量

Example Code: Counting Semaphore for Parking Lot Management

资源管理
信号量的计数值表示资源的当前可用数量,通过动态增减信号量,实现对有限资源(如停车位)的精确管理。
任务协调
通过信号量,确保多个任务对共享资源的访问井然有序,避免资源竞争和冲突。

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h"

#include "freertos/semphr.h" // 信号量头文件

int iCount = 0;
SemaphoreHandle_t semaphoreHandle = NULL;

void carInTask(void *pvParameters)
{
    int empty = 0;
    while (1)
    {
        empty = uxSemaphoreGetCount(semaphoreHandle); // 获取信号量剩余数量
        ESP_LOGI("carInTask", "empty = %d\n", empty);

        vTaskDelay(pdMS_TO_TICKS(1000));
        if (xSemaphoreTake(semaphoreHandle, 0) == pdPASS)
        {
            ESP_LOGI("carInTask", "carIn\n");
        }
        else
        {
            ESP_LOGI("carInTask", "carIn failed\n");
        }
    }
}

void carOutTask(void *pvParameters)
{
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(6000));

        xSemaphoreGive(semaphoreHandle);
        ESP_LOGI("carOutTask", "carOut\n");
    }
}

void app_main(void)
{
    semaphoreHandle = xSemaphoreCreateCounting(5, 5);
    // xSemaphoreGive(semaphoreHandle);// 在使用计数信号量时,无需手动释放信号量

    xTaskCreate(carInTask, "carInTask", 2048, NULL, 5, NULL);
    xTaskCreate(carOutTask, "carOutTask", 2048, NULL, 5, NULL);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值