0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用Arduino在FreeRTOS中實現(xiàn)信號量和互斥量的方式

科技觀察員 ? 來源:circuitdigest ? 作者:Rishabh Jain ? 2022-08-16 15:34 ? 次閱讀

信號量和互斥(互斥)是用于同步、資源管理和保護資源免受損壞的內核對象。在本教程的前半部分,我們將了解Semaphore背后的理念,以及如何以及在何處使用它。

什么是信號量?

信號量是一種信號機制,其中處于等待狀態(tài)的任務由另一個任務發(fā)出信號以執(zhí)行。換句話說,當一個task1完成它的工作時,它會顯示一個標志或將一個標志加1,然后另一個任務(task2)收到這個標志,表明它現(xiàn)在可以執(zhí)行它的工作了。當 task2 完成其工作時,標志將減 1。

因此,基本上,它是一種“給予”和“接受”機制,而信號量是一個整數(shù)變量,用于同步對資源的訪問。

FreeRTOS 中的信號量類型:

信號量有兩種類型。

二進制信號量

計數(shù)信號量

1、Binary Semaphore:它有兩個整數(shù)值0和1。有點類似于長度為1的Queue。比如我們有兩個task,task1和task2。task1向task2發(fā)送數(shù)據,因此task2不斷檢查隊列項,如果有1,那么它可以讀取數(shù)據,否則它必須等到它變成1。取完數(shù)據后,task2將隊列遞減,使其為0,即task1再次可以將數(shù)據發(fā)送到task2。

從上面的例子可以說,二進制信號量是用于任務之間或任務與中斷之間的同步。

2. Counting Semaphore:它的值大于0,可以認為是長度大于1的隊列。這個semaphore用于對事件進行計數(shù)。在這種使用場景中,事件處理程序將在每次事件發(fā)生時“給予”一個信號量(增加信號量計數(shù)值),而處理程序任務將在每次處理事件時“獲取”一個信號量(減少信號量計數(shù)值) 。

因此,計數(shù)值是已發(fā)生的事件數(shù)與已處理的事件數(shù)之差。

現(xiàn)在,讓我們看看如何在我們的 FreeRTOS 代碼中使用 Semaphore。

如何在 FreeRTOS 中使用信號量?

FreeRTOS 支持用于創(chuàng)建信號量、獲取信號量和提供信號量的不同 API。

現(xiàn)在,同一個內核對象可以有兩種類型的 API。如果我們必須從 ISR 提供??信號量,則無法使用正常的信號量 API。您應該使用受中斷保護的 API。

在本教程中,我們將使用二進制信號量,因為它易于理解和實現(xiàn)。由于此處使用了中斷功能,因此您需要在 ISR 功能中使用受中斷保護的 API。當我們說將任務與中斷同步時,這意味著在 ISR 之后立即將任務置于運行狀態(tài)。

創(chuàng)建信號量:

要使用任何內核對象,我們必須首先創(chuàng)建它。要創(chuàng)建二進制信號量,請使用vSemaphoreCreateBinary()。

此 API 不接受任何參數(shù),并返回 SemaphoreHandle_t 類型的變量。創(chuàng)建一個全局變量名sema_v來存儲信號量。

SemaphoreHandle_t sema_v;

sema_v = xSemaphoreCreateBinary();

給出信號量:

對于提供信號量,有兩種版本——一種用于中斷,另一種用于正常任務。

xSemaphoreGive():這個 API 只接受一個參數(shù),它是信號量的變量名,如上面在創(chuàng)建信號量時給出的 sema_v。它可以從您想要同步的任何正常任務中調用。

xSemaphoreGiveFromISR():這是 xSemaphoreGive() 的受中斷保護的 API 版本。當我們需要同步 ISR 和普通任務時,應該從 ISR 函數(shù)中使用 xSemaphoreGiveFromISR()。

獲取信號量:

要獲取信號量,請使用 API 函數(shù)xSemaphoreTake()。這個 API 有兩個參數(shù)。

xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

xSemaphore:在我們的案例 sema_v 中要采用的信號量的名稱。

xTicksToWait:這是任務在阻塞狀態(tài)下等待信號量變?yōu)榭捎玫淖铋L時間。在我們的項目中,我們將 xTicksToWait 設置為portMAX_DELAY以使 task_1 無限期地等待阻塞狀態(tài),直到 sema_v 可用。

現(xiàn)在,讓我們使用這些 API 并編寫代碼來執(zhí)行一些任務。

這里連接了一個按鈕和兩個 LED。按鈕將充當連接到 Arduino Uno 引腳 2 的中斷按鈕。按下此按鈕時將產生中斷,連接到引腳 8 的 LED 將打開,再次按下時將關閉。

