Biorąc pod uwagę mikrokontroler, który uruchamia następujący kod:
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
}
}
Załóżmy, że if(has_flag)
warunek ma wartość false, a my zamierzamy wykonać instrukcję uśpienia. Tuż przed wykonaniem instrukcji snu otrzymujemy przerwanie. Po wyjściu z przerwania wykonujemy instrukcję snu.
Ta sekwencja wykonywania jest niepożądana, ponieważ:
- Mikrokontroler poszedł spać zamiast budzić się i dzwonić
process()
. - Mikrokontroler może nigdy się nie obudzić, jeśli nie zostanie odebrane żadne przerwanie.
- Połączenie z urządzeniem
process()
jest odraczane do następnego przerwania.
Jak można napisać kod, aby zapobiec wystąpieniu tego stanu wyścigu?
Edytować
Niektóre mikrokontrolery, takie jak ATMega, mają bit aktywacji uśpienia, który zapobiega występowaniu tego stanu (dziękuję Kvegaoro za zwrócenie na to uwagi). JRoberts oferuje przykładową implementację, która ilustruje to zachowanie.
Inne mikroskopy, takie jak PIC18, nie mają tego bitu i problem nadal występuje. Jednak te mikroskopy są zaprojektowane w taki sposób, że przerwania mogą nadal budzić rdzeń, niezależnie od tego, czy ustawiony jest bit globalnego włączania przerwań (dziękuję supercat za wskazanie tego). W przypadku takich architektur rozwiązaniem jest wyłączenie globalnych przerwań tuż przed snem. Jeśli przerwanie zostanie wyzwolone tuż przed wykonaniem instrukcji uśpienia, procedura obsługi przerwań nie zostanie wykonana, rdzeń się obudzi, a gdy globalne przerwania zostaną ponownie włączone, procedura obsługi przerwań zostanie wykonana. W pseudokodzie implementacja wyglądałaby następująco:
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
jako int
, a przyrost to za każdym razem istnieje przerwanie. Następnie zmień if(has_flag)
na while (interrupts_count)
a potem spać. Niemniej jednak przerwanie może wystąpić po wyjściu z pętli while. Jeśli jest to problem, to czy samo przetwarzanie jest przetwarzane?