割り込み (Interrupt)

割り込みサービスルーチン (ISR:Interrupt Service Routine)

割り込み発生時にそれを処理するのは、割り込み属性でタグ付けされた、引数および戻り値を持たない関数です。

割り込み属性の構文

__attribute__( ( interrupt
  [, shadow      // 高速コンテキスト保存を行う
   | auto_psv    // PSV領域に変数を配置する (既定)
   | no_auto_psv // PSV領域に変数を配置しない
    (
    [ save( symbol-list ) ]  // 割り込み処理内で保存、回復する変数のリスト
    [, irq( irqid ) ]        // 割り込みベクタの指定 
    [, altirq( altirqid ) ]  // 代替割り込みベクタの指定
    [, preprologue( asm ) ]  // コンパイラが生成する関数の直前に挿入するアセンブリコード
    ) ]
  ) )
※ [ ]で囲まれた項目は省略可能です。またinterruptと__interrupt__は同等です。

例えばタイマ1割り込みを処理するISRは、次のように宣言します。

void __attribute__((interrupt)) _T1Interrupt( void );

ISR宣言

または次のようなマクロが定義されているので、

#define _ISR      __attribute__((interrupt))
#define _ISRFAST  __attribute__((interrupt, shadow))

次のようにも記述できます。

void _ISR _T1Interrupt( void );

PSV (Program Space Visibility)

MPLAB C30 v3.0以降では、変数の配置場所にPSV領域を使用するか否かを指定する必要があります。これを明示しないと次のように警告が表示され、auto_psvが指定されたものとして処理されます。

warning: PSV model not specified for '_T1Interrupt';
    assuming 'auto_psv' this may affect latency

これに対処するためには、次のようにPSVを指定します。

void __attribute__((interrupt, auto_psv)) _T1Interrupt( void );

no_auto_psv属性を指定するとconst変数に割り込みハンドラ内からアクセスできなくなりますが、割り込みの呼び出し遅延を抑えられます。

※ 参照[ Microchip MANUAL:MPLAB C30 C Compiler Users Guide(DS51284F) Chapter7.10 ]

ISR記述の指針

  1. 他の関数から呼び出さない (必須)
  2. 他の関数を呼び出さない (推奨)

ISRは関数から抜けるときに、通常のRETURN命令ではなくRETFIE (割り込みからの戻り命令) を使用します。このため割り込み以外から呼び出されると、関数を抜けるときにステータスレジスタなどを破壊します。

またISRから他の関数を呼び出すと、すべてのワーキングレジスタとREPEAT Loop Counterの保存と復帰が行われます。これにより処理が遅延しますので、ISRから他の関数を呼び出さないことが望ましいです。

割り込みフラグ (Interrupt Request Flags)

割り込み発生時に、割り込みフラグが1にセットされます。これは自動でクリアされないため、割り込みサービスルーチン (ISR) を抜ける前に明示的にクリアする必要があります。さもなければ、ISRから抜けた直後に再び割り込みが発生することになります。

割り込みサービスルーチンを抜ける前に、必ず割り込みフラグをクリアすること。

割り込みベクタテーブル

  • IVT (Interrupt Vector Table)
  • AIVT (Alternate Interrupt Vector Table)

割り込み優先順位 (Interrupt Priority Level)

IRQ (Interrupt Request) の割り込みの優先順位は、0から7の8レベルで指定します。既定値は4であり、0と指定することは割り込みを無効とすることと同義です。

同一順位を指定された割り込みは、割り込みベクタテーブルの上位で定義されているものが優先されます。

CPU割り込み優先順位 (IPL:CPU Interrupt Priority Level)

CPU自体にも割り込みの優先順位を設定でき、全体の割り込みを制御するのに使用します。

このCPU割り込み優先順位 (IPL:CPU Interrupt Priority Level) は、マクロを使用して次のように設定できます。

SET_CPU_IPL( 7 );

このマクロは、プロセッサ ヘッダファイル内で次のように定義されています。

#define SET_CPU_IPL(ipl) {       \
  int DISI_save;                 \
                                 \
  DISI_save = DISICNT;           \
  asm volatile ("disi #0x3FFF"); \
  SRbits.IPL = ipl;              \
  DISICNT = DISI_save; } (void) 0;

#define SET_AND_SAVE_CPU_IPL(save_to, ipl) { \
  save_to = SRbits.IPL; \
  SET_CPU_IPL(ipl); } (void) 0;

#define RESTORE_CPU_IPL(saved_to) SET_CPU_IPL(saved_to)

なおIPLの既定値は0であり、IRQのそれは4であるため、初期状態ではすべての割り込みが有効となります。

割り込みの有効/無効 (Interrupt Enable)

各IRQ (Interrupt Request) に対して割り当てられた割り込み有効ビットを1に設定することで、割り込みが有効となります。

また割り込み優先順位が0ではなく、CPU割り込み優先順位より高く設定されている必要があります。

割り込みの発生条件

まとめると、割り込みが発生するには以下の条件を満たしている必要があります。

  1. 割り込み有効ビットが1に設定されている
  2. 割り込み優先順位が1以上に設定されている
  3. 割り込み優先順位がCPU割り込み優先順位より高く設定されている
  4. 割り込み優先順位が6以下のとき、DISIが無効とされている

トラップ (Traps)

エラーなどにより処理を続行できないときに発生する割り込みです。

  • Math Error Trap
  • Address Error Trap
  • Stack Error Trap
  • Oscillator Fail Trap

これらの割り込み優先順位は8以上が設定されており、汎用の割り込みよりも常に優先されます。

レジスタ (Register)

割り込みの制御には、以下のレジスタが関与します。

この内SRとCORCONは、SRのIPL<2:0>とCORCONのIPL<3>のビットを連結させて、CPU割り込み優先順位を決定します。