0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用Arduino在FreeRTOS中实现信号量和互斥量的方式

科技观察员 来源:circuitdigest 作者:Rishabh Jain 2022-08-16 15:34 次阅读

信号量和互斥(互斥)是用于同步、资源管理和保护资源免受损坏的内核对象。在本教程的前半部分,我们将了解Semaphore背后的理念,以及如何以及在何处使用它。

什么是信号量?

信号量是一种信号机制,其中处于等待状态的任务由另一个任务发出信号以执行。换句话说,当一个task1完成它的工作时,它会显示一个标志或将一个标志加1,然后另一个任务(task2)收到这个标志,表明它现在可以执行它的工作了。当 task2 完成其工作时,标志将减 1。

因此,基本上,它是一种“给予”和“接受”机制,而信号量是一个整数变量,用于同步对资源的访问。

FreeRTOS 中的信号量类型:

信号量有两种类型。

二进制信号量

计数信号量

1、Binary Semaphore:它有两个整数值0和1。有点类似于长度为1的Queue。比如我们有两个task,task1和task2。task1向task2发送数据,因此task2不断检查队列项,如果有1,那么它可以读取数据,否则它必须等到它变成1。取完数据后,task2将队列递减,使其为0,即task1再次可以将数据发送到task2。

从上面的例子可以说,二进制信号量是用于任务之间或任务与中断之间的同步。

2. Counting Semaphore:它的值大于0,可以认为是长度大于1的队列。这个semaphore用于对事件进行计数。在这种使用场景中,事件处理程序将在每次事件发生时“给予”一个信号量(增加信号量计数值),而处理程序任务将在每次处理事件时“获取”一个信号量(减少信号量计数值) 。

因此,计数值是已发生的事件数与已处理的事件数之差。

现在,让我们看看如何在我们的 FreeRTOS 代码中使用 Semaphore。

如何在 FreeRTOS 中使用信号量?

FreeRTOS 支持用于创建信号量、获取信号量和提供信号量的不同 API

现在,同一个内核对象可以有两种类型的 API。如果我们必须从 ISR 提供​​信号量,则无法使用正常的信号量 API。您应该使用受中断保护的 API。

在本教程中,我们将使用二进制信号量,因为它易于理解和实现。由于此处使用了中断功能,因此您需要在 ISR 功能中使用受中断保护的 API。当我们说将任务与中断同步时,这意味着在 ISR 之后立即将任务置于运行状态。

创建信号量:

要使用任何内核对象,我们必须首先创建它。要创建二进制信号量,请使用vSemaphoreCreateBinary()。

此 API 不接受任何参数,并返回 SemaphoreHandle_t 类型的变量。创建一个全局变量名sema_v来存储信号量。

SemaphoreHandle_t sema_v;

sema_v = xSemaphoreCreateBinary();

给出信号量:

对于提供信号量,有两种版本——一种用于中断,另一种用于正常任务。

xSemaphoreGive():这个 API 只接受一个参数,它是信号量的变量名,如上面在创建信号量时给出的 sema_v。它可以从您想要同步的任何正常任务中调用。

xSemaphoreGiveFromISR():这是 xSemaphoreGive() 的受中断保护的 API 版本。当我们需要同步 ISR 和普通任务时,应该从 ISR 函数中使用 xSemaphoreGiveFromISR()。

获取信号量:

要获取信号量,请使用 API 函数xSemaphoreTake()。这个 API 有两个参数。

xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

xSemaphore:在我们的案例 sema_v 中要采用的信号量的名称。

xTicksToWait:这是任务在阻塞状态下等待信号量变为可用的最长时间。在我们的项目中,我们将 xTicksToWait 设置为portMAX_DELAY以使 task_1 无限期地等待阻塞状态,直到 sema_v 可用。

现在,让我们使用这些 API 并编写代码来执行一些任务。

