マイクロコントローラーの割り込み処理とFSMの例


9

最初の質問

マイクロコントローラでの割り込みの処理について、一般的な質問があります。私はMSP430を使用していますが、質問は他のuCにも当てはまると思います。コードに沿って頻繁に割り込みを有効/無効にするのが良い方法かどうか知りたいのですが。つまり、割り込みの影響を受けないコードの一部がある場合(または、さらに悪いことに、何らかの理由で割り込みをリッスンしてはならない場合)、次のことを行う方がよいでしょう。

  • クリティカルセクションの前に割り込みを無効にしてから、再度有効にします。
  • それぞれのISR内にフラグを設定し、(割り込みを無効にする代わりに)クリティカルセクションの前にフラグをfalseに設定し、直後にtrueにリセットします。ISRのコードが実行されないようにするため。
  • どちらもないので、提案は大歓迎です!

更新:割り込みと状態チャート

具体的な状況をお知らせします。4つのブロックで構成されるステートチャートを実装するとします。

  1. 遷移/効果。
  2. 終了条件。
  3. エントリー活動。
  4. 活動を行う。

これは大学で教授が私たちに教えたものです。おそらく、それを行う最良の方法は、このスキームに従うことではありません。

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

また、たとえばSTATE_A、(debouceシステムなどを使用した)ボタンのセットからの割り込みに敏感になりたいとしましょう。誰かがこれらのボタンの1つを押すと、割り込みが生成され、入力ポートに関連するフラグが変数にコピーされますbuttonPressed。デバウンスが何らかの方法で200ミリ秒に設定されている場合(ウォッチドッグタイマー、タイマー、カウンターなど)、buttonPressed200ミリ秒より前に新しい値で更新することはできません。これは私があなたに(そして私自身ももちろん)尋ねていることです)

終了STATE_Aする前に、DOアクティビティの割り込みを有効にし、無効にする必要がありますか?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

次の繰り返しでブロック1(遷移/効果)を次回実行するときに、遷移に沿ってチェックされた条件が、その前の値を上書きした後続の割り込みから来ていないことを確信してbuttonPressedいる方法で必要(250ミリ秒が経過する必要があるため、これが発生することは不可能ですが)。


3
自分の状況を知らずに推奨するのは難しいです。ただし、組み込みシステムで割り込みを無効にする必要がある場合があります。割り込みを見逃さないように、割り込みを短時間だけ無効にしておくことをお勧めします。すべての割り込みではなく、特定の割り込みのみを無効にできる場合があります。あなたが説明したようにISR内フラグ技術を使用したことは一度もありません。
kkrambo

回答:


17

最初の戦術は、いつでも割り込みが発生しても問題がないように、全体的なファームウェアを設計することです。フォアグラウンドコードがアトミックシーケンスを実行できるように、割り込みをオフにする必要がある場合は、慎重に行う必要があります。多くの場合、それを回避するためのアーキテクチャ上の方法があります。

ただし、マシンはサービスを提供するためのものであり、その逆ではありません。一般的な経験則は、悪いプログラマが本当に悪いコードを書かないようにすることだけです。マシンがどのように機能するかを正確に理解し、それらの機能を利用して目的のタスクを実行するための優れた方法を構築する方がはるかに優れています。

サイクルやメモリの場所が本当にタイトでない限り(確かに発生する可能性があります)、それ以外の場合は、コードの明確さと保守性を最適化する必要があることに注意してください。たとえば、クロックティック割り込みで32ビットカウンターを更新する16ビットマシンがある場合、フォアグラウンドコードがカウンターを読み取るときに、その2つの半分が一致していることを確認する必要があります。1つの方法は、割り込みを停止し、2つのワードを読み取ってから、割り込みを再びオンにすることです。割り込みレイテンシが重要でない場合は、それで問題ありません。

割り込みレイテンシを低くする必要がある場合、たとえば、上位ワードを読み取り、下位ワードを読み取り、上位ワードを再度読み取り、変更された場合は繰り返すことができます。これにより、フォアグラウンドコードが少し遅くなりますが、割り込みのレイテンシはまったく追加されません。さまざまな小さなトリックがあります。もう1つは、カウンターをインクリメントする必要があることを示すフラグを割り込みルーチンに設定し、フォアグラウンドコードのメインイベントループでそれを行うことです。フラグが再度設定される前にイベントループがインクリメントを実行するようにカウンターの割り込みレートが十分に遅い場合、これは正常に機能します。

または、フラグの代わりに1ワードのカウンターを使用します。フォアグラウンドコードは、システムを更新した最後のカウンターを含む個別の変数を保持します。ライブカウンターから保存された値を差し引いた符号なし減算を実行して、一度に処理する必要があるティック数を決定します。これにより、フォアグラウンドコードは一度に最大2 N -1個のイベントを見逃すことができます。Nは、ALUがアトミックに処理できるネイティブワードのビット数です。

それぞれの方法には、独自の長所と短所があります。正解は1つではありません。繰り返しになりますが、機械がどのように機能するかを理解すれば、経験則は必要ありません。


7

クリティカルセクションが必要な場合は、クリティカルセクションを保護する操作がアトミックであり、中断できないことを確認する必要があります。

したがって、割り込みを無効にすることは、通常、単一のプロセッサ命令によって処理され(コンパイラの組み込み関数を使用して呼び出されます)、実行できる最も安全な方法の1つです。

システムによっては、割り込みが失われるなど、いくつかの問題が発生する可能性があります。一部のマイクロコントローラは、グローバル割り込みイネーブルの状態に関係なくフラグを設定し、クリティカルセクションを終了した後、割り込みが実行され、遅延されるだけです。しかし、高い頻度で発生する割り込みがある場合、長時間割り込みをブロックすると、割り込みが再度発生したときに失敗する可能性があります。

