ArduinoでTimerを使わずに割り込み処理をする

 個人的に timer割り込みはあまり好きじゃなくて、普段はなるべく割り込みを使わないようにソースを考えてます。どうしても割り込み処理が欲しい場合は timer を使用せずにメインループの中で割り込みっぽい処理を行っているのですが、今回はその方法です。

 Arduinoとタイトルにありますが、言語で行う方法なのでArduino以外、ラズパイやPICなどでも利用できる内容です。

 では Timer を使った割り込み例を絡めて順に説明していきたいと思います。Arduino単体で手軽に試せるようなに、例えば数秒間隔で文字列「◆」をシリアル送信するとかで考えてみます。

スポンサーリンク

delay()を使用した簡単な例

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("◆");
  delay(1000);
}

 delay()使って、およそ1秒置きに「◆」をシリアル送信してます。簡単なLチカ方式です。

▼実行結果▼

 シリアルモニタで確認すると、およそ1秒置きに「◆」が表示されます。非常に簡単なんですがdelay()待機中の1秒間は、無駄な時間で他の処理が何もできないというデメリットがあります。またこの方法ではTimerほど正確に時間を刻めません。そこで Timer割り込みを使用します。

timer使用した割り込みの例

 ソースの抜粋です。

ISR (TIMER1_COMPA_vect) {
  digitalWrite(13, !digitalRead(13));
  Serial.print("◆");
}

void loop() {
  delay(1000);
  i++;
  if (i != 4)  Serial.println(i);
  if (i == 4) {
    Serial.println(i);
    i = 0;
  }
}

 3秒毎に割り込み発生するように設定してます。割り込み時(ISR()関数実行時)に「◆」をシリアル送信。ついでにデバック目的でONボードのLEDを3秒毎に点滅させてます。

 メインループでは1秒間隔で数値をカウントしてシリアル送信してます。

▼実行結果▼

 正確に3秒毎に「◆」が出力されます。timer 使えば無駄時間も発生せず時間カウント中に他の処理もできるため便利です。

 特にこだわりが無ければ timer割り込み使うのが正確で簡単かと思います。ただ個人的に Arduino の Timer割り込みはあまり好きではないため、割り込みっぽい処理を timer を使わずにメインループの中で行ってみたいと思います。

▼Timer割り込みの方法はこちら▼

Timerを使わずに割り込み処理

 では本題のメインループの中で割り込みを使用せずに割り込みっぽい処理を行う方法です。

まずは簡単な例から

void loop() {
  currentTime = micros();

  if (currentTime - SerialTimer > 1000000) { //1s
    SerialTimer = currentTime;
    Serial.println("◆");
  }
}

 このソース例では1秒毎に「◆」がシリアル送信されます。if 文が割り込み時の処理のイメージです。ループの頭で時間を測定しおいて、if で時間監視、目的の時間が経過していたら処理を実行、また実行したときの時間を記憶し、、、とそれの繰り返しです。

 レジスタ設定とかいらないし、手軽で簡単かと思います。delay()と違い無駄時間は発生せず、時間監視中にも他の処理を行うことができます。ただ、Timer ほど時間計測が正確でないといったことと、メインループ内でdelay()を使うと時間監視が少しややこしくなるといった背反もあります。これではあまり割り込みっぽくないので、次からはもう少し割り込みっぽい例を見てみます。

スポンサーリンク

▼もう一つ参考例▼

void loop() {
  currentTime = micros();

  if (currentTime - CountTimer > 1000000) { //1s
    CountTimer = currentTime;
    Serial.println(i);
    i++;
    if (i == 4)i = 1;
  }

  if (currentTime - SerialTimer > 3000000) { //3s
    SerialTimer = currentTime;
    Serial.print("★");
  }
}

 1秒毎に数字カウントをシリアル送信、3秒毎に「★」をシリアル送信してます。

▼実行結果▼

 秒数カウントと3秒置きに★がシリアル通信されてます。完全な割り込みでは無く、どうしても処理時間が入ってしまうため、厳密な時間間隔では無くなってしまいますが、ある程度アバウトな時間間隔の処理でよければこれで十分です。工夫すれば結構正確にカウントはできると思います。

▼さらにこんなことも可能です▼

void loop() {
  currentTime = micros();

  if (digitalRead(13) == HIGH && currentTime - LEDTimer13 > 350000) {
    LEDTimer13 = currentTime;
    digitalWrite(13, LOW);
  }
  else if (digitalRead(13) == LOW && currentTime - LEDTimer13 > 650000) {
    LEDTimer13 = currentTime;
    digitalWrite(13, HIGH);
  }

  if (digitalRead(12) == HIGH && currentTime - LEDTimer12 > 150000) {
    LEDTimer12 = currentTime;
    digitalWrite(12, LOW);
  }
  else if (digitalRead(12) == LOW && currentTime - LEDTimer12 > 550000) {
    LEDTimer12 = currentTime;
    digitalWrite(12, HIGH);
  }
}

 2つのLED(ピン)をそれぞれの時間間隔でOn/Offしてます。13pinはOn時間350ms、Off時間650msで点滅。12pinからはOn時間150ms、Off時間550msで点滅(出力)させてます。ちょっとしたPWM波形ですね。(このソース実行検証してないので間違ってたらすみません・・・。)

 逆にこれを delay() や timer割り込みで実現するのはかなり難しいと思います。この方法であればUNOのPWM出力ピンからだけでなく、すべての出力ピンからPWM出力(サーボ制御など)を行うこともできると思います。

 時間間隔はtimerとくらべ少しアバウトになってしまうといいましたが、以前ドローンのモーター速度制御をこの方法で行って飛行させてます。それくらいの正確さはでます。

 最後、割り込みから少し離れてしまいましたが 、よくこういった方法で割り込みっぽい処理を行ってます。以前にも同じ内容の投稿をしているのですが今回は目的を少し変えて投稿させてもらいました。

▼Arduinoマルチタスク▼

▼ドローン自作▼

コメント