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

news/2024/12/16 11:54:12/

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

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

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

文章目录

  • Binary Semaphore
    • xSemaphoreCreateBinary()
      • 返回值
    • xSemaphoreGive()
    • xSemaphoreTake()
    • Example Code: Binary Semaphore for Task Synchronization
  • Count Semaphore
    • xSemaphoreCreateCounting()
    • uxSemaphoreGetCount()
    • Example Code: Counting Semaphore for Parking Lot Management

参考资料
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);
}

http://www.ppmy.cn/news/1555579.html

相关文章

什么叫中间件服务器?

什么叫中间件服务器&#xff1f;它在软件架构中扮演着怎样的角色&#xff1f;在现代应用程序开发中&#xff0c;中间件服务器的概念很多人对它并不太熟悉&#xff0c;但其实它的作用却不小。 中间件服务器是一种连接不同软件应用程序的中介。想象一下&#xff0c;在一个大型企…

【前端面试】前端工程化

文章目录 1. 什么是前端工程化2. 前端工程化的核心要素2.1 模块化2.2 组件化2.3 自动化2.4 标准化 3. 工程化工具链3.1 包管理工具3.2 构建工具3.3 测试工具3.4 CI/CD 工具 4. Webpack 面试题4.1 基础问题4.2 进阶问题4.3 原理问题 5. 前端工程化实践5.1 项目初始化5.2 开发环境…

2024年软件测试面试题大全【含答案】

一、面试基础题 简述测试流程: 1、阅读相关技术文档&#xff08;如产品PRD、UI设计、产品流程图等&#xff09;。 2、参加需求评审会议。 3、根据最终确定的需求文档编写测试计划。 4、编写测试用例&#xff08;等价类划分法、边界值分析法等&#xff09;。 5、用例评审(…

嵌入式入门工程-简单电子温度计(一)

先大致确定硬件 一个stm32f103C8最小系统作控制&#xff0c;DS8B20温度检测模块&#xff0c;8个LED数码管&#xff0c;74HC138D作3位到8位的扩展来作位选择&#xff0c;74HC245作信号放大来驱动数码管显示。 建立工程文件夹SimpleElectronicThermometer&#xff0c;开发keil5&…

macOS:安装第三方软件

基于安全性考虑&#xff0c;Mac 系统通常不允许安装那些从网络上下载下来的第三方软件包。 比如&#xff0c;在打开镜像盘时&#xff0c;报错为“该镜像已损坏&#xff0c;请移至废纸篓”&#xff0c;或者打开软件时提示“XXX 已损坏&#xff0c;打不开。您应该将它移到废纸篓”…

宽窄依赖/宽窄巷子——spark

宽窄依赖是用于标记算子是否需要shuffle过程的 ——本质&#xff1a;只是一种标记&#xff0c;标记两个RDD之间的依赖关系&#xff0c;用于判断是否需要进行shuffle 窄依赖&#xff1a;Narrow Dependencies 定义&#xff1a;父RDD的一个分区的数据只给了子RDD的一个分区 【不…

Devops-蓝鲸篇-03-蓝盾流水线简单介绍

BKCI流水线快速了解 BK-CI 可以帮你快速实现一条持续交付流水线来编译、测试、部署你的应用&#xff0c;下面将通过教程和文档指南告诉你&#xff0c;怎么在 BK-CI 里配置和管理持续集成、持续交付&#xff08;CI/CD&#xff09;流水线。 下面为流水线的完整逻辑图&#xff1…

C++中的接口继承和实现继承以及多态性与性能的平衡处理

接口继承 接口继承是指子类只继承基类的纯虚函数&#xff0c;即只继承基类的接口&#xff0c;而不继承基类的实现。子类必须实现基类中的所有纯虚函数&#xff0c;否则子类也将成为抽象类。在 C 中&#xff0c;接口继承主要通过抽象类来实现。抽象类是包含至少一个纯虚函数的类…