原创 6. ESP32开发之freeRTOS的信号量

2025-5-18 20:48 55 0 分类: MCU/ 嵌入式 文集: ESP32
  • 什么是信号量
  • 信号量能干啥
  • 信号量的函数
  • 实例举例
  • 总结

什么是信号量

简而言之,就是发出通知,接收通知的任务获得通知后去干啥啥。通知有多有少。自定义通知数量的,叫计数型信号量;只有有无(即“0”,“1”)通知的,叫二进制信号量。

信号量能干啥

  • 资源管理:控制多个任务对共享资源(如外设、内存块)的访问权限,避免竞争条件
  • 任务同步 :实现任务间的时序协调(如等待某个事件完成)
  • 中断与任务通信:在中断服务程序(ISR)中快速通知任务处理事件(需使用 xxxFromISR 版本的函数)

信号量的函数

  • 创建二进制信号量函数原型
SemaphoreHandle_t xSemaphoreCreateBinary()//返回值为SemaphoreHandle_t结构体指针,即创建的信号量,不成功返回NULL
  • 释放信号量,使信号量计数加1
xSemaphoreGive(xSemaphore)
  • 获取信号量,使信号量计数减1
xSemaphoreTake(xSemaphore, //创建的信号量
xBlockTime //等待时间,超时则返回pdFulse
);
  • 创建计数型信号量
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount, //最大计数值
UBaseType_t uxInitialCount//初始计数值
);//返回值为SemaphoreHandle_t结构体指针,即创建的信号量,不成功返回NULL
  • 删除信号量
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

实例

  • 二进制信号量的使用
/**
* Copyright (C) 2024-2034 HalfMoon2.
* All rights reserved.
*
* @file Filename without the absolute path
* @brief Brief description
* @author HalfMoon2
* @date 2025-04-29
* @version v0.1
*
* @revision history:
* 2025-04-29 - Initial version.
*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <esp_log.h>

SemaphoreHandle_t semap=NULL;

void taskA(void *pvParam)
{
while(1){
xSemaphoreGive(semap);

vTaskDelay(pdMS_TO_TICKS(1000));
}
}

void taskB(void *pvParam)
{
BaseType_t num=0;
while(1){
if(xSemaphoreTake(semap,pdMS_TO_TICKS(500))==pdPASS){
num++;
ESP_LOGI("taskB","num is %d",num);
}else{
ESP_LOGI("taskB","Take failed");
}

}
}

void app_main(void)
{
semap=xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1);
xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,3,NULL,1);

}


结果解析:taskB任务每500ms获取一次信号量,当获取成功则num变量加一,然而taskA任务每1000ms发出一个信号量,所以taskB每间隔500ms会失败一次。


  • 计数型信号量使用
/**
* Copyright (C) 2024-2034 HalfMoon2.
* All rights reserved.
*
* @file Filename without the absolute path
* @brief Brief description
* @author HalfMoon2
* @date 2025-04-30
* @version v0.1
*
* @revision history:
* 2025-04-30 - Initial version.
*/
#include <stdio.h>
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <esp_log.h>

SemaphoreHandle_t semap=NULL;

void taskA(void *pvParam)
{
while(1){
xSemaphoreGive(semap);

vTaskDelay(pdMS_TO_TICKS(1000));
}
}

void taskB(void *pvParam)
{
BaseType_t num=0;
while(1){
if(xSemaphoreTake(semap,pdMS_TO_TICKS(400))==pdPASS){
num++;
ESP_LOGI("taskB","num is %d",num);
}else{
ESP_LOGI("taskB","Take failed");
}
}
}

void app_main(void)
{
semap=xSemaphoreCreateCounting(3,2);
xTaskCreatePinnedToCore(taskA,"taskA",2048,NULL,4,NULL,1);
xTaskCreatePinnedToCore(taskB,"taskB",2048,NULL,3,NULL,1);
}


结果解析:创建计数型信号量时,初始值为2,taskA优先级高些先执行,信号量计数值加1,信号量计数值为3,taskB每400ms获取信号量前三次时成功的,第四次时,由于taskA阻塞时间为1000ms,这个时候其还没发出信号量,信号量计数值为0,所以taskB获取失败。

总结

  • 二进制信号量可以说是计数型信号量的特殊用法。
  • 信号量相对于消息队列要简单很多,区分好应用场景。信号量只传通知,消息队列是要传整个消息结构体的。如果看源码的话,信号量的API背后用的也是队列的API。
  • 二进制信号量对后面学习互斥量给予了启发,一个任务如果take了此信号量不give,另一个任务想take会是什么结果呢?

作者: 二月半, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-1862109.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /3 下一条