因此,當按下按鈕時,將從 ISR 函數(shù)調用xSemaphoreGiveFromISR (),從 TaskLED 函數(shù)調用 xSemaphoreTake() 函數(shù)。

為了使系統(tǒng)看起來多任務,將其他 LED 連接到引腳 7,引腳 7 將始終處于閃爍狀態(tài)。

信號量代碼說明

讓我們通過打開 Arduino IDE 開始編寫代碼

1. 首先,包含Arduino_FreeRTOS.h頭文件。現(xiàn)在,如果使用任何內核對象,如隊列信號量,則還必須包含一個頭文件。

#include  
#include 

2.聲明一個SemaphoreHandle_t類型的變量來存儲信號量的值。

SemaphoreHandle_t 中斷信號量;

3. 在 void setup() 中,使用 xTaskCreate() API 創(chuàng)建兩個任務(TaskLED 和 TaskBlink),然后使用 xSemaphoreCreateBinary() 創(chuàng)建一個信號量。創(chuàng)建一個具有相同優(yōu)先級的任務,然后嘗試使用這個數(shù)字。此外,將引腳 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. 現(xiàn)在,實現(xiàn) ISR 功能。創(chuàng)建一個函數(shù)并將其命名為與attachInterrupt()函數(shù)的第二個參數(shù)相同。為了使中斷正常工作,您需要使用millis或micros功能并通過調整去抖時間來消除按鈕的去抖問題。從此函數(shù)調用interruptHandler()函數(shù),如下所示。

長去抖時間 = 150; 
volatile unsigned long last_micros; 
void debounceInterrupt() { 
if((long)(micros() - last_micros) >= debounce_time * 1000) { 
interruptHandler(); 
last_micros = micros(); 
} 
}

在interruptHandler()函數(shù)中,調用xSemaphoreGiveFromISR()API。

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

這個函數(shù)會給TaskLed一個信號量來打開LED。

5. 創(chuàng)建一個TaskLed函數(shù)并在while循環(huán)中調用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. 另外,創(chuàng)建一個函數(shù)來閃爍連接到引腳 7 的其他 LED。

void TaskLed1(void *pvParameters) 
{ 
(void) pvParameters; 
pinMode(7,輸出);
而(1){
數(shù)字寫入(7,高);
vTaskDelay(200 / portTICK_PERIOD_MS); 
數(shù)字寫入(7,低);
vTaskDelay(200 / portTICK_PERIOD_MS); 
} 
}

7. void 循環(huán)函數(shù)將保持為空。不要忘記它。

無效循環(huán)(){}

就是這樣,完整的代碼可以在本教程的末尾找到。現(xiàn)在,上傳此代碼并根據電路圖將 LED 和按鈕與 Arduino UNO 連接起來。

電路原理

poYBAGL7SCGAG-2xAAQwgqHUXRE555.png

上傳代碼后,您會看到一個 LED 在 200 毫秒后閃爍,當按下按鈕時,第二個 LED 會立即發(fā)光,如最后給出的視頻所示。

pYYBAGL7SB2AZJtlAAZ3UKhATq8320.png

通過這種方式,信號量可以在帶有 Arduino 的 FreeRTOS 中使用,它需要將數(shù)據從一個任務傳遞到另一個任務而不會造成任何損失。

現(xiàn)在,讓我們看看什么是 Mutex 以及如何使用 FreeRTOS。

什么是互斥鎖?

如上所述,信號量是一種信號機制,類似地,Mutex 是一種鎖定機制,與信號量不同,信號量具有單獨的遞增和遞減函數(shù),但在 Mutex 中,函數(shù)本身接受和給出。這是一種避免共享資源損壞的技術。

為了保護共享資源,需要為資源分配一個令牌卡(互斥體)。擁有這張卡的人可以訪問其他資源。其他人應該等到卡歸還。這樣,只有一個資源可以訪問任務,其他資源等待機會。

讓我們通過一個例子來了解FreeRTOS 中的 Mutex 。

這里我們有三個任務,一個用于在 LCD 上打印數(shù)據,第二個用于將 LDR 數(shù)據發(fā)送到 LCD 任務,最后一個任務用于在 LCD 上發(fā)送溫度數(shù)據。所以這里兩個任務共享相同的資源,即 LCD。如果 LDR 任務和溫度任務同時發(fā)送數(shù)據,則其中一個數(shù)據可能損壞或丟失。

因此,為了防止數(shù)據丟失,我們需要鎖定 task1 的 LCD 資源,直到它完成顯示任務。然后 LCD 任務將解鎖,然后 task2 可以執(zhí)行其工作。