这里连接了一个按钮和两个 LED。按钮将充当连接到 Arduino Uno 引脚 2 的中断按钮。按下此按钮时将产生中断,连接到引脚 8 的 LED 将打开,再次按下时将关闭。

因此,当按下按钮时,将从 ISR 函数调用xSemaphoreGiveFromISR (),从 TaskLED 函数调用 xSemaphoreTake() 函数。

为了使系统看起来多任务,将其他 LED 连接到引脚 7,引脚 7 将始终处于闪烁状态。

信号量代码说明

让我们通过打开 Arduino IDE 开始编写代码

1. 首先,包含Arduino_FreeRTOS.h头文件。现在,如果使用任何内核对象,如队列信号量,则还必须包含一个头文件。

#include  
#include 

2.声明一个SemaphoreHandle_t类型的变量来存储信号量的值。

SemaphoreHandle_t 中断信号量;

3. 在 void setup() 中,使用 xTaskCreate() API 创建两个任务(TaskLED 和 TaskBlink),然后使用 xSemaphoreCreateBinary() 创建一个信号量。创建一个具有相同优先级的任务,然后尝试使用这个数字。此外,将引脚 2 配置为输入并启用内部上拉电阻并连接中断引脚。最后,启动调度程序,如下所示。

无效设置(){ 
  pinMode(2,INPUT_PULLUP);
  xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL ); 
xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL ); 
  中断信号量 = xSemaphoreCreateBinary(); 
  if (interruptSemaphore != NULL) { 
    attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW); 
  } 
}

4. 现在,实现 ISR 功能。创建一个函数并将其命名为与attachInterrupt()函数的第二个参数相同。为了使中断正常工作,您需要使用millis或micros功能并通过调整去抖时间来消除按钮的去抖问题。从此函数调用interruptHandler()函数,如下所示。

长去抖时间 = 150; 
volatile unsigned long last_micros; 
void debounceInterrupt() { 
if((long)(micros() - last_micros) >= debounce_time * 1000) { 
interruptHandler(); 
last_micros = micros(); 
} 
}

在interruptHandler()函数中,调用xSemaphoreGiveFromISR()API。

void interruptHandler() { 
xSemaphoreGiveFromISR(interruptSemaphore, NULL); 
}

这个函数会给TaskLed一个信号量来打开LED。

5. 创建一个TaskLed函数并在while循环中调用xSemaphoreTake()API 并检查信号量是否被成功获取。如果它等于 pdPASS(即 1),则使 LED 切换如下所示。

void TaskLed(void *pvParameters) 
{ 
(void) pvParameters; 
pinMode(8,输出);
while(1) { 
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) { 
digitalWrite(8, !digitalRead(8)); 
}   
} 
}

6. 另外,创建一个函数来闪烁连接到引脚 7 的其他 LED。

void TaskLed1(void *pvParameters) 
{ 
(void) pvParameters; 
pinMode(7,输出);
而(1){
数字写入(7,高);
vTaskDelay(200 / portTICK_PERIOD_MS); 
数字写入(7,低);
vTaskDelay(200 / portTICK_PERIOD_MS); 
} 
}

7. void 循环函数将保持为空。不要忘记它。

无效循环(){}

就是这样,完整的代码可以在本教程的末尾找到。现在,上传此代码并根据电路图将 LED 和按钮与 Arduino UNO 连接起来。

电路原理

poYBAGL7SCGAG-2xAAQwgqHUXRE555.png

上传代码后,您会看到一个 LED 在 200 毫秒后闪烁,当按下按钮时,第二个 LED 会立即发光,如最后给出的视频所示。

pYYBAGL7SB2AZJtlAAZ3UKhATq8320.png

通过这种方式,信号量可以在带有 Arduino 的 FreeRTOS 中使用,它需要将数据从一个任务传递到另一个任务而不会造成任何损失。

现在,让我们看看什么是 Mutex 以及如何使用 FreeRTOS。

