Bei einem Mikrocontroller, auf dem der folgende Code ausgeführt wird:
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
}
}
Angenommen, die if(has_flag)
Bedingung wird als falsch ausgewertet und wir sind dabei, die Schlafanweisung auszuführen. Kurz bevor wir den Schlafbefehl ausführen, erhalten wir eine Unterbrechung. Nachdem wir den Interrupt verlassen haben, führen wir die Schlafanweisung aus.
Diese Ausführungssequenz ist nicht wünschenswert, weil:
- Der Mikrocontroller schlief ein, anstatt aufzuwachen und anzurufen
process()
. - Der Mikrocontroller wird möglicherweise nie aktiviert, wenn danach keine Unterbrechung empfangen wird.
- Der Anruf an
process()
wird auf den nächsten Interrupt verschoben.
Wie kann der Code geschrieben werden, um das Auftreten dieser Rennbedingung zu verhindern?
Bearbeiten
Einige Mikrocontroller, wie z. B. das ATMega, verfügen über ein Sleep-Aktivierungsbit, das das Auftreten dieses Zustands verhindert (danke Kvegaoro, dass Sie darauf hingewiesen haben). JRoberts bietet eine Beispielimplementierung, die dieses Verhalten veranschaulicht.
Andere Mikros, wie bei PIC18s, haben dieses Bit nicht und das Problem tritt immer noch auf. Diese Mikros sind jedoch so konzipiert, dass Interrupts den Kern immer noch aufwecken können, unabhängig davon, ob das globale Interrupt-Aktivierungsbit gesetzt ist (danke Supercat, dass Sie darauf hingewiesen haben). Für solche Architekturen besteht die Lösung darin, globale Interrupts unmittelbar vor dem Einschlafen zu deaktivieren. Wenn ein Interrupt unmittelbar vor dem Ausführen des Sleep-Befehls ausgelöst wird, wird der Interrupt-Handler nicht ausgeführt, der Kern wird aktiviert, und sobald globale Interrupts wieder aktiviert werden, wird der Interrupt-Handler ausgeführt. Im Pseudocode würde die Implementierung folgendermaßen aussehen:
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
als ein int
und erhöhe es jedes Mal dort Unterbrechung. Dann wechseln Sie if(has_flag)
zu while (interrupts_count)
und schlafen Sie dann. Der Interrupt kann jedoch auftreten, nachdem Sie die while-Schleife verlassen haben. Wenn dies ein Problem ist, führt die Verarbeitung dann den Interrupt selbst durch?