- Ano ang Semaphore?
- Paano magagamit ang Semaphore sa FreeRTOS?
- Paliwanag sa Semaphore Code
- Diagram ng Circuit
- Ano ang Mutex?
- Paano gamitin ang Mutex sa FreeRTOS?
- Paliwanag sa Mutex Code
Sa mga nakaraang tutorial, natakpan namin ang mga pangunahing kaalaman sa FreeRTOS sa Arduino at ng Queue kernel object sa FreeRTOS Arduino. Ngayon, sa pangatlong tutorial na FreeRTOS na ito, malalaman namin ang higit pa tungkol sa FreeRTOS at mga advance na API, na maaaring gawing mas malalim mong maunawaan ang multi-tasking platform.
Ang Semaphore at Mutex (Mutual Exclusion) ay ang mga kernel object na ginagamit para sa pagsabay, pamamahala ng mapagkukunan at pagprotekta ng mga mapagkukunan mula sa katiwalian. Sa unang kalahati ng tutorial na ito, makikita natin ang ideya sa likod ng Semaphore, paano at saan ito gagamitin. Sa ikalawang kalahati, magpapatuloy kami sa Mutex.
Ano ang Semaphore?
Sa mga nakaraang tutorial, tinalakay namin ang tungkol sa mga priyoridad sa gawain at kilalanin din na ang isang mas mataas na pangunahing gawain na pre-empts ng isang mas mababang gawain na may priyoridad kaya habang ang pagpapatupad ng mataas na pangunahing gawain ay maaaring may posibilidad na ang katiwalian ng data ay maaaring mangyari sa mas mababang gawain na prioridad dahil ay hindi pa naisakatuparan at ang data ay patuloy na darating sa gawaing ito mula sa isang sensor na nagsasanhi ng pagkawala ng data at hindi paggana ng buong aplikasyon.
Kaya, kailangang protektahan ang mga mapagkukunan mula sa pagkawala ng data at dito gumaganap ang Semaphore ng isang mahalagang papel.
Ang Semaphore ay isang mekanismo ng pag-sign kung saan ang isang gawain sa isang naghihintay na estado ay sinenyasan ng isa pang gawain para sa pagpapatupad. Sa madaling salita, kapag natapos ang gawain ng isang gawain nito, magpapakita ito ng isang watawat o magpapataas ng isang bandila ng 1 at pagkatapos ang watawat na ito ay natanggap ng isa pang gawain (gawain2) na nagpapakita na maaari nitong maisagawa ang gawain nito ngayon. Kapag natapos ng gawain2 ang gawain nito pagkatapos ang flag ay mababawasan ng 1.
Kaya, karaniwang, ito ay isang "Bigyan" at "Dalhin" na mekanismo at ang semaphore ay isang variable na integer na ginagamit upang i-synchronize ang pag-access sa mga mapagkukunan.
Mga uri ng Semaphore sa FreeRTOS:
Ang semaphore ay may dalawang uri.
- Binary Semaphore
- Nagbibilang ng Semaphore
1. Binary Semaphore: Mayroon itong dalawang halaga ng integer na 0 at 1. Ito ay medyo katulad sa pila ng haba ng 1. Halimbawa, mayroon kaming dalawang gawain, gawain1 at gawain2. Nagpapadala ang Task1 ng data sa task2 kaya't patuloy na suriin ng task2 ang item ng pila kung mayroong 1, pagkatapos ay mababasa nito ang data na kailangan pa nitong maghintay hanggang sa maging 1. Matapos kunin ang data, bawasan ng task2 ang pila at gawin itong 0 Nangangahulugan ito ng task1 muli maaaring ipadala ang data sa gawain2.
Mula sa halimbawa sa itaas, masasabing ang binary semaphore ay ginagamit para sa pagsabay sa pagitan ng mga gawain o sa pagitan ng mga gawain at makagambala.
2. Nagbibilang ng Semaphore: Mayroon itong mga halagang mas malaki sa 0 at maaaring maiisip ng pila ng haba na higit sa 1. Ginagamit ang semaphore na ito para sa pagbibilang ng mga kaganapan. Sa senaryo ng paggamit na ito, ang isang handler ng kaganapan ay 'magbibigay' ng isang semaphore sa tuwing may isang kaganapan na nangyayari (pagdaragdag ng halaga ng bilang ng semaphore), at ang isang gawain ng handler ay 'kukuha' ng isang semaphore sa tuwing nagpoproseso ito ng isang kaganapan (binabawasan ang halaga ng bilang ng semaphore).
Ang bilang ng bilang ay, samakatuwid, ang pagkakaiba sa pagitan ng bilang ng mga kaganapan na naganap at ang bilang na naproseso.
Ngayon, tingnan natin kung paano gamitin ang Semaphore sa aming FreeRTOS code.
Paano magagamit ang Semaphore sa FreeRTOS?
Sinusuportahan ng FreeRTOS ang iba't ibang mga API para sa paglikha ng isang semaphore, pagkuha ng isang semaphore at pagbibigay ng isang semaphore.
Ngayon, maaaring mayroong dalawang uri ng mga API para sa parehong bagay ng kernel. Kung kailangan naming magbigay ng semaphore mula sa isang ISR, kung gayon ang normal na semaphore API ay hindi maaaring gamitin. Dapat kang gumamit ng mga nakakagambalang protektadong API.
Sa tutorial na ito, gagamit kami ng binary semaphore dahil madaling maunawaan at maipatupad. Tulad ng pag-andar ng nakakagambala na ginamit dito, kailangan mong gumamit ng mga nakakagambalang protektadong API sa pagpapaandar ng ISR. Kapag sinasabi namin ang pagsabay sa isang gawain sa isang nakakagambala, nangangahulugan ito na ilagay ang gawain sa Pagpapatakbo ng estado pagkatapos mismo ng ISR.
Lumilikha ng isang Semaphore:
Upang magamit ang anumang bagay na kernel, kailangan muna namin itong likhain. Para sa paglikha ng isang binary semaphore, gumamit ng vSemaphoreCreateBinary ().
Ang API na ito ay hindi kumukuha ng anumang parameter at nagbabalik ng isang variable ng uri na SemaphoreHandle_t. Ang isang pandaigdigang variable na pangalan na sema_v ay nilikha upang maiimbak ang semaphore.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Pagbibigay ng isang semaphore:
Para sa pagbibigay ng isang semaphore, mayroong dalawang bersyon- isa para sa makagambala at isa pa para sa normal na gawain.
- xSemaphoreGive (): Ang API na ito ay tumatagal lamang ng isang argument na kung saan ay ang variable na pangalan ng semaphore tulad ng sema_v tulad ng ibinigay sa itaas habang lumilikha ng isang semaphore. Maaari itong tawagan mula sa anumang normal na gawain na nais mong i-synchronize.
- xSemaphoreGiveFromISR (): Ito ang nakakagambalang protektadong bersyon ng API ng xSemaphoreGive (). Kapag kailangan naming i-synchronize ang isang ISR at normal na gawain, pagkatapos ay dapat gamitin ang xSemaphoreGiveFromISR () mula sa pagpapaandar ng ISR.
Pagkuha ng isang semaphore:
Upang kumuha ng isang semaphore, gamitin ang pagpapaandar ng API xSemaphoreTake (). Ang API na ito ay tumatagal ng dalawang mga parameter.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Pangalan ng semaphore na kukuha sa aming kaso na sema_v.
xTicksToWait: Ito ang maximum na dami ng oras na maghihintay ang gawain sa Naka- block na estado para magamit ang semaphore. Sa aming proyekto, itatakda namin ang xTicksToWait sa portMAX_DELAY upang gawin ang gawain_1 na maghintay nang walang katiyakan sa Naka-block na estado hanggang sa magamit ang sema_v.
Ngayon, gamitin natin ang mga API na ito at magsulat ng isang code upang maisagawa ang ilang mga gawain.
Dito naka-interface ang isang push-button at dalawang LEDs. Ang push-button ay kikilos bilang isang nakakagambala na pindutan na nakakabit sa pin 2 ng Arduino Uno. Kapag ang pindutan na ito ay pinindot ang isang nakakagambala ay bubuo at isang LED na konektado sa pin 8 ay bubuksan at kapag pinindot mo ito muli ay OFF
Kaya, kapag pinindot ang pindutan xSemaphoreGiveFromISR () ay tatawagin mula sa ISR function at xSemaphoreTake () function ay tatawagin mula sa paggana ng TaskLED.
Upang gawing multitasking ang hitsura ng system, ikonekta ang iba pang mga LED na may pin 7 na magiging palaging kumikislap na estado.
Paliwanag sa Semaphore Code
Simulan natin ang pagsulat ng code para sa pamamagitan ng pagbubukas ng Arduino IDE
1. Una, isama ang file ng header ng Arduino_FreeRTOS.h . Ngayon, kung ang anumang bagay na kernel ay ginamit tulad ng pila ng pila, ang isang header file ay dapat ding isama para rito.
# isama ang # isama
2. Ipahayag ang isang variable ng uri ng SemaphoreHandle_t upang maiimbak ang mga halaga ng semaphore.
SemaphoreHandle_t makagambalaSemaphore;
3. Sa void setup (), lumikha ng dalawang mga gawain (TaskLED at TaskBlink) gamit ang xTaskCreate () API at pagkatapos ay lumikha ng isang semaphore gamit ang xSemaphoreCreateBinary (). Lumikha ng isang gawain na may pantay na mga priyoridad at sa paglaon subukang i-play ang numero na ito. Gayundin, I-configure ang pin 2 bilang isang input at paganahin ang panloob na resistor na pull-up at ikabit ang makagambala na pin. Panghuli, simulan ang tagapag-iskedyul tulad ng ipinakita sa ibaba.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); kung (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Ngayon, ipatupad ang pagpapaandar ng ISR. Gumawa ng isang pag-andar at pangalanan ito katulad ng pangalawang argument ng function na attachInterrupt () . Upang maayos na gumana ang nakakagambala, kailangan mong alisin ang problema sa pag-debounce ng pushbutton gamit ang millis o micros function at sa pamamagitan ng pag-aayos ng oras ng pag-debit. Mula sa pagpapaandar na ito, tawagan ang interruptHandler () na pagpapaandar tulad ng ipinakita sa ibaba.
mahabang debouncing_time = 150; pabagu-bago na hindi pinirmahan matagal na huling_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
Sa pagpapaandar ng interruptHandler () , tawagan ang xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Ang pagpapaandar na ito ay magbibigay ng isang semaphore sa TaskLed upang i-ON ang LED.
5. Lumikha ng isang function na TaskLed at sa loob ng habang loop, tawagan ang xSemaphoreTake () API at suriin kung matagumpay na nakuha ang semaphore o hindi. Kung ito ay katumbas ng pdPASS (ibig sabihin 1) pagkatapos ay gawin ang LED na toggle tulad ng ipinakita sa ibaba.
walang bisa ang TaskLed (walang bisa * pvParameter) { (walang bisa) pvParameter; pinMode (8, OUTPUT); habang (1) { kung (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Gayundin, lumikha ng isang pagpapaandar upang kumurap ng iba pang LED na konektado sa pin 7.
walang bisa ang TaskLed1 (walang bisa * pvParameter) { (walang bisa) pvParameter; pinMode (7, OUTPUT); habang (1) { digitalWrite (7, TAAS); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Ang pag-andar ng void loop ay mananatiling walang laman. Huwag kalimutan ito.
void loop () {}
Iyon lang, ang kumpletong code ay matatagpuan sa pagtatapos ng tutorial na ito. Ngayon, i-upload ang code na ito at ikonekta ang mga LED at push-button sa Arduino UNO alinsunod sa diagram ng circuit.
Diagram ng Circuit
Matapos i-upload ang code, makikita mo ang isang LED na kumikislap pagkatapos ng 200ms at kapag pinindot ang pindutan, kaagad ang ikalawang LED ay mamula tulad ng ipinakita sa video na ibinigay sa dulo.
Sa ganitong paraan, maaaring magamit ang mga semaphore sa FreeRTOS kasama ang Arduino kung saan kailangan nitong ipasa ang data mula sa isang gawain patungo sa isa pa nang walang anumang pagkawala.
Ngayon, tingnan natin kung ano ang Mutex at kung paano ito magagamit na FreeRTOS.
Ano ang Mutex?
Tulad ng ipinaliwanag sa itaas semaphore ay isang mekanismo ng pagbibigay ng senyas, katulad, ang Mutex ay isang mekanismo ng pagla-lock hindi katulad ng semaphore na may magkakahiwalay na pagpapaandar para sa pagtaas at pagbawas ngunit sa Mutex, tumatagal ang pagpapaandar at nagbibigay sa sarili nito. Ito ay isang pamamaraan upang maiwasan ang katiwalian ng ibinahaging mga mapagkukunan.
Upang maprotektahan ang nakabahaging mapagkukunan, nagtatalaga ang isang token card (mutex) sa mapagkukunan. Sinumang mayroong card na ito ay maaaring ma-access ang iba pang mapagkukunan. Dapat maghintay ang iba hanggang sa mabalik ang kard. Sa ganitong paraan, isang mapagkukunan lamang ang maaaring ma-access ang gawain at ang iba ay naghihintay para sa kanilang pagkakataon.
Unawain natin ang Mutex sa FreeRTOS sa tulong ng isang halimbawa.
Narito mayroon kaming tatlong mga gawain, Isa para sa pag-print ng data sa LCD, pangalawa para sa pagpapadala ng data ng LDR sa gawain ng LCD at huling gawain para sa pagpapadala ng data ng Temperatura sa LCD. Kaya narito ang dalawang gawain ay nagbabahagi ng parehong mapagkukunan ibig sabihin LCD. Kung ang gawain ng LDR at gawain sa temperatura ay magpadala ng data nang sabay-sabay pagkatapos ang isa sa data ay maaaring masira o mawala.
Kaya upang maprotektahan ang pagkawala ng data, kailangan naming i-lock ang mapagkukunan ng LCD para sa task1 hanggang matapos ang pagpapakita ng gawain. Pagkatapos ang gawain ng LCD ay bubuksan at pagkatapos ay maaaring maisagawa ng task2 ang gawain nito.
Maaari mong obserbahan ang pagtatrabaho ng Mutex at semaphores sa diagram sa ibaba.
Paano gamitin ang Mutex sa FreeRTOS?
Ginagamit din ang mga Mutex sa parehong paraan tulad ng mga semaphore. Una, likhain ito, pagkatapos ay magbigay at kumuha gamit ang kani-kanilang mga API.
Lumilikha ng isang Mutex:
Upang lumikha ng isang Mutex, gumamit ng xSemaphoreCreateMutex () API . Tulad ng ipinahihiwatig ng pangalan nito na ang Mutex ay isang uri ng Binary semaphore. Ginagamit ang mga ito sa iba't ibang mga konteksto at layunin. Ang isang binary semaphore ay para sa pag-syncing ng mga gawain habang ginagamit ang Mutex para sa pagprotekta sa isang ibinahaging mapagkukunan.
Ang API na ito ay hindi kumukuha ng anumang argumento at nagbabalik ng isang variable ng uri na SemaphoreHandle_t . Kung ang mutex ay hindi maaaring malikha, xSemaphoreCreateMutex () ibalik ang Null.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Pagkuha ng isang Mutex:
Kapag nais ng isang gawain na mag-access sa isang mapagkukunan, kukuha ito ng isang Mutex sa pamamagitan ng paggamit ng xSemaphoreTake () API. Ito ay kapareho ng isang binary semaphore. Tumatagal din ito ng dalawang mga parameter.
xSemaphore: Pangalan ng Mutex na kukuha sa aming kaso mutex_v .
xTicksToWait: Ito ang maximum na dami ng oras na maghihintay ang gawain sa Naka- block na estado para sa magagamit na Mutex. Sa aming proyekto, itatakda namin ang xTicksToWait sa portMAX_DELAY upang gawin ang gawain_1 na maghintay nang walang katiyakan sa Naka-block na estado hanggang sa magamit ang mutex_v .
Pagbibigay ng isang Mutex:
Matapos ma-access ang nakabahaging mapagkukunan, dapat ibalik ng gawain ang Mutex upang ma-access ito ng iba pang mga gawain. Ginagamit ang xSemaphoreGive () API upang ibalik ang Mutex.
Ang pagpapaandar ng xSemaphoreGive () ay tumatagal lamang ng isang argument na kung saan ay ang Mutex na ibibigay sa aming kaso mutex_v.
Gamit ang mga nasa itaas na API, Ipatupad natin ang Mutex sa FreeRTOS code gamit ang Arduino IDE.
Paliwanag sa Mutex Code
Dito ang layunin para sa bahaging ito ay ang paggamit ng isang Serial monitor bilang isang nakabahaging mapagkukunan at dalawang magkakaibang mga gawain upang ma-access ang serial monitor upang mai-print ang ilang mensahe.
1. Ang mga file ng header ay mananatiling kapareho ng isang semaphore.
# isama ang # isama
2. Ipahayag ang isang variable ng uri ng SemaphoreHandle_t upang maiimbak ang mga halaga ng Mutex.
SemaphoreHandle_t mutex_v;
3. Sa void setup (), simulan ang serial monitor na may 9600 baud rate at lumikha ng dalawang gawain (Task1 at Task2) gamit ang xTaskCreate () API. Pagkatapos ay lumikha ng isang Mutex gamit ang xSemaphoreCreateMutex (). Lumikha ng isang gawain na may pantay na mga priyoridad at sa paglaon subukang i-play sa numerong ito.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); kung (mutex_v == NULL) { Serial.println ("Hindi maaaring malikha ang Mutex"); } xTaskCreate (Gawain1, "Gawain 1", 128, NULL, 1, NULL); xTaskCreate (Gawain2, "Gawain 2", 128, NULL, 1, NULL); }
4. Ngayon, gumawa ng mga pagpapaandar sa gawain para sa Task1 at Task2. Sa isang habang loop ng gawain function, bago ang pag-print ng isang mensahe sa serial monitor kailangan naming kumuha ng isang Mutex gamit ang xSemaphoreTake () pagkatapos ay i-print ang mensahe at pagkatapos ay ibalik ang Mutex gamit ang xSemaphoreGive (). Pagkatapos ay magbigay ng ilang pagkaantala.
walang bisa ang Task1 (walang bisa * pvParameter) { habang (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Kumusta mula sa Task1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Katulad nito, ipatupad ang paggana ng Task2 na may pagkaantala ng 500ms.
5. Void loop () ay mananatiling walang laman.
Ngayon, i-upload ang code na ito sa Arduino UNO at buksan ang serial monitor.
Makikita mo ang mga mensahe ay nagpi-print mula sa task1 at task2.
Upang subukan ang pagtatrabaho ng Mutex, magkomento lamang xSemaphoreGive (mutex_v); mula sa anumang gawain. Maaari mong makita na ang programa ay nakasabit sa huling naka-print na mensahe .
Ito ay kung paano maipapatupad ang Semaphore at Mutex sa FreeRTOS kasama ang Arduino. Para sa karagdagang impormasyon tungkol sa Semaphore at Mutex, maaari mong bisitahin ang opisyal na dokumentasyon ng FreeRTOS.
Ang mga kumpletong code at video para sa Semaphore at Mutes ay ibinibigay sa ibaba.