Ho fatto e testato uno script per rilevare il flusso di un liquido, che sembrava funzionare bene, ma le due variabili che contano i giri dei flussometri non erano dichiarate come volatile. Oggi leggendo le reference, mi sono accorto se sarebbe stato corretto dichiarare le variabile modificate dalla routine degli interrupt come volatile. Ma in pratica, in che errore sono caduto? posso capire quanto erano distorte le mie rilevazioni?
Il compilatore cerca di ottimizzare il codice che produce, per esempio può mettere il contenuto di una variabile dentro un registro che risiede nella cpu, registro che viene acceduto molto rapidamente e la cosa funziona fino a che non viene eseguito del codice concorrente a seguito di una richiesta di interrupt.
Aggiungere "volatile" ad una dichiarazione equivale a dire al compilatore di non ottimizzare l'accesso a questa variabile ma di leggere sempre il valore dalla ram anche se il compilatore ha fatto una analisi che gli dice che può salvare il valore in un registro.
L'accesso ad una risorsa condivisa (la variabile) da codice interno ad una ISR(interrupt service request) e da codice esterno alla ISR viene detto accesso concorrente ad una risorsa e dovrebbe essere gestito in modo particolare, al fine di evitare comportamenti del software non voluto.
In sostanza e come se tu avessi fatto un versamento sul tuo conto corrente al fine di fornire liquidità per l'accredito di bollette sul conto, ma tua moglie ha fatto un accesso concorrente al tuo conto e l'ha svuotato.
Documentati qui:
http://www.nongnu.org/avr-libc/user-manual/optimization.html
http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
Ciao.
Quindi non è possibile sapere a priori ne a posteriori quale sia stato l'effetto distorto o meglio l'importo che mia moglie mi ha rubato dal conto..?
Le mogli non rubano, fanno prelievi a fondo perduto
leo72:
Le mogli non rubano, fanno prelievi a fondo perduto
Al di la della battuta, ritorno in tema usando sempre l'esempio della moglie che preleva a tua insaputa non entrando nel merito delle sue intenzioni. Chissà magari, ti stavano pignorando l'auto e tua moglie ti ha parato il popò.
Se variabile non è dichiarata volatile e da diversi contesti accedi alla variabile condivisa il compilatore se lo ritiene opportuno metter il valore di questa variabile in un registro interno, es valore 10, e lo salva i r21 es, quando incontra il codice r21++, incrementa il valore 10 che diventa 11, ma ancora non ha salvato il dato in memoria perché il contesto (es una funzione o altro blocco di codice {}) ancora non è terminato, in questo frangente una interruzione (interrupt) avviene e la conseguenza e che la cpu smette di eseguire il contesto corrente e passa ad eseguire codice concorrente, ovviamente se accede alla variabile in memoria (cosa che fa la prima volta) trova il valore 10 non 11.
Il problema è tutto li, basta dirgli di non mettere quella variabile in un registro dichiarandola volatile.
Tuttavia c'è ancora un problema di carattere funzionale a cui bisogna prestare attenzione:
Se la variabile volatile viene usata come indice di accesso per un array, sia il codice in loop che quello in ISR possono
modificare il contenuto della variabile, es i++ messo nel loop e i++ messo nella ISR, entrambe non le istruzioni non riescono ad incrementare realmente di una unita i, ma di 2 o più tutto dipende da quante volte al secondo il codice
della ISR viene eseguito.
La soluzione e quella di proteggere il codice nel loop o comunque fuori dalla ISR con una serie di istruzioni poste in macro fornite dalla libreria C di AVR, sostanzialmente equivale a spegnere globalmente gli interrupt, modificare il valore della variabile e infine riabilitare globalmente gli interrupt.
Il pezzo di codice che segue usa questa tecnica:
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
buffer_display[0] &= ~CHARminus;
buffer_display[1] = 0x0;
buffer_display[2] = 0x0;
buffer_display[3] = 0x0;
while (widx < N_DIGIT) {
if ((*s == '-') && (widx == 0))
buffer_display[widx] |= CHARminus;
else
buffer_display[widx] = decode_char(*s);
++s;
if(*s == '.') {
buffer_display[widx] |= CHARdot;
++s;
}
++widx;
}
} // ATOMIC_RESTORESTATE
Il codice contenuto nel blocco ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {}, cioè il codice che si trova dentro le parentesi graffe, si dice essere codice atomico, che è non interrompibile da eventi. Quindi ATOMIC_BLOCK(ATOMIC_RESTORESTATE) forza l'atomicità di un blocco di codice che di suo non è atomico.
In sostanza dici al banchiere, nessuno può prelevare dal conto prima che le bollette vengono accreditate e quindi pagate. Cosa che può essere bene o male, tipo: le bollette sono state pagate, ma la tua auto è pignorata. :0
Ciao.
Comunque le ISR il compilatore avr-gcc di default le rende tutte atomiche, sei tu che esplicitamente devi renderle non atomiche.
Inoltre, sempre di default, vengono inserite le istruzioni per disattivare gli interrupt globali e salvare lo stato del sistema all'ingresso nella ISR e fare le operazioni inverse all'uscita. Questo vale però solo per le ISR "normali", se si sceglie ISR nude e crude (NACKED) è poi a cura dell'utente effettuare i salvataggi e ripristinare lo stato dei registri.
http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html