時間の管理方法(Arduino詳解)

Arduino詳解その25です。前回の記事はこちらです。
hirocom777.hatenadiary.org

時間の管理方法

 Arduinoのスケッチに限らず、プログラムというものは時間の経過とともに実行されていくものです。当然時間の取り扱いが重要になってきます。今までご紹介したスケッチにも時間を扱う方法が出てきましたが、ここで改めて整理しておきたいと思います。

f:id:HiroCom777:20201120211736j:plain

一定時間何もしない

 この方法はあちこちのスケッチで見かけます。一定時間何もしないことによってスケッチの処理速度をコントロールします。この連載でも最初のころご紹介しました。以下の二つですね。

・delay()
 カッコの中に時間(ミリ秒)を数値で指定すると、指定した時間だけスケッチが停止します。
・delayMicroseconds()
 カッコの中に時間(マイクロ秒)を数値で指定すると、指定した時間だけスケッチが停止します。

簡単に使えて便利なんですが、何分指定した時間だけスケッチが止まってしまうため、多用するとスケッチ自体の処理が遅くなってしまいます。使いすぎは禁物です。この問題は以下を使って解決できることがあります。

リセットしてからの経過時間をみる

 Arduinoにはスケッチが起動(リセット)してから経過した時間を返す関数があります。この値をチェックすることで時間の経過を知ることが出来ます。

・millis()
 リセットして空の経過時間をミリ秒単位で返します。unsigned long型の数値を返すため、最大4294967295ミリ秒=1193時間≒約50日でオーバーフローしてゼロに戻ります。
・micros()
 リセットして空の経過時間をマイクロ秒単位で返します。ArduinoUNO(クロック16MHz)の場合、4マイクロ秒単位でカウントします。こちらもunsigned long型の数値を返すため、最大4294967295マイクロ秒≒約4295秒≒約71.6時間でオーバーフローしてゼロに戻ります。

僕は、よくloop()の中で以下のような使い方をしますね。

//動作確認用LED
#define Led 13
//動作確認用LED点滅時間管理
unsigned long lastTime = 0;

void setup() {
  //動作確認用LED出力設定
  pinMode(Led , OUTPUT);
}

void loop() {

  //メインの処理

  //1秒ごとに動作確認用LEDを点滅
  if ((millis() - lastTime) > 1000) {
    lastTime = millis();
    digitalWrite(Led, !digitalRead(Led));
  }
}

こうすると、メインの処理にはほとんど影響を与えることなく大体一定間隔(1秒)で動作確認用のLEDを点滅させることが出来ます。

ここで気になるのが時間がどんどん経過してmillis()がオーバーフローしてしまった時です。変数lastTimeがオーバーフローしていない状態だと計算が狂ってしまいそうです。
でも、結論から言うと問題ありません。millis()も変数lastTimeも符号なし(unsigned long)のデータ型をとるため、この引き算は論理シフト演算が適用されるからです。詳しい説明はここでは割愛しますが、millis()、micros()でカウント可能な時間内であればオーバーフロー時も問題ありません。

一定時間で割り込む

 1秒ごとに何かの処理をしようとすると、大概の場合上に紹介したスケッチで事足りると思うのですが、メインの処理時間が極端に長い時などは正確に動作しない場合があります。もっと確実に処理したい場合には割り込みという処理を使います。Arduinoには割り込みを使うためのモジュールがあります。

MsTimer2

 MsTimer2がそれにあたります。使用するには以下からZIPファイルをダウンロードしてArduinoIDEのメニュー スケッチ⇒ライブラリをインクルード⇒.ZIP形式のファイルをインストール と進んでダウンロードしたファイルを開いてください。

https://playground.arduino.cc/uploads/Main/MsTimer2/index.zip

上のスケッチをMsTimer2で書き直してみました。

#include <MsTimer2.h>
//動作確認用LED
#define Led 13

void setup() {
  //動作確認用LED出力設定
  pinMode(Led , OUTPUT);
  MsTimer2::set(1000, blink);
  MsTimer2::start();
}

void loop() {

  //メインの処理

}

//1秒ごとに動作確認用LEDを点滅
void blink() {
  digitalWrite(Led, !digitalRead(Led));
}

 これでメインの処理に関係なく1秒ごとにLEDが点滅します。MsTimer2の使い方は以下の通りです。

MsTimer2を使用するにはスケッチ先頭に#include <MsTimer2.h>を記述します。
・MsTimer2::set()
 MsTimer2による割り込みを設定します。指定した時間(ミリ秒単位)が経過するごとに関数名で指定した関数を呼び出します。書き方は以下の通りです。
 
 MsTimer2::set(時間, 関数名);

・MsTimer2::start()
 割り込みのタイマーを開始します。

・MsTimer2::stop()
 割り込みのタイマーを中止します。

MsTimer2の関数は、MsTimer2の名前空間で定義されているので『::』で繋げて表示します。
またMsTimer2は、tone( )関数及び13番ピンと11番ピンからのPWM出力と同時に使えません。同じ機能を使用しているからです。

それぞれの使いどころなんですが、
 delay()など ⇒ 簡単に使えますが他の処理に影響を与えやすい。
 millis()など ⇒ 他の処理に影響を与えにくくなるがスケッチが複雑になる。
 MsTimer2  ⇒ 一番正確。モジュールを使用するのでスケッチが大きくなる。また、一部の機能が使えなくなる。
となります。
 

次回はSDカード

 いかがでしょうか。これらをうまく使い分けるとスマートなスケッチが書けると思います。さて、次回はArduinoでSDカードを使ってみようと思います。お楽しみに!!
hirocom777.hatenadiary.org


Arduino UNO入門の連載記事はコチラからどうぞ!!