入力が変更されたときに関数を自動的に呼び出すことはできますか?


21

現在、私のスケッチでは、メインループを回るたびに入力ピンをチェックしています。変更を検出すると、カスタム関数を呼び出してそれに応答します。コードは次のとおりです(要点に合わせてトリミングされています)。

int pinValue = LOW;

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
}

void loop()
{
    // Read current input
    int newValue = digitalRead(2);

    // Has the input changed?
    if (newValue != pinValue) {
        pinValue = newValue;
        pinChanged();
    }
}

残念ながら、これは入力の非常に短い変更(短いパルスなど)、特にloop()少しゆっくり実行している場合は常に適切に機能するとは限りません。

Arduinoに入力の変更を検出させ、関数を自動的に呼び出す方法はありますか?


1
あなたがその外部割り込みを探しています
Butzke

回答:


26

外部割り込みを使用してこれを行うことができます。ただし、ほとんどのArduinoは、限られた数のピンでのみこれをサポートします。詳細については、のドキュメントを参照してくださいattachInterrupt()

Unoを使用していると仮定すると、次のようにできます。

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
    attachInterrupt(0, pinChanged, CHANGE);
}

void loop()
{
}

これはpinChanged()、外部割り込み0で変更が検出されるたびに呼び出されます。Unoでは、GPIOピン2に対応します。外部割り込み番号は他のボードで異なるため、関連ドキュメントを確認することが重要です。

ただし、このアプローチには制限があります。カスタムpinChanged()関数は、割り込みサービスルーチン(ISR)として使用されています。つまりloop()、呼び出しの実行中に残りのコード(のすべて)が一時的に停止します。重要なタイミングの混乱を防ぐために、ISRをできるだけ速くすることを目指してください。

また、ISR中に他の割り込みが実行されないことに注意することも重要です。つまり、割り込み(コアdelay()millis()関数など)に依存するものは、その内部では適切に動作しない可能性があります。

最後に、ISRがスケッチ内のグローバル変数を変更する必要がある場合、通常は次のように宣言する必要がありますvolatile。例:

volatile int someNumber;

コンパイラーに値が予期せず変更される可能性があることを伝えるため、それは重要です。したがって、古いコピー/キャッシュを使用しないように注意する必要があります。


質問で言及されている「ブリーフパルス」に関して、ピンが割り込みをトリガーするための状態にある必要がある最小時間はありますか。(明らかにそれはループ内で起こっている他に何に依存する、はるかに少ないポーリング以上になるだろう)
sachleen

1
@sachleen ISR関数の実行中に発生しない限り機能します(回答で説明)。そのためpinChanged()、できるだけ短くする必要があります。したがって、通常、最小時間はpinChanged()関数自体を実行する時間でなければなりません。
jfpoilpret 14

2
割り込みを使用するときに注意しなければならないすべての不変のものを含む、この非常に詳細な回答に対して+1!
jfpoilpret 14

3
volatilesomeNumberのように、グローバル変数が1バイトよりも大きい場合、共有グローバルの宣言に加えて、プログラムによるバイトアクセスの間に発生するピン変更割り込みから保護する必要があります。このようなステートメントにsomeNumber +=5;は、下位バイトの追加とキャリーを含む上位バイトの追加が含まれます。これら2つ(より広い変数の場合)は、割り込みで分割しないでください。(それぞれ)操作の前後に割り込みをオフにして復元するだけで十分です。
JRobert

@sachleen-最小パルスサイズに関して。データシートで明確な答えを見つけることは困難ですが、ピン変更割り込みのタイミングによって判断すると、それらは半クロックサイクル以内にラッチされます。割り込みが「記憶」されると、ISRが起動して処理するまで記憶されます。
ニックギャモン

5

デジタル入力として設定されたピンの状態が変化すると、割り込みが発生します。INT1またはINT2による割り込みの一意のベクトルとは異なり、PinChangeInt機能は共通ベクトルを使用し、このベクトルの割り込みサービスルーチン(別名ISR)は、どのピンが変更されたかを判断する必要があります。

幸いなことに、PinChangeIntライブラリはこれを簡単にします。

PCintPort::attachInterrupt(PIN, burpcount,RISING); // attach a PinChange Interrupt to our pin on the rising edge
// (RISING, FALLING and CHANGE all work with this library)
// and execute the function burpcount when that pin changes

0

単にHIGHまたはLOWではなく、しきい値を通過する電圧を検出したい場合は、アナログコンパレータを使用できます。スケッチ例:

volatile boolean triggered;

ISR (ANALOG_COMP_vect)
  {
  triggered = true;
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Started.");
  ADCSRB = 0;           // (Disable) ACME: Analog Comparator Multiplexer Enable
  ACSR =  bit (ACI)     // (Clear) Analog Comparator Interrupt Flag
        | bit (ACIE)    // Analog Comparator Interrupt Enable
        | bit (ACIS1);  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
   }  // end of setup

void loop ()
  {
  if (triggered)
    {
    Serial.println ("Triggered!"); 
    triggered = false;
    }

  }  // end of loop

これは、入力で(たとえば)1Vから2Vへの変化を検出する必要がある光検出器などの場合に役立ちます。

回路例:

ここに画像の説明を入力してください

また、タイマー/カウンター1の現在のカウントを保存することにより、特定の入力の正確な時刻を記憶するプロセッサーのInput Capture Unitを使用することもできます。これにより、 ISRを使用して現在の時刻をキャプチャする前に、遅延(おそらく数マイクロ秒)を導入するのではなく、関心が生じました。

タイミングが重要なアプリケーションの場合、これにより精度が多少向上します。

サンプルアプリケーション:Arduinoをコンデンサテスターに​​変える

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