次のコードを実行しているマイクロコントローラーがあるとします。
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
}
}
if(has_flag)
条件がfalseと評価され、sleep命令を実行しようとしているとします。 右我々はスリープ命令を実行する前に、我々は、割り込みを受けます。割り込みを離れた後、sleep命令を実行します。
次の理由により、この実行シーケンスは望ましくありません。
- マイクロコントローラーは、目を覚まして呼び出すのではなく、スリープ状態になりました
process()
。 - その後に割り込みが受信されない場合、マイクロコントローラが起動しないことがあります。
- への呼び出し
process()
は、次の割り込みまで延期されます。
この競合状態の発生を防ぐために、コードをどのように記述できますか?
編集
ATMegaなどの一部のマイクロコントローラーには、この状態の発生を防ぐスリープイネーブルビットがあります(指摘してくれたKvegaoroに感謝)。JRobertsは、この動作を例示する実装例を提供しています。
PIC18のような他のマイクロにはこのビットがなく、問題はまだ発生します。ただし、これらのマイクロは、グローバル割り込み有効化ビットが設定されているかどうかに関係なく、割り込みがコアをウェイクアップできるように設計されています(これを指摘してくれてありがとう)。このようなアーキテクチャの場合、解決策は、スリープ状態になる直前にグローバル割り込みを無効にすることです。スリープ命令を実行する直前に割り込みが発生した場合、割り込みハンドラーは実行されず、コアがウェイクアップし、グローバル割り込みが再度有効になると、割り込みハンドラーが実行されます。疑似コードでは、実装は次のようになります。
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
、int
割り込みがあるたびにインクリメントします。次にをに変更しif(has_flag)
てwhile (interrupts_count)
から、スリープします。それにもかかわらず、whileループを終了した後に割り込みが発生する可能性があります。これが問題である場合、割り込み自体で処理を行いますか?