您可以在下圖中觀察互斥量和信號量的工作。

pYYBAGL7SBiAaPDtAAFXiAZad48919.png

如何在 FreeRTOS 中使用互斥鎖?

互斥量的使用方式也與信號量相同。首先,創(chuàng)建它,然后使用各自的 API 提供和獲取。

創(chuàng)建互斥鎖:

要創(chuàng)建互斥體,請使用xSemaphoreCreateMutex() API。顧名思義,互斥量是一種二進制信號量。它們用于不同的上下文和目的。二進制信號量用于同步任務,而 Mutex 用于保護共享資源。

此 API 不接受任何參數(shù)并返回SemaphoreHandle_t類型的變量。如果無法創(chuàng)建互斥鎖,則xSemaphoreCreateMutex()返回 NULL。

SemaphoreHandle_t mutex_v;

mutex_v = xSemaphoreCreateMutex();

采取互斥鎖:

當任務想要訪問資源時,它將使用xSemaphoreTake() API 獲取 Mutex。它與二進制信號量相同。它還需要兩個參數(shù)。

xSemaphore:在我們的例子中使用的 Mutex 的名稱mutex_v。

xTicksToWait:這是任務在阻塞狀態(tài)下等待 Mutex 可用的最長時間。在我們的項目中,我們將 xTicksToWait 設置為portMAX_DELAY以使 task_1 在 Blocked 狀態(tài)下無限期等待,直到mutex_v可用。

提供互斥鎖:

訪問共享資源后,任務應該返回 Mutex,以便其他任務可以訪問它。xSemaphoreGive() API 用于返回 Mutex。

xSemaphoreGive() 函數(shù)只接受一個參數(shù),即在我們的案例 mutex_v 中給出的 Mutex。

使用上述 API,讓我們使用 Arduino IDE 在 FreeRTOS 代碼中實現(xiàn) Mutex。

互斥代碼說明

這部分的目標是使用串行監(jiān)視器作為共享資源和兩個不同的任務來訪問串行監(jiān)視器以打印一些消息。

1. 頭文件將保持與信號量相同。

#include  
#include 

2. 聲明一個SemaphoreHandle_t類型的變量來存儲 Mutex 的值。

SemaphoreHandle_t mutex_v;

3. 在void setup() 中,以 9600 波特率初始化串行監(jiān)視器,并使用xTaskCreate()API 創(chuàng)建兩個任務(Task1 和 Task2)。然后使用xSemaphoreCreateMutex()創(chuàng)建一個 Mutex 。創(chuàng)建一個具有相同優(yōu)先級的任務,然后嘗試使用這個數(shù)字。

無效設置(){
序列.開始(9600);
mutex_v = xSemaphoreCreateMutex(); 
if (mutex_v == NULL) { 
Serial.println("無法創(chuàng)建互斥鎖"); 
} 
xTaskCreate(Task1, "任務 1", 128, NULL, 1, NULL); 
xTaskCreate(Task2, "任務 2", 128, NULL, 1, NULL); 
}

4. 現(xiàn)在,為Task1 和Task2 制作任務函數(shù)。在任務函數(shù)的while循環(huán)中,在串行監(jiān)視器上打印消息之前,我們必須使用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)); 
} 
}

同理,實現(xiàn)延遲500ms的Task2函數(shù)。

5. void loop()將保持為空。

現(xiàn)在,將此代碼上傳到 Arduino UNO 并打開串行監(jiān)視器。

您將看到正在從 task1 和 task2 打印消息。

poYBAGL7SBCAUrmqAAElUa0KgQI681.png

要測試 Mutex 的工作,只需注釋xSemaphoreGive(mutex_v); 從任何任務。您可以看到程序掛在最后一條打印消息上。

poYBAGL7SAuAFdn1AAF9_V6mP5g682.png

這就是使用 Arduino 在 FreeRTOS 中實現(xiàn)信號量和互斥量的方式。

信號量代碼:

#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);
}
}

無效循環(huán)(){}

無效中斷處理程序(){
xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}

void TaskLed(void *pvParameters)
{
(void) pvParameters;
pinMode(8,輸出);
為了 (;;) {
if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS) {
數(shù)字寫入(8,!數(shù)字讀?。?));
}
}
}
void TaskBlink(void *pvParameters)
{
(void) pvParameters;
pinMode(7,輸出);
為了 (;;) {
數(shù)字寫入(7,高);
vTaskDelay(200 / portTICK_PERIOD_MS);
數(shù)字寫入(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("無法創(chuàng)建互斥鎖");
}
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));
}
}

