- Què és el semafor?
- Com s'utilitza Semaphore a FreeRTOS?
- Explicació del codi del semàfor
- Esquema de connexions
- Què és Mutex?
- Com s'utilitza Mutex a FreeRTOS?
- Explicació del codi Mutex
En tutorials anteriors, hem tractat els conceptes bàsics de FreeRTOS amb Arduino i l’objecte del nucli de la cua a FreeRTOS Arduino. Ara, en aquest tercer tutorial de FreeRTOS, aprendrem més sobre FreeRTOS i les seves API avançades, que us poden fer comprendre més profundament la plataforma de tasques múltiples.
Semaphore i Mutex (exclusió mútua) són els objectes del nucli que s’utilitzen per a la sincronització, la gestió de recursos i la protecció dels recursos contra la corrupció. A la primera meitat d’aquest tutorial, veurem la idea darrere de Semaphore, com i on utilitzar-lo. A la segona meitat, continuarem amb Mutex.
Què és el semafor?
En tutorials anteriors, hem debatut sobre les prioritats de la tasca i també hem sabut que una tasca de prioritat més alta impedeix una tasca de prioritat inferior, de manera que, mentre s’executa una tasca de màxima prioritat, pot haver-hi la possibilitat que es produeixi una corrupció de dades en tasques de prioritat inferior perquè encara no s'ha executat i les dades arriben contínuament a aquesta tasca des d'un sensor que provoca la pèrdua de dades i el mal funcionament de tota l'aplicació.
Per tant, cal protegir els recursos de la pèrdua de dades i, en aquest cas, el semafor té un paper important.
El semafor és un mecanisme de senyalització en què una tasca en estat d’espera és assenyalada per una altra tasca per a la seva execució. Dit d’una altra manera, quan una tasca1 hagi acabat el seu treball, mostrarà un senyalador o incrementarà un senyalador per 1 i, després, aquesta altra tasca (tasca2) la rebrà mostrant que pot realitzar el seu treball ara. Quan la tasca 2 hagi acabat el seu treball, la bandera es reduirà en 1.
Per tant, bàsicament és un mecanisme de "Donar" i "Prendre" i el semàfor és una variable sencera que s'utilitza per sincronitzar l'accés als recursos.
Tipus de semàfor a FreeRTOS:
El semàfor és de dos tipus.
- Semàfor binari
- Comptant semàfor
1. Semàfor binari: té dos valors enters 0 i 1. És una mica similar a la cua de longitud 1. Per exemple, tenim dues tasques, tasca1 i tasca2. La tasca 1 envia les dades a la tasca 2, de manera que la tasca 2 revisa contínuament l'element de la cua si n'hi ha 1 i, a continuació, pot llegir les dades que ha d'esperar fins que es converteixin en 1. Després de prendre les dades, la tasca 2 disminueix la cua i la converteix en 0 Això vol dir que tasca1 de nou pot enviar les dades a task2.
De l'exemple anterior, es pot dir que el semàfor binari s'utilitza per a la sincronització entre tasques o entre tasques i interrupció.
2. Compte de semàfor: té valors superiors a 0 i es pot pensar en una cua de longitud superior a 1. Aquest semàfor s'utilitza per comptar esdeveniments. En aquest escenari d'ús, un gestor d'esdeveniments "donarà" un semàfor cada vegada que es produeixi un esdeveniment (incrementant el valor del recompte de semàfor), i una tasca de controlador "prendrà" un semàfor cada vegada que processi un esdeveniment (decrementant el valor del recompte de semàfor).
El valor del recompte és, per tant, la diferència entre el nombre d'esdeveniments que s'han produït i el nombre que s'ha processat.
Ara, vegem com utilitzar Semaphore al nostre codi FreeRTOS.
Com s'utilitza Semaphore a FreeRTOS?
FreeRTOS admet diferents API per crear un semàfor, agafar un semàfor i donar un semàfor.
Ara, pot haver-hi dos tipus d’APIs per al mateix objecte del nucli. Si hem de donar semàfor des d'un ISR, llavors no es pot utilitzar l'API normal del semàfor. Heu d'utilitzar API protegides contra interrupcions.
En aquest tutorial, farem servir semàfor binari perquè és fàcil d’entendre i implementar. Com que aquí s’utilitza la funcionalitat d’interrupció, heu d’utilitzar API protegides contra interrupcions a la funció ISR. Quan diem que sincronitzem una tasca amb una interrupció, significa posar la tasca en estat d’execució just després de l’ISR.
Creació d'un semàfor:
Per utilitzar qualsevol objecte del nucli, primer el hem de crear. Per crear un semàfor binari, utilitzeu vSemaphoreCreateBinary ().
Aquesta API no pren cap paràmetre i retorna una variable del tipus SemaphoreHandle_t. Es crea un nom de variable global sema_v per emmagatzemar el semàfor.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Donar un semàfor:
Per donar un semàfor, hi ha dues versions: una per a interrupció i una altra per a la tasca normal.
- xSemaphoreGive (): aquesta API només adopta un argument que és el nom de la variable del semàfor com sema_v tal com es va indicar anteriorment mentre es crea un semàfor. Es pot trucar des de qualsevol tasca normal que vulgueu sincronitzar.
- xSemaphoreGiveFromISR (): Aquesta és la versió de l'API protegida per interrupcions de xSemaphoreGive (). Quan hem de sincronitzar un ISR i una tasca normal, s'hauria d'utilitzar xSemaphoreGiveFromISR () des de la funció ISR.
Prenent un semàfor:
Per prendre un semàfor, utilitzeu la funció API xSemaphoreTake (). Aquesta API té dos paràmetres.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: nom del semàfor a prendre en el nostre cas sema_v.
xTicksToWait: és la quantitat màxima de temps que la tasca esperarà en estat Bloquejat perquè el semàfor estigui disponible. Al nostre projecte, establirem xTicksToWait a portMAX_DELAY per fer que la tasca_1 esperi indefinidament en estat Bloquejat fins que el sema_v estigui disponible.
Ara, fem servir aquestes API i escrivim un codi per realitzar algunes tasques.
Aquí s’interfacen un polsador i dos LED. El polsador actuarà com un botó d’interrupció que s’adjunta al pin 2 d’Arduino Uno. Quan es prem aquest botó, es generarà una interrupció i s'encendrà un LED connectat al pin 8 i, quan el torneu a prémer, s'apagarà.
Per tant, quan es prem el botó, es cridarà xSemaphoreGiveFromISR () des de la funció ISR i la funció xSemaphoreTake () des de la funció TaskLED.
Per fer que el sistema sembli multitarea, connecteu altres LED amb el pin 7, que sempre estaran parpellejant.
Explicació del codi del semàfor
Comencem a escriure codi per obrir l'IDE Arduino
1. Primer, incloeu el fitxer de capçalera Arduino_FreeRTOS.h . Ara, si s’utilitza algun objecte del nucli com a semàfor de cua, també s’ha d’incloure un fitxer de capçalera.
#include #include
2. Declareu una variable de tipus SemaphoreHandle_t per emmagatzemar els valors del semàfor.
SemaphoreHandle_t interruptSemaphore;
3. A void setup (), creeu dues tasques (TaskLED i TaskBlink) mitjançant l'API xTaskCreate () i, a continuació, creeu un semàfor amb xSemaphoreCreateBinary (). Creeu una tasca amb les mateixes prioritats i, posteriorment, intenteu jugar amb aquest número. A més, configureu el pin 2 com a entrada i activeu la resistència de tracció interna i connecteu el pin d'interrupció. Finalment, inicieu el planificador com es mostra a continuació.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); if (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Ara, implementeu la funció ISR. Feu una funció i anomeneu -la igual que el segon argument de la funció attachInterrupt () . Per fer que la interrupció funcioni correctament, heu d’eliminar el problema de rebut del botó mitjançant la funció de millis o micros i ajustant el temps de rebot. Des d'aquesta funció, truqueu a la funció interruptHandler () com es mostra a continuació.
temps de debouncing llarg = 150; volàtil sense signar llarg last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
A la funció interruptHandler () , truqueu a l' API xSemaphoreGiveFromISR () .
void interruptorHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Aquesta funció donarà un semàfor a TaskLed per encendre el LED.
5. Crear un TaskLed funció i dins el temps de bucle, truqueu xSemaphoreTake () de l'API i comprovar si el semàfor es pren o no amb èxit. Si és igual a pdPASS (és a dir, 1), feu que el LED commuti com es mostra a continuació.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, OUTPUT); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. A més, creeu una funció per parpellejar altres LED connectats al pin 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, OUTPUT); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, BAIX); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. La funció de bucle buit romandrà buida. No ho oblideu.
bucle buit () {}
Ja està, es pot trobar el codi complet al final d'aquest tutorial. Ara, pengeu aquest codi i connecteu els LED i el botó amb Arduino UNO segons el diagrama del circuit.
Esquema de connexions

Després de penjar el codi, veureu que un LED parpelleja després de 200 ms i, quan es prem el botó, el segon LED brillarà immediatament, tal com es mostra al vídeo que apareix al final.

D’aquesta manera, els semàfors es poden utilitzar a FreeRTOS amb Arduino on necessita passar les dades d’una tasca a una altra sense cap pèrdua.
Ara, vegem què és Mutex i com utilitzar-lo FreeRTOS.
Què és Mutex?
Com s'ha explicat anteriorment, el semàfor és un mecanisme de senyalització, de manera similar, Mutex és un mecanisme de bloqueig a diferència del semàfor que té funcions separades per incrementar i disminuir, però en Mutex, la funció pren i dóna en si mateixa. És una tècnica per evitar la corrupció de recursos compartits.
Per protegir el recurs compartit, s’assigna una targeta de testimoni (mutex) al recurs. Qui tingui aquesta targeta pot accedir a l’altre recurs. Altres haurien d’esperar fins que la targeta torni. D’aquesta manera, només un recurs pot accedir a la tasca i altres esperen la seva oportunitat.
Comprenem Mutex a FreeRTOS amb l'ajut d'un exemple.
Aquí tenim tres tasques, una per imprimir dades en pantalla LCD, segona per enviar dades LDR a tasca LCD i última tasca per enviar dades de temperatura en pantalla LCD. Així doncs, aquí dues tasques estan compartint el mateix recurs, és a dir, LCD. Si la tasca LDR i la tasca de temperatura envien dades simultàniament, és possible que una de les dades es corrompi o es perdi.
Per protegir la pèrdua de dades, hem de bloquejar el recurs LCD de la tasca 1 fins que finalitzi la tasca de visualització. A continuació, la tasca LCD es desbloquejarà i, a continuació, la tasca 2 podrà realitzar el seu treball.
Podeu observar el funcionament de Mutex i els semàfors al diagrama següent.

Com s'utilitza Mutex a FreeRTOS?
Els mutexs també s’utilitzen de la mateixa manera que els semàfors. Primer, creeu-lo i, a continuació, doneu i preneu utilitzant les API corresponents.
Creació d'un Mutex:
Per crear un Mutex, utilitzeu l' API xSemaphoreCreateMutex () . Com el seu nom indica, Mutex és un tipus de semàfor binari. S'utilitzen en diferents contextos i finalitats. Un semàfor binari serveix per sincronitzar tasques mentre que Mutex s’utilitza per protegir un recurs compartit.
Aquesta API no accepta cap argument i retorna una variable del tipus SemaphoreHandle_t . Si no es pot crear el mutex, xSemaphoreCreateMutex () retorna NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Fer un Mutex:
Quan una tasca vulgui accedir a un recurs, prendrà un Mutex mitjançant l' API xSemaphoreTake () . És el mateix que un semàfor binari. També pren dos paràmetres.
xSemaphore: nom del Mutex que cal prendre en el nostre cas mutex_v .
xTicksToWait: és la quantitat màxima de temps que la tasca esperarà en estat Bloquejat perquè el Mutex estigui disponible. Al nostre projecte, establirem xTicksToWait a portMAX_DELAY per fer que la tasca_1 esperi indefinidament en estat Bloquejat fins que el mutex_v estigui disponible.
Donar un Mutex:
Després d’accedir al recurs compartit, la tasca hauria de retornar el Mutex perquè altres tasques hi puguin accedir. L' API xSemaphoreGive () s'utilitza per retornar el Mutex.
La funció xSemaphoreGive () només adopta un argument que és el Mutex que es donarà en el nostre cas mutex_v.
Mitjançant les API anteriors, implementem Mutex al codi FreeRTOS mitjançant Arduino IDE.
Explicació del codi Mutex
Aquí l'objectiu d'aquesta part és utilitzar un monitor sèrie com a recurs compartit i dues tasques diferents per accedir al monitor sèrie per imprimir algun missatge.
1. Els fitxers de capçalera seguiran sent els mateixos que un semàfor.
#include #include
2. Declareu una variable de tipus SemaphoreHandle_t per emmagatzemar els valors de Mutex.
SemaphoreHandle_t mutex_v;
3. A la configuració nul·la (), inicialitzeu el monitor sèrie amb una velocitat de transmissió de 9600 i creeu dues tasques (Task1 i Task2) mitjançant l' API xTaskCreate () . A continuació, creeu un Mutex mitjançant xSemaphoreCreateMutex (). Creeu una tasca amb les mateixes prioritats i més endavant proveu de jugar amb aquest número.
configuració nul·la () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("No es pot crear Mutex"); } xTaskCreate (Tasca1, "Tasca 1", 128, NULL, 1, NULL); xTaskCreate (Tasca 2, "Tasca 2", 128, NUL, 1, NUL); }
4. Ara, feu funcions de tasca per a la Tasca 1 i la Tasca 2. En un moment de la funció de bucle de tasca, abans d'imprimir un missatge al monitor sèrie hem de prendre un Mutex amb xSemaphoreTake (), després imprimir el missatge i després tornar el Mutex amb xSemaphoreGive (). A continuació, doneu una mica de retard.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hola de la tasca1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
De la mateixa manera, implementeu la funció Task2 amb un retard de 500 ms.
5. El bucle buit () romandrà buit.
Ara, pengeu aquest codi a Arduino UNO i obriu el monitor sèrie.
Veureu que s’estan imprimint missatges des de la tasca 1 i la tasca 2.

Per provar el funcionament de Mutex, només cal que comenteu xSemaphoreGive (mutex_v); de qualsevol tasca. Podeu veure que el programa queda penjat a l’últim missatge d’impressió .

Així es poden implementar Semaphore i Mutex a FreeRTOS amb Arduino. Per obtenir més informació sobre Semaphore i Mutex, podeu visitar la documentació oficial de FreeRTOS.
A continuació es mostren els codis i el vídeo complets de Semaphore and Mutes.