什么是互斥锁?

如上所述,信号量是一种信号机制,类似地,Mutex 是一种锁定机制,与信号量不同,信号量具有单独的递增和递减函数,但在 Mutex 中,函数本身接受和给出。这是一种避免共享资源损坏的技术。

为了保护共享资源,需要为资源分配一个令牌卡(互斥体)。拥有这张卡的人可以访问其他资源。其他人应该等到卡归还。这样,只有一个资源可以访问任务,其他资源等待机会。

让我们通过一个例子来了解FreeRTOS 中的 Mutex 。

这里我们有三个任务,一个用于在 LCD 上打印数据,第二个用于将 LDR 数据发送到 LCD 任务,最后一个任务用于在 LCD 上发送温度数据。所以这里两个任务共享相同的资源,即 LCD。如果 LDR 任务和温度任务同时发送数据,则其中一个数据可能损坏或丢失。

因此,为了防止数据丢失,我们需要锁定 task1 的 LCD 资源,直到它完成显示任务。然后 LCD 任务将解锁,然后 task2 可以执行其工作。

您可以在下图中观察互斥量和信号量的工作。

pYYBAGL7SBiAaPDtAAFXiAZad48919.png

如何在 FreeRTOS 中使用互斥锁?

互斥量的使用方式也与信号量相同。首先,创建它,然后使用各自的 API 提供和获取。

创建互斥锁:

要创建互斥体,请使用xSemaphoreCreateMutex() API。顾名思义,互斥量是一种二进制信号量。它们用于不同的上下文和目的。二进制信号量用于同步任务,而 Mutex 用于保护共享资源。

此 API 不接受任何参数并返回SemaphoreHandle_t类型的变量。如果无法创建互斥锁,则xSemaphoreCreateMutex()返回 NULL。

SemaphoreHandle_t mutex_v;

mutex_v = xSemaphoreCreateMutex();

采取互斥锁:

当任务想要访问资源时,它将使用xSemaphoreTake() API 获取 Mutex。它与二进制信号量相同。它还需要两个参数。

xSemaphore:在我们的例子中使用的 Mutex 的名称mutex_v。

xTicksToWait:这是任务在阻塞状态下等待 Mutex 可用的最长时间。在我们的项目中,我们将 xTicksToWait 设置为portMAX_DELAY以使 task_1 在 Blocked 状态下无限期等待,直到mutex_v可用。

提供互斥锁:

访问共享资源后,任务应该返回 Mutex,以便其他任务可以访问它。xSemaphoreGive() API 用于返回 Mutex。

xSemaphoreGive() 函数只接受一个参数,即在我们的案例 mutex_v 中给出的 Mutex。

使用上述 API,让我们使用 Arduino IDE 在 FreeRTOS 代码中实现 Mutex。

互斥代码说明

这部分的目标是使用串行监视器作为共享资源和两个不同的任务来访问串行监视器以打印一些消息。

1. 头文件将保持与信号量相同。

#include  
#include 

2. 声明一个SemaphoreHandle_t类型的变量来存储 Mutex 的值。

SemaphoreHandle_t mutex_v;

3. 在void setup() 中,以 9600 波特率初始化串行监视器,并使用xTaskCreate()API 创建两个任务(Task1 和 Task2)。然后使用xSemaphoreCreateMutex()创建一个 Mutex 。创建一个具有相同优先级的任务,然后尝试使用这个数字。

无效设置(){
序列.开始(9600);
mutex_v = xSemaphoreCreateMutex(); 
if (mutex_v == NULL) { 
Serial.println("无法创建互斥锁"); 
} 
xTaskCreate(Task1, "任务 1", 128, NULL, 1, NULL); 
xTaskCreate(Task2, "任务 2", 128, NULL, 1, NULL); 
}