無效循環(huán)(){
}

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • FreeRTOS
    +關注

    關注

    12

    文章

    483

    瀏覽量

    61863
  • Arduino
    +關注

    關注

    187

    文章

    6455

    瀏覽量

    186372
  • 信號量
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8297
收藏 人收藏

    評論

    相關推薦

    FreeRTOS信號量使用教程

    信號量是操作系統(tǒng)重要的一部分,信號量一般用來進行資源管理和任務同步, FreeRTOS信號量又分為二值
    的頭像 發(fā)表于 12-19 09:22 ?2998次閱讀
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>信號量</b>使用教程

    FreeRTOS信號量的使用與實例

    嵌入式系統(tǒng),任務管理是一個重要的部分,它涉及到任務之間的通信和同步,信號量,隊列,互斥鎖和事件標志組等概念。本文將以 FreeRTOS
    的頭像 發(fā)表于 12-12 15:25 ?2149次閱讀

    轉:第23章 FreeRTOS互斥信號量

    FreeRTOS互斥信號量的源碼實現(xiàn)是基于消息隊列實現(xiàn)的。本章教程配套的例子含Cortex
    發(fā)表于 09-06 14:58

    第15章 互斥信號量

    15.1 互斥信號量15.1.1互斥信號量的概念及其作用 互斥信號量就是
    發(fā)表于 10-06 16:40

    關于UCOSIII的信號量互斥信號量的理解?

    UCOSIII延時一定會引起任務切換,如果所有任務都進入等待態(tài),則切換到空閑任務運行?請求信號量,如果信號量值非零,不進行任務切換;為零,(等待超時后?或者一般都是設置死等)進行任
    發(fā)表于 03-13 00:11

    FreeRTOS信號量介紹

    FreeRTOS信號量 & ESP32實戰(zhàn)閱讀建議:有一定操作系統(tǒng)基礎知識。FreeRTOS信號量1. 二值信號量??二值
    發(fā)表于 01-27 07:28

    詳解互斥信號量的概念和運行

    1 、互 斥 信 號 1.1 互斥信號量的概念及其作用 互斥信號量的主要作用是對資源實現(xiàn)
    的頭像 發(fā)表于 10-22 11:57 ?1.1w次閱讀
    詳解<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>的概念和運行

    FreeRTOS信號量 & ESP32實戰(zhàn)

    FreeRTOS信號量 & ESP32實戰(zhàn)閱讀建議:有一定操作系統(tǒng)基礎知識。FreeRTOS信號量1. 二值信號量??二值
    發(fā)表于 12-03 18:06 ?1次下載
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>信號量</b> & ESP32實戰(zhàn)

    FreeRTOS 隊列 信號量 互斥

    文章目錄前言Queue 隊列semaphore 信號量Mutex 互斥微信公眾號前言FreeRTOS STM32CubeMX配置 內存管理 任務管理上節(jié)介紹了用STM32CubeMX
    發(fā)表于 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信號量包括二進制信號量、計數(shù)信號量互斥信號量(以后簡稱
    發(fā)表于 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函數(shù)

    FreeRTOS信號量包括二進制信號量、計數(shù)信號量互斥信號量(以后簡稱
    發(fā)表于 01-26 17:44 ?4次下載
    <b class='flag-5'>FreeRTOS</b>系列第20篇---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>信號量</b>API函數(shù)

    Arduino IDE中使用FreeRTOS信號量

    電子發(fā)燒友網站提供《Arduino IDE中使用FreeRTOS信號量.zip》資料免費下載
    發(fā)表于 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信號量是一種任務間通信的方式,信號量包括:二值信號量、
    的頭像 發(fā)表于 02-10 15:07 ?1333次閱讀

    Free RTOS的互斥信號量

    二進制信號量互斥非常相似,但確實有一些細微的區(qū)別。互斥體包含優(yōu)先級繼承機制,而二進制信號量沒有。這使得二進制
    的頭像 發(fā)表于 02-10 15:36 ?1080次閱讀
    Free RTOS的<b class='flag-5'>互斥</b><b class='flag-5'>信號量</b>

    使用Linux信號量實現(xiàn)互斥點燈

    信號量常用于控制對共享資源的訪問,有計數(shù)型信號量和二值信號量之分。初始化時信號量值大于1的,就是計數(shù)型信號量,計數(shù)型
    的頭像 發(fā)表于 04-13 15:12 ?758次閱讀
    使用Linux<b class='flag-5'>信號量</b><b class='flag-5'>實現(xiàn)</b><b class='flag-5'>互斥</b>點燈