2

モーション コントロール用に arduino Mega 2650 にリミット スイッチを取り付けました。リミット スイッチの 2 つのノーマル オープン接点は、Arduino ピンとグランドに接続されているため、リミット スイッチが作動すると、Arduino ピンがグランドに短絡します。

予想どおり、このセットアップにはバウンスの問題があります。ISR のカウンターを使用して確認しました。最後に、特定の時点でリミット スイッチがオンになっているかオフになっているかを確実に識別する次のコードを作成しました。

const int lsOuterLeftIn = 18; // lsOuterLeftIn is my Limit Switch
const int LED = 9;
volatile bool lsEngaged = false; // flag for limit switch engaged
void setup() {
    pinMode(lsOuterLeftIn, INPUT_PULLUP);
    pinMode(LED, OUTPUT);
    attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR1, FALLING);
    attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR2, RISING);
}
void  loop() {
    if (lsEngaged) digitalWrite(LED, HIGH);
    else digitalWrite(LED, LOW);
}
void ISR1(){
    delay(100);
    lsEngaged = (digitalRead(lsOuterLeftIn));
}
void ISR2(){
    delay(100);
    lsEngaged = (digitalRead(lsOuterLeftIn));
}

しかし、ここに私の問題があります。このArduino documentation pageにたどり着きました、そしてそれは言います

「delay() が機能するには割り込みが必要なため、ISR 内で呼び出された場合は機能しません。」

しかし、私はdelay()内部の ISR を利用していますが、うまくいっているようですが、何が起こっているのでしょうか? delay()ドキュメントに記載されているように、機能が誤動作する可能性があるため、現時点では機能しているが、簡単に壊れる可能性がありますか?

4

4 に答える 4

3

TomKeddie の答えは正しいように見えます。問題はありません。とにかく、私の意見では、あなたのコードは少なくとも 2 つの理由で概念的に間違っています。では、その理由を説明します。

入力には 2 種類あります。すぐに答えなければならないものと、すぐには脅威にならないが答えなければならないものです。たとえば、通常、セキュリティエンドストップは最初のグループに分類されます。これは、ヒットするとすぐにアクチュエータを停止する必要があるためです。一方、UI ボタン​​は、すぐに応答する必要がないため、2 番目のグループに分類されます。

注: よくできたプログラムでは、通常、10 分の 1 ミリ秒以内に 2 番目の種類の入力に応答できるため、ユーザーに遅延が発生することはありません。

ここで、入力が 2 番目の入力グループに該当する場合、ISR を使用してそれを読み取ることはできません。より重要なものをブロックする可能性があるためです。代わりに、メイン ループで読み取り、適切にデバウンスします。たとえば、Bounceライブラリを使用するか、自分で実装できます。

#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5

unsigned long previousMillis;
char stableVals;

...

void  loop() {
    if ((millis() - previousMillis) > CHECK_EVERY_MS)
    {
        previousMillis += CHECK_EVERY_MS;
        if (digitalRead(lsOuterLeftIn) != lsEngaged)
        {
            stableVals++;
            if (stableVals >= MIN_STABLE_VALS)
            {
                lsEngaged = !lsEngaged;
                stableVals = 0;
            }
        }
        else
            stableVals = 0;
    }

    ...
}

これは、値が変更されたかどうかを 20 ミリ秒ごとにチェックします。ただし、値は 5 サイクル (つまり 100 ms) 以上安定している場合にのみ更新されます。

このようにして、そのタスクでメイン プログラムをブロックすることはありません。

一方、あなたの入力があなたのデバイスにとって深刻な脅威を表している場合 (エンドストップなど)、できるだけ早く回答する必要があります。この場合、応答する前に 100 ミリ秒待機する必要があり、これは入力の迅速さの必要性に反しています。