4. 现在,为Task1 和Task2 制作任务函数。在任务函数的while循环中,在串行监视器上打印消息之前,我们必须使用xSemaphoreTake()获取 Mutex ,然后打印消息,然后使用xSemaphoreGive() 返回 Mutex。然后再拖延一些时间。

void Task1(void *pvParameters) { 
while(1) { 
xSemaphoreTake(mutex_v, portMAX_DELAY); 
Serial.println("来自 Task1 的您好"); 
xSemaphoreGive(mutex_v); 
vTaskDelay(pdMS_TO_TICKS(1000)); 
} 
}

同理,实现延迟500ms的Task2函数。

5. void loop()将保持为空。

现在,将此代码上传到 Arduino UNO 并打开串行监视器。

您将看到正在从 task1 和 task2 打印消息。

poYBAGL7SBCAUrmqAAElUa0KgQI681.png

要测试 Mutex 的工作,只需注释xSemaphoreGive(mutex_v); 从任何任务。您可以看到程序挂在最后一条打印消息上。

poYBAGL7SAuAFdn1AAF9_V6mP5g682.png

这就是使用 Arduino 在 FreeRTOS 中实现信号量和互斥量的方式。

信号量代码:

#include
#include
长去抖时间 = 150;
volatile unsigned long last_micros;

SemaphoreHandle_t 中断信号量;

无效设置(){
pinMode(2, INPUT_PULLUP);
xTaskCreate(TaskLed, "Led", 128, NULL, 0, NULL );
xTaskCreate(TaskBlink, "LedBlink", 128, NULL, 0, NULL );
中断信号量 = xSemaphoreCreateBinary();
如果(中断信号量!= NULL){
attachInterrupt(digitalPinToInterrupt(2), debounceInterrupt, LOW);
}
}

无效循环(){}

无效中断处理程序(){
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}

