Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
attachInterrupt()

attachInterrupt()

概要

attachInterrupt()は、外部割り込みが発生したときに呼び出す関数を設定します。attachInterrupt()のリファレンスはこちらを参照してください。

ATmega328Pの外部割り込みは、INT0とINT1、PCINT0~PCINT23のどれかで起動されます。Arduinoでは、INT0(デジタルピン2番)とINT1(デジタルピン3番)の外部割り込みを利用できます。

割り込みの制御

INT0とINT1の外部割り込みは、外部割り込み関連のレジスタ(EICRAとEIMSK)を操作することで実現します。

EICRA

EICRA(External Interrupt Control Register A、外部割り込み制御レジスタA)は、割り込みの検出方法を制御します。

EICRA
ビット 7 6 5 4 3 2 1 0
名称 - - - - ISC11 ISC10 ISC01 ISC00

ISC11とISC10の組み合わせで、INT1(デジタルピン3)の割り込み条件を、ISC01とISC00の組み合わせでINT0(デジタルピン2)の割り込み条件を設定します。

ISC11/ISC01 ISC10/ISC00 割り込み発生条件
0 0 LOWのとき
0 1 値が変化したとき
1 0 HIGHからLOWになるとき
1 1 LOWからHIGHになるとき

EIMSK

EIMSK(External Interrupt Mask Register)は、外部割り込みの許可・禁止を制御します。

EIMSK
ビット 7 6 5 4 3 2 1 0
名称 - - - - - - INT1 INT0

それぞれ1のときに、INT1/INT0の割り込みを許可します(かつ状態レジスタのIビットが1になっている必要があります)。

ソースコード

attachInterrupt()は、hardware/arduino/avr/cores/arduino/WInterrupts.c に定義されています。以下に全ソースコードを示します。Arduino Unoの場合に適用される部分だけを抜粋しています。実際には#if によりさまざまなチップに対応しています。

static void nothing(void) {
}

static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS] = {
   nothing,
   nothing,
};

void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    intFunc[interruptNum] = userFunc;
    
    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
      
    // Enable the interrupt.
      
    switch (interruptNum) {
    case 0:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
       break;
    case 1:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;   
    case 2:
      break;
    }
  }
}

intFunc[]は、外部割り込みが発生したときに起動する関数を代入しておく配列です。intFuc[]の型のvoidFuncPtrとEXTERNAL_NUM_INTERRUPTSは、hardware/arduino/cores/arduino/wiring_private.hに以下のように定義されています。

#define EXTERNAL_NUM_INTERRUPTS 2
typedef void (*voidFuncPtr)(void);

voidFuncPtrは、voidを返す、引数のない関数へのポインタ型です。EXTERNAL_NUM_INTERRUPTSが2なので、関数を2個定義することができます。初期値として、何もしないnothing()という関数が代入されています。

入力は、interruptNumとuserFuc、modeで、それぞれ、uint8_t、void(*)(void)、int型です。出力はありません。

void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {

割り込み番号(interruptNum)が、EXTERNAL_NUM_INTERRUPTS未満のとき、それ以降を実行します。

引数で指定した関数をintFunc[]に格納します。割り込みハンドラの中から指定した関数が呼び出されます。

    intFunc[interruptNum] = userFunc;

割り込み番号に対応したレジスタの値を設定します。

    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
      
    // Enable the interrupt.
      
    switch (interruptNum) {
    case 0:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
       break;
    case 1:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;   
    case 2:
      break;
    }
  }
}

論理演算が見づらいので、少しずつ解剖していきます。

(1 << ISC00) | (1 << ISC01)

ISC00とISC01は、それぞれ、0と1です。よって、1 << ISC00は1(0b00000001)、1<<ISC01は2(0b00000010)です。

両者の論理和をとると、0b00000011(=3)です。

(EICRA & ~((1 << ISC00) | (1 << ISC01)))

次に、EICRAと~((1 << ISC00) | (1 << ISC01)))の論理積をとっています。((1 << ISC00) | (1 << ISC01)))は、0b00000011なので、その否定である、~((1 << ISC00) | (1 << ISC01)))は、0b11111100となります。

EICRAとこの0b11111100の論理積をとっているので、EICRAの下位2ビットをクリアするということになります。

mode << ISC00との論理和をとります。ISC00は0なので、modeとの論理和と同値です。

(EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00)

modeとして指定するのは、LOWあるいはCHANGE、RISING、FALLINGのどれかで、それぞれ、0、1、2、3です。

これにより、EICRAを設定します。

modeの値は検査していないので、変な値を指定すると、INT1の設定を変えてしまう可能性があります。

INT0_vect、INT1_vectは、それぞれ、INT0、INT1が発生したときに起動される割り込みハンドラです。

#define IMPLEMENT_ISR(vect, interrupt) \
  ISR(vect) { \
    intFunc[interrupt](); \
  }

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)

ISR()は、割り込みハンドラを定義するためのマクロです。割り込みハンドラでは、intFunc[]に登録した関数を呼び出します。

バージョン

Arduino 1.8.3



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2016 garretlab all rights reserved.
inserted by FC2 system