Când depanem aplicațiile încorporate, este mai greu să prinzi erori care nu sunt întotdeauna permanente, dar numai din când în când. Unul dintre motivele acestor erori: variabile care sunt accesate în mod asincron. Astfel de variabile trebuie să fie bine definite și să aibă o protecție adecvată.
Definiția trebuie să includă cuvântul cheie volatil. Aceasta informează compilatorul că o variabilă poate fi modificată nu numai din codul executabil curent, dar și din alte locuri. Apoi compilatorul va evita anumite optimizări ale acestei variabile.
Pentru a proteja o variabilă comună, fiecare operație de acces trebuie să fie atomică. Adică, nu ar trebui întrerupt până la sfârșit. De exemplu, accesul la o variabilă pe 32 de biți sau 16 biți pe o arhitectură pe 8 biți nu este atomică, deoarece operațiile de citire sau scriere necesită mai mult de o instrucțiune.
Luați în considerare un exemplu tipic al unei variabile publice - un cronometru de program. În dispozitivul de tratare a întreruperii, valoarea sa este modificată, iar în codul principal este citită. În cazul în care un manipulator de alte întreruperi sunt dezactivate, de exemplu, în mod implicit făcut în microcontrolere AVR, funcționarea variabila este atomică și nu există stocuri nu se va întâmpla.
volatile nesemnate lung system_timer = 0;
#pragma vector = TIMER0_COMP_vect
__interruptrupt Timer0CompVect (void)
system_timer ++;
>
Pe de altă parte, în bucla principală, programele de întrerupere sunt cel mai adesea rezolvate, iar versiunea codului nesigur ar putea arăta astfel:
dacă (system_timer> = next_cycle)
next_cycle + = 100;
do_ ceva ();
>
Acest cod nu este sigur, deoarece funcționarea citirii variabilei system_timer nu este atomică. În timp ce citim un octet al variabilei system_timer, poate să apară o întrerupere TIMER0_COMP și manipularea va schimba valoarea sa. Apoi, la revenirea la programul principal, vom citi restul variabilei deja de la noua sa valoare. În unele cazuri, amestecul de la valorile vechi și noi nu va cauza eșecuri, dar în altele poate afecta foarte mult comportamentul programului. Ei bine, de exemplu, în cazul în care valoarea veche a fost system_timer 0x00FFFFFF, iar noul 0x01000000.
Pentru a proteja accesul la variabila sistem_timer, puteți utiliza funcția de monitorizare, pentru a face acest lucru, cuvântul cheie _monitor este specificat înainte de numele funcției.
dacă (get_system_timer ()> = next_cycle)
next_cycle + = 100;
do_ ceva ();
>
Funcția de monitorizare - o funcție care se află la intrarea păstrează SREG registru, dezactivează întreruperile pe durata executării sale, și înainte de eliberarea conținutului de restaurare SREG.
Dacă doriți ca întreruperile să fie dezactivate într-o anumită zonă a codului, puteți utiliza funcții intrinseci.
...
nesemnate tmp lung;
nesemnate char oldState;
oldState = __save_interrupt (); // salvați registrul SREG
__disable_interrupt (); // interzicerea întreruperilor
tmp = system_timer; // citiți valoarea sistemului_timer în variabila temporară __restore_interrupt (oldState); // restaura SREG
Instrumentele C ++ vă permit să integrați această logică în clasă.
Mutex ()
__restore_interrupt (actual_state);
>
nesemnate tmp lung;
Mutex m; // crea un obiect de clasă, acum accesul va fi atomic
tmp = system_timer; salvați sistemul_timer într-o modificare temporară
>
Când creați obiectul m, constructorul va stoca registrul SREG și va dezactiva întreruperile. La sfârșitul blocului, distrugătorul va restabili conținutul SREG. Frumoasă, nu?
În general, principiul este peste tot, dar există multe opțiuni pentru implementare. De exemplu, când accesați o variabilă, nu puteți interzice toate întreruperile, ci doar cele care utilizează această variabilă.
Asta e tot. Toate depanarea bună.
Problema este posibilă cu o variabilă pe 8 biți. Funcționare ca system_timer - = 100 Campiglio câteva instrucțiuni de asamblare și în codul principal poate fi, de asemenea, întrerupt între citirea system_timer și înregistrează rezultatele.
---------
Există un alt mod de a citi multi-byte contoare asincrone (fără a dezactiva întrerupe) - citește o variabilă de două ori și a compara toate bytes, cu excepția cei mai tineri. În cazul în care octeți în copiile sunt - vom lua ultima valoare de citire în cazul în care nu este egal - citește până în ultimele două valori de citire ale octeți sunt egale. Contor scăzut octet între citirile pot varia în timp, fără transport, deci nu este implicat în verificarea.
După cum puteți vedea din exemple, codul este dat pentru compilatorul IAR. În WinAVR, o problemă similară este rezolvată prin includerea unui fișier. în care macro-urile sunt definite pentru implementarea accesului atomic.
de exemplu:
.
ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
// bloc de cod cu întreruperi interzise
>
.