- Kas ir semafors?
- Kā FreeRTOS izmantot semaforu?
- Semafora koda skaidrojums
- Ķēdes shēma
- Kas ir Mutex?
- Kā lietot Mutex FreeRTOS?
- Mutex koda skaidrojums
Iepriekšējās apmācībās mēs esam aprakstījuši FreeRTOS pamatus ar Arduino un Queue kodola objektu FreeRTOS Arduino. Tagad šajā trešajā FreeRTOS apmācībā mēs uzzināsim vairāk par FreeRTOS un tās iepriekšējām API, kas var padziļinātāk izprast daudzuzdevumu platformu.
Semafors un Mutex (savstarpēja izslēgšana) ir kodola objekti, kurus izmanto sinhronizēšanai, resursu pārvaldībai un resursu aizsardzībai pret korupciju. Šīs apmācības pirmajā pusē mēs redzēsim Semafora ideju, kā un kur to izmantot. Otrajā puslaikā turpināsim ar Mutex.
Kas ir semafors?
Iepriekšējās apmācībās mēs esam apsprieduši par uzdevuma prioritātēm un arī uzzinājuši, ka augstākas prioritātes uzdevums iepriekš iztukšo zemākas prioritātes uzdevumu, tāpēc, kamēr augstas prioritātes uzdevuma izpilde var būt iespēja, ka datu korupcija var notikt zemākas prioritātes uzdevumā, jo tas vēl nav izpildīts, un dati uz šo uzdevumu tiek nepārtraukti saņemti no sensora, kas izraisa datu zudumu un visas lietojumprogrammas nepareizu darbību.
Tātad ir jāaizsargā resursi no datu zuduma, un šeit svarīga loma ir semaforiem.
Semafors ir signalizācijas mehānisms, kurā gaidīšanas stāvoklī esošo uzdevumu izpildei signalizē cits uzdevums. Citiem vārdiem sakot, kad 1. uzdevums ir pabeidzis darbu, tas parādīs karodziņu vai palielinās karodziņu par 1, un tad šo karodziņu saņem cits uzdevums (2. uzdevums), parādot, ka tas var veikt savu darbu tagad. Kad 2. uzdevums būs pabeidzis darbu, karogs tiks samazināts par 1.
Tātad būtībā tas ir mehānisms “Dod” un “Ņem”, un semafors ir vesels skaitlis, kas tiek izmantots, lai sinhronizētu piekļuvi resursiem.
Semaforu veidi FreeRTOS:
Semafors ir divu veidu.
- Binārā semafora
- Semafora skaitīšana
1. Binārā semafora: tai ir divas vesela skaitļa vērtības 0 un 1. Tas ir nedaudz līdzīgs 1. garuma rindai. Piemēram, mums ir divi uzdevumi, task1 un task2. Task1 nosūta datus uz task2, tāpēc task2 nepārtraukti pārbauda rindas vienumu, ja tāds ir 1, pēc tam tas var nolasīt datus, kas tam jāgaida, līdz tas kļūst par 1. Pēc datu ņemšanas task2 samazina rindu un padara to par 0. Tas atkal nozīmē task1 var nosūtīt datus uz task2.
No iepriekš minētā piemēra var teikt, ka binārā semafora tiek izmantota sinhronizācijai starp uzdevumiem vai starp uzdevumiem un pārtraukumiem.
2. Semaforu skaitīšana: tā vērtības pārsniedz 0, un to var uzskatīt par rindu, kuras garums ir lielāks par 1. Šo semaforu izmanto notikumu skaitīšanai. Šajā lietošanas scenārijā notikumu apstrādātājs katru reizi, kad notiks kāds notikums, “sniegs” semaforu (palielinot semaforu skaita vērtību), un apstrādātāja uzdevums “paņems” semaforu katru reizi, kad tas apstrādā notikumu (samazinās semaforu skaita vērtību)..
Tādējādi skaitīšanas vērtība ir starpība starp notikušo notikumu skaitu un apstrādāto skaitu.
Tagad redzēsim, kā izmantot semaforu mūsu FreeRTOS kodā.
Kā FreeRTOS izmantot semaforu?
FreeRTOS atbalsta dažādus API, lai izveidotu semaforu, paņemtu semaforu un piešķirtu semaforu.
Tagad tam pašam kodola objektam var būt divu veidu API. Ja mums jāsniedz semafors no ISR, tad parasto semaforu API nevar izmantot. Jums jāizmanto ar pārtraukt aizsargātas API.
Šajā apmācībā mēs izmantosim bināro semaforu, jo to ir viegli saprast un īstenot. Tā kā šeit tiek izmantota pārtraukšanas funkcionalitāte, jums ISR funkcijā jāizmanto ar pārtraukt aizsargāti API. Kad mēs sakām uzdevuma sinhronizēšanu ar pārtraukumu, tas nozīmē, ka uzdevums tiek ievietots darbības stāvoklī tūlīt pēc ISR.
Semafora izveide:
Lai izmantotu jebkuru kodola objektu, mums tas vispirms ir jāizveido. Lai izveidotu bināro semaforu, izmantojiet vSemaphoreCreateBinary ().
Šī API neņem nevienu parametru un atgriež mainīgo tipa SemaphoreHandle_t. Semafora glabāšanai tiek izveidots globālā mainīgā nosaukums sema_v.
SemaforsHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Semafora piešķiršana:
Lai sniegtu semaforu, ir divas versijas - viena pārtraukšanai un otra parastajam uzdevumam.
- xSemaphoreGive (): šai API tiek izmantots tikai viens arguments, kas ir semafora mainīgais nosaukums, piemēram, sema_v, kā norādīts iepriekš, veidojot semaforu. To var izsaukt no jebkura parasta uzdevuma, kuru vēlaties sinhronizēt.
- xSemaphoreGiveFromISR (): Šī ir xSemaphoreGive () API pārtrauktā aizsardzība. Kad mums ir nepieciešams sinhronizēt ISR un parasto uzdevumu, no ISR funkcijas jāizmanto xSemaphoreGiveFromISR ().
Semafora ņemšana:
Lai ņemtu semaforu, izmantojiet API funkciju xSemaphoreTake (). Šim API ir divi parametri.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemafora: mūsu gadījumā ņemamā semafora nosaukums sema_v.
xTicksToWait: Šis ir maksimālais laiks, līdz uzdevums būs jāgaida bloķētā stāvoklī, līdz semafora kļūs pieejama. Mūsu projektā xTicksToWait iestatīsim uz portMAX_DELAY, lai uzdevums_1 bloķētā stāvoklī nenoteiktu laiku gaidītu, līdz būs pieejams sema_v.
Tagad izmantosim šos API un uzrakstīsim kodu, lai veiktu dažus uzdevumus.
Šeit ir savienota viena spiedpoga un divas gaismas diodes. Spiedpoga darbosies kā pārtraukšanas poga, kas pievienota Arduino Uno 2. tapai. Nospiežot šo pogu, tiks izveidots pārtraukums un iedegsies LED, kas ir savienots ar 8. kontaktu, un, nospiežot to vēlreiz, tas tiks izslēgts.
Tātad, nospiežot pogu, xSemaphoreGiveFromISR () tiks izsaukts no funkcijas ISR, bet funkcija xSemaphoreTake () tiks izsaukta no funkcijas TaskLED.
Lai sistēma izskatās daudzuzdevumu režīmā, pievienojiet citus gaismas diodes ar tapu 7, kas vienmēr mirgos.
Semafora koda skaidrojums
Sāksim rakstīt kodu, atverot Arduino IDE
1. Vispirms iekļaujiet galvenes failu Arduino_FreeRTOS.h . Tagad, ja tiek izmantots kāds kodola objekts, piemēram, rindas semafors, tam jāiekļauj arī galvenes fails.
# iekļaut # iekļaut
2. Paziņojiet mainīgo SemaphoreHandle_t, lai saglabātu semafora vērtības.
SemaforaHandle_t pārtrauktSemafora;
3. Tukšajā iestatījumā () izveidojiet divus uzdevumus (TaskLED un TaskBlink), izmantojot xTaskCreate () API, un pēc tam izveidojiet semaforu, izmantojot xSemaphoreCreateBinary (). Izveidojiet uzdevumu ar vienādām prioritātēm un vēlāk mēģiniet spēlēt ar šo numuru. Konfigurējiet arī tapu 2 kā ieeju un iespējojiet iekšējo pievilkšanas rezistoru un piestipriniet pārtraukuma tapu. Visbeidzot, palaidiet plānotāju, kā parādīts zemāk.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); pārtrauktSemaphore = xSemaphoreCreateBinary (); if (pārtrauktSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Tagad īstenojiet ISR funkciju. Izveidojiet funkciju un nosauciet to tāpat kā funkcijas attachInterrupt () otro argumentu. Lai pārtraukums darbotos pareizi, jums ir jānovērš spiedpogas atgrūšanas problēma, izmantojot mili vai mikros funkciju un pielāgojot atlēciena laiku. Izmantojot šo funkciju, izsauciet funkcijuruptHandler (), kā parādīts zemāk.
garš debouncing_time = 150; nepastāvīgs neparakstīts garš last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { pārtrauktHandler (); last_micros = mikros (); } }
Jo interruptHandler () funkciju, zvaniet xSemaphoreGiveFromISR () API.
voidruptHandler () { xSemaphoreGiveFromISR (pārtrauktSemaphore, NULL); }
Šī funkcija piešķirs semaforu TaskLed, lai ieslēgtu LED.
5. Izveidot TaskLed funkciju un iekšā kamēr cilpas, zvaniet xSemaphoreTake () API un pārbaudiet, vai semafors ir veiksmīgi pieņemts vai nē. Ja tas ir vienāds ar pdPASS (ti, 1), veiciet LED pārslēgšanos, kā parādīts zemāk.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, OUTPUT); while (1) { if (xSemaphoreTake (ruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Izveidojiet arī funkciju, lai mirgot citas 7. kontaktam pievienotās gaismas diodes.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, OUTPUT); kamēr (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funkcija void loop paliks tukša. Neaizmirstiet to.
void loop () {}
Tas ir viss, pilns kods ir atrodams šīs apmācības beigās. Tagad augšupielādējiet šo kodu un pievienojiet gaismas diodes un spiedpogu ar Arduino UNO saskaņā ar shēmu.
Ķēdes shēma
Pēc koda augšupielādes jūs redzēsiet, ka pēc 200 ms mirgo gaismas diode, un, nospiežot pogu, tūlīt mirgos otrais gaismas diode, kā parādīts beigās redzamajā video.
Tādā veidā semaforas var tikt izmantotas FreeRTOS ar Arduino, kur tai ir jāpārraida dati no viena uzdevuma citam bez zaudējumiem.
Tagad redzēsim, kas ir Mutex un kā to izmantot FreeRTOS.
Kas ir Mutex?
Kā paskaidrots iepriekš, semafors ir signalizācijas mehānisms, tāpat kā Mutex ir bloķēšanas mehānisms, atšķirībā no semaforas, kuram ir atsevišķas funkcijas pieaugumam un samazinājumam, bet Mutex funkcija pati par sevi ņem un dod. Tā ir metode, kā izvairīties no kopīgu resursu korupcijas.
Lai aizsargātu koplietojamo resursu, resursam piešķir marķiera karti (muteksu). Kam ir šī karte, var piekļūt citam resursam. Citiem vajadzētu gaidīt, kamēr karte atgriezīsies. Tādā veidā tikai viens resurss var piekļūt uzdevumam, un citi gaida savu iespēju.
Sapratīsim Mutex FreeRTOS ar piemēra palīdzību.
Šeit mums ir trīs uzdevumi: viens datu drukāšanai uz LCD, otrais - LDR datu nosūtīšanai uz LCD uzdevumu un pēdējais uzdevums temperatūras datu nosūtīšanai uz LCD. Tātad šeit diviem uzdevumiem ir kopīgs resurss, ti, LCD. Ja LDR uzdevums un temperatūras uzdevums vienlaikus nosūta datus, viens no datiem var būt bojāts vai pazaudēts.
Tātad, lai aizsargātu datu zudumu, mums ir jābloķē LCD resurss task1, līdz tas pabeidz displeja uzdevumu. Tad LCD uzdevums tiks atbloķēts, un tad task2 varēs veikt savu darbu.
Zemāk redzamajā diagrammā varat novērot Mutex un semaforu darbību.
Kā lietot Mutex FreeRTOS?
Arī muteksus lieto tāpat kā semaforas. Vispirms izveidojiet to, pēc tam dodiet un ņemiet, izmantojot attiecīgos API.
Mutex izveide:
Lai izveidotu Mutex, izmantojiet xSemaphoreCreateMutex () API . Tā kā nosaukums norāda, ka Mutex ir binārā semafora veids. Tos izmanto dažādos kontekstos un nolūkos. Binārā semafora ir paredzēta uzdevumu sinhronizēšanai, savukārt Mutex tiek izmantota koplietojamo resursu aizsardzībai.
Šī API neņem vērā nevienu argumentu un atgriež mainīgo tipa SemaphoreHandle_t . Ja muteksu nevar izveidot, xSemaphoreCreateMutex () atgriež NULL.
SemaforaHandle_t muteks_v; mutex_v = xSemaphoreCreateMutex ();
Mutex lietošana:
Kad uzdevums vēlas piekļūt resursam, tam būs nepieciešams Mutex, izmantojot xSemaphoreTake () API. Tas ir tas pats, kas binārā semafora. Tam nepieciešami arī divi parametri.
xSemaphore: Mutex nosaukums, kas jāņem mūsu gadījumā mutex_v .
xTicksToWait: Šis ir maksimālais laiks, līdz uzdevums būs jāgaida bloķētā stāvoklī, līdz Mutex kļūs pieejams. Mūsu projektā xTicksToWait iestatīsim uz portMAX_DELAY, lai uzdevums_1 bloķētā stāvoklī nenoteiktu laiku gaidītu, līdz būs pieejams mutex_v .
Mutex piešķiršana:
Pēc piekļuves koplietotajam resursam uzdevumam jāatgriež Mutex, lai citi uzdevumi tam varētu piekļūt. xSemaphoreGive () API tiek izmantots, lai atgrieztu Mutex.
Funkcijai xSemaphoreGive () ir nepieciešams tikai viens arguments, kas ir Mutex, kas jāsniedz mūsu gadījumā mutex_v.
Izmantojot iepriekš minētos API, ieviesīsim Mutex FreeRTOS kodā, izmantojot Arduino IDE.
Mutex koda skaidrojums
Šīs daļas mērķis ir izmantot sērijas monitoru kā koplietojamu resursu un divus dažādus uzdevumus, lai piekļūtu seriālajam monitoram, lai drukātu kādu ziņojumu.
1. Galvenes faili paliks tādi paši kā semafori.
# iekļaut # iekļaut
2. Deklarējiet mainīgo SemaphoreHandle_t, lai saglabātu Mutex vērtības.
SemaforaHandle_t muteks_v;
3. Invalid setup () inicializējiet seriālo monitoru ar 9600 bitu pārraides ātrumu un izveidojiet divus uzdevumus (Task1 un Task2), izmantojot API xTaskCreate () . Pēc tam izveidojiet Mutex, izmantojot xSemaphoreCreateMutex (). Izveidojiet uzdevumu ar vienādām prioritātēm un vēlāk mēģiniet spēlēt ar šo numuru.
void setup () { Sērijas.sākt (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex nevar izveidot"); } xTaskCreate (1. uzdevums, "1. uzdevums", 128, NULL, 1, NULL); xTaskCreate (2. uzdevums, "2. uzdevums", 128, NULL, 1, NULL); }
4. Tagad izveidojiet uzdevumu funkcijas Task1 un Task2. Pēc brīža uzdevuma funkcijas ciklā pirms ziņojuma drukāšanas seriālajā monitorā mums ir jāņem Mutex, izmantojot xSemaphoreTake (), pēc tam izdrukājiet ziņojumu un pēc tam atgrieziet Mutex, izmantojot xSemaphoreGive (). Tad dodiet zināmu kavēšanos.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Sveiki no 1. uzdevuma"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Līdzīgi ieviešiet Task2 funkciju ar 500 ms kavēšanos.
5. Void loop () paliks tukša.
Tagad augšupielādējiet šo kodu vietnē Arduino UNO un atveriet sērijveida monitoru.
Jūs redzēsiet, ka ziņojumi tiek drukāti no task1 un task2.
Lai pārbaudītu Mutex darbību, vienkārši komentējiet xSemaphoreGive (mutex_v); no jebkura uzdevuma. Var redzēt, ka programma karājas uz pēdējā drukas ziņojuma .
Šādi Semaforu un Mutex var ieviest FreeRTOS ar Arduino. Lai iegūtu papildinformāciju par Semaforu un Mutex, varat apmeklēt oficiālo FreeRTOS dokumentāciju.
Pilni Semafora un Mēmu kodi un video ir norādīti zemāk.