void TaskLed(void *pvParameters)
{
(void) pvParameters;
pinMode(8,输出);
为了 (;;) {
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) {
数字写入(8,!数字读取(8));
}
}
}
void TaskBlink(void *pvParameters)
{
(void) pvParameters;
pinMode(7,输出);
为了 (;;) {
数字写入(7,高);
vTaskDelay(200 / portTICK_PERIOD_MS);
数字写入(7,低);
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
无效去抖中断(){
if((long)(micros() - last_micros) >= debouncing_time * 1000) {
中断处理程序();
last_micros = micros();
}
}

互斥体代码:
#include
#include

SemaphoreHandle_t mutex_v;
无效设置(){
序列号.开始(9600);
mutex_v = xSemaphoreCreateMutex();
if (mutex_v == NULL) {
Serial.println("无法创建互斥锁");
}
xTaskCreate(Task1, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);
}

无效任务1(无效* pvParameters){
而(1){
xSemaphoreTake(mutex_v, portMAX_DELAY);
Serial.println("来自 Task1 的您好");
xSemaphoreGive(mutex_v);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

void Task2(void *pvParameters) {
而(1){
xSemaphoreTake(mutex_v, portMAX_DELAY);
Serial.println("来自 Task2 的您好");
xSemaphoreGive(mutex_v);
vTaskDelay(pdMS_TO_TICKS(500));
}
}

无效循环(){
}

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FreeRTOS
    +关注

    关注

    12

    文章

    483

    浏览量

    61959
  • Arduino
    +关注

    关注

    187

    文章

    6461

    浏览量

    186569
  • 信号量
    +关注

    关注

    0

    文章

    53

    浏览量

    8308
收藏 人收藏

    评论

    相关推荐

    FreeRTOS信号量使用教程

    信号量是操作系统重要的一部分,信号量一般用来进行资源管理和任务同步, FreeRTOS信号量又分为二值
    的头像 发表于 12-19 09:22 3075次阅读
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>使用教程

    FreeRTOS信号量的使用与实例

    嵌入式系统,任务管理是一个重要的部分,它涉及到任务之间的通信和同步,信号量,队列,互斥锁和事件标志组等概念。本文将以 FreeRTOS
    的头像 发表于 12-12 15:25 2311次阅读

    转:第23章 FreeRTOS互斥信号量

    FreeRTOS互斥信号量的源码实现是基于消息队列实现的。本章教程配套的例子含Cortex
    发表于 09-06 14:58

    第15章 互斥信号量

    15.1 互斥信号量15.1.1互斥信号量的概念及其作用 互斥信号量就是
    发表于 10-06 16:40

    关于UCOSIII的信号量互斥信号量的理解?

    UCOSIII延时一定会引起任务切换,如果所有任务都进入等待态,则切换到空闲任务运行?请求信号量,如果信号量值非零,不进行任务切换;为零,(等待超时后?或者一般都是设置死等)进行任
    发表于 03-13 00:11

    FreeRTOS信号量介绍

    FreeRTOS信号量 & ESP32实战阅读建议:有一定操作系统基础知识。FreeRTOS信号量1. 二值信号量  二值
    发表于 01-27 07:28

    详解互斥信号量的概念和运行

    1 、互 斥 信 号 1.1 互斥信号量的概念及其作用 互斥信号量的主要作用是对资源实现
    的头像 发表于 10-22 11:57 1.1w次阅读
    详解<b class='flag-5'>互斥</b><b class='flag-5'>信号量</b>的概念和运行

    FreeRTOS信号量 & ESP32实战

    FreeRTOS信号量 & ESP32实战阅读建议:有一定操作系统基础知识。FreeRTOS信号量1. 二值信号量  二值
    发表于 12-03 18:06 1次下载
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b> & ESP32实战

    FreeRTOS 队列 信号量 互斥

    文章目录前言Queue 队列semaphore 信号量Mutex 互斥微信公众号前言FreeRTOS STM32CubeMX配置 内存管理 任务管理上节介绍了用STM32CubeMX
    发表于 12-09 09:51 0次下载
    <b class='flag-5'>FreeRTOS</b> 队列 <b class='flag-5'>信号量</b> <b class='flag-5'>互斥</b><b class='flag-5'>量</b>

    FreeRTOS高级篇6---FreeRTOS信号量分析

    FreeRTOS信号量包括二进制信号量、计数信号量互斥信号量(以后简称
    发表于 01-26 17:39 7次下载
    <b class='flag-5'>FreeRTOS</b>高级篇6---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>分析

    FreeRTOS系列第20篇---FreeRTOS信号量API函数

    FreeRTOS信号量包括二进制信号量、计数信号量互斥信号量(以后简称
    发表于 01-26 17:44 4次下载
    <b class='flag-5'>FreeRTOS</b>系列第20篇---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>API函数

    Arduino IDE中使用FreeRTOS信号量

    电子发烧友网站提供《Arduino IDE中使用FreeRTOS信号量.zip》资料免费下载
    发表于 01-04 10:18 0次下载
    <b class='flag-5'>在</b><b class='flag-5'>Arduino</b> IDE中使用<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信号量</b>

    FreeRTOS的二值信号量

    FreeRTOS信号量是一种任务间通信的方式信号量包括:二值信号量
    的头像 发表于 02-10 15:07 1440次阅读

    Free RTOS的互斥信号量

    二进制信号量互斥非常相似,但确实有一些细微的区别。互斥体包含优先级继承机制,而二进制信号量没有。这使得二进制
    的头像 发表于 02-10 15:36 1114次阅读
    Free RTOS的<b class='flag-5'>互斥</b><b class='flag-5'>信号量</b>

    使用Linux信号量实现互斥点灯

    信号量常用于控制对共享资源的访问,有计数型信号量和二值信号量之分。初始化时信号量值大于1的,就是计数型信号量,计数型
    的头像 发表于 04-13 15:12 782次阅读
    使用Linux<b class='flag-5'>信号量</b><b class='flag-5'>实现</b><b class='flag-5'>互斥</b>点灯