Dato un microcontrollore che esegue il seguente codice:
volatile bool has_flag = false;
void interrupt(void) //called when an interrupt is received
{
clear_interrupt_flag(); //clear interrupt flag
has_flag = true; //signal that we have an interrupt to process
}
int main()
{
while(1)
{
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
Supponiamo che la if(has_flag)
condizione sia falsa e che stiamo per eseguire l'istruzione sleep. Proprio prima di eseguire l'istruzione sleep, riceviamo un interrupt. Dopo aver lasciato l'interruzione, eseguiamo l'istruzione sleep.
Questa sequenza di esecuzione non è desiderabile perché:
- Il microcontrollore andò a dormire invece di svegliarsi e chiamare
process()
. - Il microcontrollore non può mai svegliarsi se in seguito non viene ricevuto alcun interrupt.
- La chiamata a
process()
viene posticipata fino al successivo interrupt.
Come può essere scritto il codice per evitare che si verifichi questa condizione di competizione?
modificare
Alcuni microcontrollori, come ad esempio l'ATMega, hanno un bit di abilitazione Sleep che impedisce che si verifichi questa condizione (grazie Kvegaoro per averlo segnalato). JRoberts offre un'implementazione di esempio che esemplifica questo comportamento.
Altri micro, come i PIC18, non hanno questo bit e il problema persiste. Tuttavia, questi micro sono progettati in modo tale che gli interrupt possano comunque riattivare il core indipendentemente dal fatto che sia impostato il bit di abilitazione dell'interrupt globale (grazie supercat per averlo segnalato). Per tali architetture, la soluzione è disabilitare gli interrupt globali prima di andare a dormire. Se un interrupt scatta subito prima di eseguire l'istruzione sleep, il gestore degli interrupt non verrà eseguito, il core si riattiverà e, una volta riattivati gli interrupt globali, verrà eseguito il gestore degli interrupt. In pseudo-codice, l'implementazione sarebbe simile a questa:
int main()
{
while(1)
{
//clear global interrupt enable bit.
//if the flag tested below is not set, then we enter
//sleep with the global interrupt bit cleared, which is
//the intended behavior.
disable_global_interrupts();
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
enable_global_interrupts(); //set global interrupt enable bit.
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
interrupt_flag
come int
, e incrementarlo ogni volta che c'è interruzione. Quindi modificare la if(has_flag)
di while (interrupts_count)
sonno e poi. Tuttavia, l'interrupt potrebbe verificarsi dopo essere usciti dal ciclo while. Se questo è un problema, allora l'elaborazione nello stesso interrupt?