- Kas yra semaforas?
- Kaip naudoti semaforą FreeRTOS?
- Semaforo kodo paaiškinimas
- Grandinės schema
- Kas yra „Mutex“?
- Kaip „Mutex“ naudoti „FreeRTOS“?
- „Mutex“ kodo paaiškinimas
Ankstesnėse mokymo programose „FreeRTOS“ pagrindus aptarėme naudodami „Arduino“ ir „Queue“ branduolio objektą „FreeRTOS Arduino“. Dabar, šioje trečioje „FreeRTOS“ pamokoje, sužinosime daugiau apie „FreeRTOS“ ir jos išankstines API, kurios gali geriau suprasti kelių užduočių platformą.
Semaforas ir „Mutex“ (abipusis išskyrimas) yra branduolio objektai, naudojami sinchronizuoti, valdyti išteklius ir apsaugoti išteklius nuo sugadinimo. Pirmoje šios pamokos pusėje pamatysime semaforo idėją, kaip ir kur ją naudoti. Antroje pusėje pratęsime „Mutex“.
Kas yra semaforas?
Ankstesnėse pamokose aptarėme užduočių prioritetus ir taip pat sužinojome, kad aukštesnio prioriteto užduotis iš anksto sukuria mažesnio prioriteto užduotį, taigi, vykdant aukšto prioriteto užduotį, gali būti, kad duomenų sugadinimas gali įvykti žemesnio prioriteto užduotyje, nes tai dar nevykdomas, o duomenys nuolat patenka į šią užduotį iš jutiklio, dėl kurio prarandami duomenys ir sutrinka visos programos veikimas.
Taigi reikia apsaugoti išteklius nuo duomenų praradimo ir čia semaforas vaidina svarbų vaidmenį.
Semaforas yra signalizavimo mechanizmas, kai laukimo būsenos užduotį signalizuoja kita užduotis vykdyti. Kitaip tariant, kai užduotis1 baigs savo darbą, ji parodys vėliavą arba padidins vėliavą 1, tada šią vėliavą gauna kita užduotis (užduotis2), parodanti, kad ji gali atlikti savo darbą dabar. Kai užduotis 2 baigs darbą, vėliava bus sumažinta 1.
Taigi iš esmės tai yra „Duok“ ir „Imk“ mechanizmas, o semaforas yra sveikasis skaičius, kuris naudojamas sinchronizuoti prieigą prie išteklių.
Semaforų tipai „FreeRTOS“:
Semaforas yra dviejų tipų.
- Dvejetainis semaforas
- Skaičiuojant semaforą
1. Dvejetainė semafora: ji turi dvi sveiko skaičiaus reikšmes 0 ir 1. Tai šiek tiek panaši į 1 ilgio eilę. Pavyzdžiui, turime dvi užduotis: task1 ir task2. „Task1“ siunčia duomenis į task2, todėl task2 nuolat tikrina eilės elementą, jei yra 1, tada jis gali perskaityti kitus duomenis, kurių turi laukti, kol taps 1. Paėmęs duomenis, task2 sumažina eilę ir padaro ją 0. Tai dar kartą reiškia task1 gali nusiųsti duomenis į task2.
Iš minėto pavyzdžio galima pasakyti, kad dvejetainė semafora naudojama sinchronizuoti tarp užduočių arba tarp užduočių ir pertraukimo.
2. Semaforo skaičiavimas: jo reikšmės yra didesnės nei 0 ir gali būti laikoma didesne nei 1 eilė. Ši semafora naudojama įvykiams skaičiuoti. Šiame naudojimo scenarijuje įvykių tvarkytojas „duos“ semaforą kiekvieną kartą įvykus įvykiui (didinant semaforų skaičiaus vertę), o tvarkytojo užduotis „ims“ semaforą kiekvieną kartą apdorodama įvykį (mažindama semaforų skaičiaus vertę).
Todėl skaičiavimo vertė yra skirtumas tarp įvykių skaičiaus ir apdoroto skaičiaus.
Dabar pažiūrėkime, kaip naudoti „Semaforą“ mūsų „FreeRTOS“ kode.
Kaip naudoti semaforą FreeRTOS?
„FreeRTOS“ palaiko skirtingas semaforo kūrimo, semaforo paėmimo ir semaforo suteikimo API.
Dabar tam pačiam branduolio objektui gali būti du API tipai. Jei mes turime pateikti semaforą iš ISR, įprastos semaforo API naudoti negalima. Turėtumėte naudoti nutrauktas apsaugotas API.
Šioje pamokoje naudosime dvejetainę semaforą, nes ją lengva suprasti ir įgyvendinti. Kadangi čia naudojama pertraukimo funkcija, ISR funkcijoje turite naudoti apsaugotas pertraukimu API. Kai sakome, kad užduotis sinchronizuojama su pertraukimu, tai reiškia užduotį į bėgimo būseną iškart po ISR.
Semaforo kūrimas:
Norėdami naudoti bet kurį branduolio objektą, pirmiausia turime jį sukurti. Norėdami sukurti dvejetainę semaforą, naudokite vSemaphoreCreateBinary ().
Ši API neima jokio parametro ir pateikia kintamąjį, kurio tipas yra SemaphoreHandle_t. Semaforui saugoti sukurtas visuotinis kintamojo vardas sema_v.
SemaforasHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Duoti semaforą:
Norėdami pateikti semaforą, yra dvi versijos - viena pertraukimui ir kita įprastai užduočiai.
- „xSemaphoreGive“): Ši API turi tik vieną argumentą, kuris yra semaforo kintamasis vardas, pvz., „sema_v“, kaip nurodyta aukščiau, kuriant semaforą. Ją galima iškviesti iš bet kurios įprastos užduoties, kurią norite sinchronizuoti.
- „xSemaphoreGiveFromISR“ (): Tai yra „ xSemaphoreGive“ () apsaugota pertraukimu apsaugota API versija. Kai mums reikia sinchronizuoti ISR ir įprastą užduotį, tada iš funkcijos ISR reikia naudoti „xSemaphoreGiveFromISR“ ().
Semaforo paėmimas:
Norėdami paimti semaforą, naudokite API funkciją xSemaphoreTake (). Ši API turi du parametrus.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemafora: semaforo, kurį reikia paimti, pavadinimas sema_v.
„xTicksToWait“: tai yra maksimalus laikas, kurį užduotis laukia užblokuotoje būsenoje, kol semaforas taps prieinamas. Savo projekte „ xTicksToWait“ nustatysime į portMAX_DELAY , kad užduotis_1 neribotą laiką lauktų užblokuotoje būsenoje, kol bus pasiekiama „sema_v“.
Dabar naudokime šias API ir parašykime kodą kai kurioms užduotims atlikti.
Čia yra vienas mygtukas ir du šviesos diodai. Mygtukas veiks kaip pertraukimo mygtukas, pritvirtintas prie „Arduino Uno“ 2 kaiščio. Paspaudus šį mygtuką, bus sukurtas pertraukimas ir į 8 kaištį prijungtas šviesos diodas bus įjungtas, o vėl paspaudus jis bus išjungtas.
Taigi, paspaudus mygtuką, xSemaphoreGiveFromISR () bus iškviestas iš ISR funkcijos, o funkcija xSemaphoreTake () bus iškviesta iš TaskLED funkcijos.
Kad sistema atrodytų daugiafunkciškai, prijunkite kitus šviesos diodus su kaiščiu 7, kurie visada mirksės.
Semaforo kodo paaiškinimas
Pradėkime rašyti kodą atidarydami „Arduino IDE“
1. Pirmiausia įtraukite „ Arduino_FreeRTOS.h“ antraštės failą. Dabar, jei kuris nors branduolio objektas yra naudojamas kaip eilės semaforas, jam taip pat reikia įtraukti antraštės failą.
# įtraukti # įtraukti
2. Semaforo reikšmėms saugoti paskelbkite kintamąjį „ SemaphoreHandle_t“.
SemaforasHandle_t pertrauktiSemaforas;
3. Atlikdami „void setup“ (), sukurkite dvi užduotis („TaskLED“ ir „TaskBlink“) naudodami API xTaskCreate (), tada sukurkite semaforą naudodami „xSemaphoreCreateBinary“ (). Sukurkite užduotį su vienodais prioritetais ir vėliau bandykite žaisti šiuo numeriu. Taip pat sukonfigūruokite 2 kaištį kaip įvestį, įjunkite vidinį traukimo rezistorių ir pritvirtinkite pertraukimo kaištį. Galiausiai paleiskite tvarkaraštį, kaip parodyta žemiau.
negaliojanti sąranka () { pinMode (2, INPUT_PULLUP); „xTaskCreate“ („TaskLed“, „Led“, 128, NULL, 0, NULL); „xTaskCreate“ („TaskBlink“, „LedBlink“, 128, NULL, 0, NULL); nutrSemaphore = xSemaphoreCreateBinary (); if (nutrauktiSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Dabar įgyvendinkite ISR funkciją. Sukurkite funkciją ir pavadinkite ją taip pat, kaip ir antrasis funkcijos attachInterrupt () argumentas. Norėdami, kad pertraukimas veiktų tinkamai, turite pašalinti mygtuko atšaukimo problemą naudodami milis arba mikros funkciją ir koreguodami peradresavimo laiką. Naudodamiesi šia funkcija, iškvieskite funkciją „ nutrūktiHandler“ (), kaip parodyta toliau.
ilgas debouncing_time = 150; nepastovūs nepasirašyti ilgi last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { nutrauktiHandler (); last_micros = mikros (); } }
Be interruptHandler () funkciją, skambinkite xSemaphoreGiveFromISR () API.
negaliojantis pertraukimasHandler () { xSemaphoreGiveFromISR (pertraukimoSemaforas, NULL); }
Ši funkcija suteiks semaforą „TaskLed“ įjungti šviesos diodą.
5. Sukurkite „ TaskLed“ funkciją ir „ while“ ciklo viduje iškvieskite xSemaphoreTake () API ir patikrinkite, ar semaforas sėkmingai užimtas, ar ne. Jei jis yra lygus pdPASS (ty 1), tada perjunkite šviesos diodą, kaip parodyta žemiau.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, OUTPUT); while (1) { if (xSemaphoreTake (pertrauktiSemaFore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Taip pat sukurkite funkciją, kad mirksėtų kitas prie 7 kaiščio prijungtas šviesos diodas.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, OUTPUT); o (1) { digitalWrite (7, HIGH); „vTaskDelay“ (200 / portTICK_PERIOD_MS); „digitalWrite“ (7, LOW); „vTaskDelay“ (200 / portTICK_PERIOD_MS); } }
7. „Void loop“ funkcija liks tuščia. Nepamiršk to.
void loop () {}
Viskas, visą kodą rasite šios pamokos pabaigoje. Dabar įkelkite šį kodą ir prijunkite šviesos diodus ir mygtuką su „Arduino UNO“ pagal grandinės schemą.
Grandinės schema
Įkėlę kodą pamatysite, kad po 200 ms mirksi šviesos diodas, o paspaudus mygtuką tuoj pat šviečia antrasis šviesos diodas, kaip parodyta pabaigoje pateiktame vaizdo įraše.
Tokiu būdu semaforas gali būti naudojamas „FreeRTOS“ su „Arduino“, kur jai reikia perduoti duomenis iš vienos užduoties į kitą be jokių nuostolių.
Dabar pažiūrėkime, kas yra „Mutex“ ir kaip jį naudoti „FreeRTOS“.
Kas yra „Mutex“?
Kaip paaiškinta aukščiau, semaforas yra signalizavimo mechanizmas, panašiai, „Mutex“ yra fiksavimo mechanizmas, skirtingai nei semaforas, kuris turi atskiras funkcijas prieaugiui ir mažinimui, tačiau „Mutex“ funkcija ima ir duoda save. Tai yra būdas išvengti bendrų išteklių korupcijos.
Norint apsaugoti bendrą šaltinį, ištekliui priskiriama žetonų kortelė („muteksas“). Kas turi šią kortelę, gali pasiekti kitą šaltinį. Kiti turėtų palaukti, kol kortelė grįš. Tokiu būdu tik vienas šaltinis gali pasiekti užduotį, o kiti laukia savo šanso.
Supraskime „ Mutex“ sistemoje „FreeRTOS“ naudodamiesi pavyzdžiu.
Čia mes turime tris užduotis: vieną, skirtą duomenims spausdinti į LCD ekraną, antrą - LDR duomenų siuntimui į LCD užduotį ir paskutinę užduotį temperatūros duomenims siųsti į LCD. Taigi čia dvi užduotys naudoja tą patį šaltinį, ty LCD. Jei LDR užduotis ir temperatūros užduotis siunčia duomenis vienu metu, vienas iš duomenų gali būti sugadintas arba prarastas.
Taigi, norėdami apsaugoti duomenų praradimą, turime užrakinti „task1“ LCD išteklius, kol jis baigs rodymo užduotį. Tada LCD užduotis bus atrakinta ir tada task2 galės atlikti savo darbą.
Žemiau pateiktoje diagramoje galite stebėti „Mutex“ ir semaforų veikimą.
Kaip „Mutex“ naudoti „FreeRTOS“?
Mutexai taip pat naudojami kaip semaforai. Pirmiausia sukurkite jį, tada duokite ir paimkite naudodami atitinkamas API.
„Mutex“ kūrimas:
Norėdami sukurti „ Mutex“ , naudokite API xSemaphoreCreateMutex () . Kaip rodo jo pavadinimas, „Mutex“ yra dvejetainės semaforos rūšis. Jie naudojami skirtingais atvejais ir tikslais. Dvejetainis semaforas skirtas užduotims sinchronizuoti, o „Mutex“ - bendrų išteklių apsaugai.
Ši API nereikalauja jokių argumentų ir pateikia kintamąjį, kurio tipas yra SemaphoreHandle_t . Jei negalima sukurti mutekso , xSemaphoreCreateMutex () grąžins NULL.
SemaforasHandle_t muteksas_v; mutex_v = xSemaphoreCreateMutex ();
Mutex vartojimas:
Kai užduotis nori pasiekti išteklių, ji imsis „ Mutex“ , naudodama API xSemaphoreTake () . Tai tas pats, kas dvejetainė semafora. Tai taip pat reikalauja dviejų parametrų.
„xSemaphore“: „Mutex“, kurį reikia paimti, pavadinimas mutex_v .
„xTicksToWait“: tai yra maksimalus laikas, kurį užduotis laukia užblokuotoje būsenoje, kol „Mutex“ taps prieinama. Savo projekte „ xTicksToWait“ nustatysime į „ portMAX_DELAY“ , kad užduotis_1 neribotą laiką lauktų užblokuotoje būsenoje, kol bus pasiekiamas „ mutex_v“ .
Duoti „Mutex“:
Prieiga prie bendro šaltinio, užduotis turėtų grąžinti „Mutex“, kad kitos užduotys galėtų ją pasiekti. „xSemaphoreGive ()“ API naudojama „ Mutex“ grąžinti.
Funkcijai xSemaphoreGive () reikia tik vieno argumento, kuris yra „Mutex“, kuris turi būti pateiktas mūsų atveju „mutex_v“.
Naudodami aukščiau pateiktas API, įgyvendinkime „Mutex“ FreeRTOS kode naudodami „Arduino IDE“.
„Mutex“ kodo paaiškinimas
Čia šios dalies tikslas yra naudoti nuoseklųjį monitorių kaip bendrą šaltinį ir dvi skirtingas užduotis, norint pasiekti serijinį monitorių, kad būtų išspausdintas pranešimas.
1. Antraštės failai išliks tokie patys kaip semaforo.
# įtraukti # įtraukti
2. Paskelbkite „ SemaphoreHandle_t“ tipo kintamąjį, kad išsaugotumėte „ Mutex “ reikšmes.
SemaforasHandle_t muteksas_v
3. Atlikdami negaliojančią sąranką (), pradėkite serijinį monitorių naudodami 9600 perdavimo spartą ir sukurkite dvi užduotis (Task1 ir Task2) naudodami API xTaskCreate () . Tada sukurkite „ Mutex“ naudodami „ xSemaphoreCreateMutex“ (). Sukurkite užduotį su vienodais prioritetais ir vėliau pabandykite žaisti su šiuo skaičiumi.
negaliojanti sąranka () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex negalima sukurti"); } xTaskCreate (1 užduotis, "1 užduotis", 128, NULL, 1, NULL); „xTaskCreate“ (2 užduotis, „2 užduotis“, 128, NULL, 1, NULL); }
4. Dabar sukurkite „Task1“ ir „Task2“ užduočių funkcijas. Be Nors kilpa užduočių funkciją, prieš spausdinant žinutę ant serijos monitorius turime imtis unikalų identifikatorių naudojant xSemaphoreTake () , tada atspausdinti pranešimą ir tada grįžti unikalų identifikatorių naudojant xSemaphoreGive (). Tada duokite šiek tiek laiko.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println („Labas nuo 1 užduoties“); „xSemaphoreGive“ („mutex_v“); „vTaskDelay“ (pdMS_TO_TICKS (1000)); } }
Panašiai įgyvendinkite „Task2“ funkciją su 500 ms vėlavimu.
5. Void loop () liks tuščia.
Dabar įkelkite šį kodą į „Arduino UNO“ ir atidarykite nuoseklųjį monitorių.
Pamatysite pranešimus, kurie spausdinami iš task1 ir task2.
Norėdami išbandyti „ Mutex“ veikimą , tiesiog pakomentuokite „ xSemaphoreGive“ („mutex_v“); nuo bet kurios užduoties. Galite pamatyti, kad programa pakimba ant paskutinio spausdinto pranešimo .
Taip „Semafore“ ir „Mutex“ galima įdiegti „FreeRTOS“ su „Arduino“. Norėdami gauti daugiau informacijos apie „Semafore“ ir „Mutex“, galite apsilankyti oficialioje „FreeRTOS“ dokumentacijoje.
Visi kodai ir vaizdo įrašai semaforams ir nutildymams pateikti žemiau.