もちろん、デバウンスによって遅延が発生するため、そのような入力をデバウンスすることはできません。ただし、ある状態を他の状態よりも優先することはできます。エンドストップがグランドに接続されている場合、重大な脅威は入力状態がグランドの場合です。したがって、変数を次のように設定することをお勧めします。

  1. ピンがダウンすると、すぐに0に設定します
  2. ピンが上がると、100ミリ秒(メインループで)待ってから設定します。

これを行うコードは次のようなものです。

#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5

unsigned long previousMillis;
char stableVals;

attachInterrupt(digitalPinToInterrupt(lsOuterLeftIn), ISR1, FALLING);

...

void  loop() {
    if ((millis() - previousMillis) > CHECK_EVERY_MS)
    {
        previousMillis += CHECK_EVERY_MS;
        if ((digitalRead(lsOuterLeftIn) == HIGH) && (lsEngaged == LOW))
        {
            stableVals++;
            if (stableVals >= MIN_STABLE_VALS)
            {
                lsEngaged = HIGH;
                stableVals = 0;
            }
        }
        else
            stableVals = 0;
    }

    ...
}

void ISR1()
{
    lsEngaged = LOW;
}

ご覧のとおり、唯一の割り込みは立ち下がりであり、最も重要なのは非常に短いことです。

モーターの停止など、他の命令を実行する必要がある場合は、ISR1 関数を使用できます (非常に短い場合)。

覚えておいてください: ISR はできるだけ短くする必要があります。マイクロコントローラーが ISR の 1 つにあると、他のすべてが見えなくなるためです。

于 2015-10-27T10:12:37.107 に答える
2

AVR では、delay() は次のように実装されています。関連する割り込みはありません (micros() は timer0 カウント値を返し、yield() は単純なスケッチでは使用されないスケジューラーを参照します)。

そのコメントは移植性のためにあると思います。あなたはますます多くのプラットフォームで動作する環境を使用しています。あなたがしていることは AVR では問題ありませんが、おそらく別のプラットフォームではそうではありません。

単純な for ループでスピン待機することをお勧めします。消費電力が問題にならない限り、CPU は他に何もしていませんが、それはここでは範囲外です。

https://github.com/arduino/Arduino/blob/79f5715c21a81743443269a855979a64188c93df/hardware/arduino/avr/cores/arduino/wiring.cから

void delay(unsigned long ms)
{
    uint16_t start = (uint16_t)micros();

    while (ms > 0) {
        yield();
        if (((uint16_t)micros() - start) >= 1000) {
            ms--;
            start += 1000;
        }
    }
}
于 2015-10-27T00:53:12.477 に答える
0

デバウンス コードから、スイッチがオンになるまで 100 ミリ秒の反応時間を確保できるようです。

そのため、イベントのマイクロ秒以内に反応する必要が本当にない場合は、たとえば 10ms ごとに入力をポーリングすることを検討してください (たとえば、タイマー ISR から)。

(外部割り込みを使用する理由は 2 つだけです。1. シグナルに非常に高速 (µs!) に反応する必要があるか、2. タイマーがアクティブでない深い省電力モードから復帰する必要があるためです。タイマーベースのポーリングに使用できる他のすべてのもの。)

擬似コード:

#define STABLE_SIGNAL_DURATION 5

uint8_t button_time_on = 0;

volatile bool button_is_pressed = false;

...
// Every 10ms do (can be done in a timer ISR):

if ( read_button_input() == ON ) {

  if ( button_time_on >= STABLE_SIGNAL_DURATION ) {

    button_is_pressed = true;

  } else {
     button_time_on++;
  }

} else {
  button_time_on = 0; // button not pressed (any more).
  button_is_pressed = false;
}

...

とでmain()

bool button_press_handled = false;

while(1) {
  // do your other main loop stuff...

  button_press_handled = button_press_handled && button_is_pressed;

  if ( !button_press_handled && button_is_pressed ) {

    // Handle press of the button

    // ...

    // Note that we handled the event for now:
    button_press_handled = true;
  }
}
于 2015-10-30T16:21:29.717 に答える