クリティカルセクションで、1つの割り込みだけを実行せずに、他の割り込みを実行する必要がある場合、他のアプローチが実行可能と思われます。

割り込みサービスルーチンのプログラミングは、できる限り短いことに気づきました。したがって、通常のプログラムルーチン中にチェックされるフラグを設定するだけです。ただし、その場合は、フラグが設定されるのを待つ間、競合状態に注意してください。

多くのオプションがあり、確かにこれに対する単一の正解ではありません。これは、慎重な設計を必要とし、他のものよりも少し考える価値があるトピックです。


5

コードのセクションを中断せずに実行する必要があると判断した場合は、異常な状況を除いて、タスクを完了するために可能な限り最小限の時間割り込みを無効にし、その後再び有効にする必要があります。

それぞれのISR内にフラグを設定し、(割り込みを無効にする代わりに)クリティカルセクションの前にフラグをfalseに設定し、直後にtrueにリセットします。ISRのコードが実行されないようにするため。

これにより、割り込み、コードジャンプ、チェック、リターンの発生が可能になります。コードがこれほど多くの中断を処理できる場合は、おそらくチェックを実行するのではなくフラグを設定するようにISRを設計する必要があります(フラグは短くなります)。通常のコードルーチンでフラグを処理します。これは、誰かが割り込みにあまりにも多くのコードを入れたように聞こえ、彼らは通常のコードで行われるべきより長いアクションを実行するために割り込みを使用しています。

割り込みが長いコードを処理している場合は、提案されているフラグで問題が解決する可能性がありますが、コードをリファクタリングして割り込み内の過剰なコードを排除できない場合は、割り込みを無効にすることをお勧めします。 。

フラグの方法でそれを行うことの主な問題は、割り込みをまったく実行しないことです。これは後で影響を与える可能性があります。ほとんどのマイクロコントローラーは、割り込みがグローバルに無効になっている場合でも割り込みフラグを追跡し、割り込みを再度有効にすると割り込みを実行します。

  • クリティカルセクション中に割り込みが発生しなかった場合は、何も実行されません。
  • クリティカルセクション中に1つの割り込みが発生した場合、1つ後に実行されます。
  • クリティカルセクション中に複数の割り込みが発生した場合、その後に実行されるのは1つだけです。

システムが複雑で、割り込みをより完全に追跡する必要がある場合は、より複雑なシステムを設計して、割り込みを追跡し、それに応じて操作する必要があります。

ただし、その機能を実現するために必要な最小限の作業を実行するように割り込みを常に設計し、他のすべてを通常の処理まで遅らせる場合、割り込みが他のコードに悪影響を与えることはほとんどありません。必要に応じて割り込みをキャプチャまたはデータを解放するか、必要に応じて出力などを設定/リセットしてから、メインコードパスでフラグ、バッファ、および割り込みが影響する変数に注意を払い、メインループで長い処理を実行できるようにします。割り込みではなく。

これにより、中断されないコードセクションが必要になる可能性のあるごくわずかな状況を除いて、すべてが排除されます。


私は私が働いている状況をよりよく説明するために投稿を更新しました:)
ウンベルトD.

1
あなたのサンプルコードでは、不要なときに特定のボタン割り込みを無効にし、必要なときに有効にすることを検討します。これを頻繁に行うことは、仕様による問題ではありません。必要に応じて後で他の割り込みをコードに追加できるように、グローバル割り込みをオンのままにします。または、状態Aに進んだときにフラグをリセットし、それ以外の場合は無視します。ボタンが押され、フラグが設定されている場合、誰が気にしますか?あなたが戻って状態Aになるまでそれを無視する
アダムデイビス

はい!実際の設計では、グローバル割り込みを有効にしてLPM3(MSP430)に頻繁にアクセスし、割り込みが検出されるとすぐにLPM3を終了して実行を再開するため、これが解決策になる場合があります。したがって、ソリューションはあなたが提示したものであると私が考えているものです。コードの2番目の部分で報告されています。それが必要な状態のdoアクティビティの実行を開始するとすぐに割り込みを有効にし、transitionsブロックに進む前に無効にします。他の可能な解決策は、「Doアクティビティブロック」を終了する直前に割り込みを無効にし、後で(いつ?)再度有効にすることですか?
ウンベルトD.

1

説明したようにISR内にフラグを設定しても、基本的には割り込みをトリガーしたイベントを無視しているため、機能しません。通常、割り込みをグローバルに無効にすることをお勧めします。他の人が言ったように、これを頻繁に行う必要はありません。単一の命令を介して行われる読み取りまたは書き込みは、命令が実行されるか、または実行されないため、保護する必要がないことに注意してください。

多くは、共有しようとしているリソースの種類によって異なります。ISRからメインプログラム(またはその逆)にデータを供給する場合は、FIFOバッファーのようなものを実装できます。唯一のアトミック操作は、読み取りポインターと書き込みポインターの更新です。これにより、割り込みを無効にして費やす時間を最小限に抑えることができます。


0

考慮する必要がある微妙な違いがあります。割り込みの処理を「遅延」するか、「無視」して割り込みを行うかを選択できます。

コードでは、割り込みを無効にすることをよく言っています。ハードウェア機能が原因で発生する可能性があるのは、割り込みを有効にするとトリガーされることです。これはある意味で割り込みを遅らせています。システムが確実に機能するためには、これらの割り込みの処理を遅らせることができる最大時間を知る必要があります。そして、割り込みが無効になっているすべてのケースがより短時間で終了することを確認します。

割り込みを無視したい場合があります。最善の方法は、ハードウェアレベルで割り込みをブロックすることです。割り込みを生成する必要がある入力を指定できる割り込みコントローラーなどがあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.