Arduinoのコーディングタイマーと遅延

2019年9月5日更新:Arduinoボード上で単純なマルチタスクを実現するための最初のステップは、delay()呼び出しを削除することです。 任意のボード上のArduinoの説明可能なシンプルなマルチタスクは、他のすべての必要なステップをカバーします。

2019年5月5日更新:isFinished()をjustFinished()に名前変更しました。遅延が終了した直後に1回だけTRUEを返すためです。 フリーズ/一時停止遅延の例を追加しました

前書き

delay()を使用しないでください

delay()を使用すると、遅延の期限が切れるのを待っている間にシステムがスタックします。 ただし、遅延を置き換えるには注意が必要です。 このページでは、Arduino delay()をノンブロッキングバージョンに置き換える方法を順を追って説明します。これにより、遅延がタイムアウトするまでコードを実行し続けることができます。

Arduinoボードの電源がオン(またはリセット)になるとLedがオンになり、10秒後にオフになると、それぞれ簡単なスケッチがいくつかあります。 最初のコードは、コードを記述しない方法の例です。 2番目は動作するコードの例であり、3番目はmillisDelayライブラリを使用してコードを簡素化する例です。 単発タイマーと繰り返しタイマーの例もあります。

delay()を使用してはならない理由を既に理解しており、Arduino、符号なしlong 、オーバーフロー、符号なし減算を使用することの重要性に精通している場合は、millisDelayライブラリの使用(ステップ4)にスキップできます。

millisDelayライブラリーは、機能の遅延とタイマーを提供し、Arduinoの初心者にとって使いやすく、理解しやすいものです。

この説明は、Arduinoでタイマーと遅延をコーディングする方法でもオンラインで入手できます。

ステップ1:Arduinoで遅延をコーディングしない方法

スケッチで遅延をコーディングしない方法を次に示します。

 int led = 13; 
符号なしlong delayStart = 0; //遅延が開始した時間bool delayRunning = false; //遅延が完了するのをまだ待っている場合はtrue
void setup(){pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、HIGH); // delayStart = millis()でledをオンにします; //遅延を開始しますdelayRunning = true; // まだ終わっていません }
void loop(){// 10秒== 10000mS後に遅延がタイムアウトしたかどうかを確認if(delayRunning &&((millis()-delayStart)> = 10000)){delayRunning = false; // //このコードが一度以上実行されるのを防ぐdigitalWrite(led、LOW); // ledをオフSerial.println( "Turned LED Off"); } //ここに他のループコード。 。 。 Serial.println( "Run Other Code"); }

Arduinoが起動時に1回呼び出すsetup()メソッドでは、LEDがオンになります。 setup()が終了すると、Arduinoはloop()メソッドを繰り返し呼び出します 。 ほとんどのコードはここにあり、出力を送信するセンサーなどを読み取ります。上記のスケッチでは、最初にloop()が呼び出され、 delay(10000)がすべてを10秒間停止してからLEDをオフにして続行します。 このコードを実行すると、起動後10秒間は[ 他のコードを実行]が出力されないことがわかりますが、ledがオフになった後(ledOnはfalse)、loop()が繰り返し呼び出されるため、非常に高速に出力されます再び。

ここで注意すべき点は、loop()コードでdelay()関数を実際に使用しないでください。 setup()コードでdelay()を使用すると便利な場合があり、 loop()コードで数ミリ秒の非常に小さな遅延を使用して非常に小さな値を回避できることがよくありますが、 loop()メソッド

ステップ2:Arduinoで非ブロッキング遅延を記述する方法

前のスケッチではブロッキング遅延を使用しました。つまり、遅延が期限切れになるのを待っている間に、コードが他のことを完全に停止するものです。 この次のスケッチでは、遅延の期限が切れるのを待っている間にコードの実行を継続できる非ブロッキング遅延の作成方法を示します。

 int led = 13; 
符号なしlong delayStart = 0; //遅延が開始した時間bool delayRunning = false; //遅延の完了を待機している場合はtrue void setup(){pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、HIGH); // delayStart = millis()でledをオンにします; //遅延を開始しますdelayRunning = true; //まだ終了していません} void loop(){// 10秒== 10000mS後に遅延がタイムアウトしたかどうかを確認if(delayRunning &&((millis()-delayStart)> = 10000)){delayRunning = false; // //このコードが一度以上実行されるのを防ぐdigitalWrite(led、LOW); // ledをオフSerial.println( "Turned LED Off"); } //ここに他のループコード。 。 。 Serial.println( "Run Other Code"); }

上記のスケッチでは、 setup()メソッドで、 delayStart変数がmillis()の現在の値に設定されています。

millis()は、ボードに電源が投入されてからのミリ秒数を返す組み込みメソッドです。 ボードがリセットされるたびに0として開始され、CPUハードウェアカウンターによって1ミリ秒ごとにインクリメントされます。 あとでmillis()についてさらに詳しく。 setup()が終了すると、Arduinoはloop()メソッドを繰り返し呼び出します。

loop()がコードチェックと呼ばれるたびに
a)遅延がまだ実行されていること、および
b) millis()delayStartに保存された値から10000 mS(10秒)移動した場合

時間が10000ミリ秒以上移動すると、 delayRunningがfalseに設定され、ifステートメントのコードが再度実行されてledがオフになるのを防ぎます。

このスケッチを実行すると、[ 他のコード実行]が非常にすばやく印刷され、10秒後にLEDがオフになります。クイックの場合は、画面からスクロールする前に[LEDオフ]メッセージが表示される場合があります。

millisDelayライブラリーがこのコードを単純化する方法については、以下のステップ4を参照してください

ステップ3:符号なしロング、オーバーフロー、符号なし減算

符号なしlong、オーバーフロー、符号なし算術、および符号なしlong変数の使用の重要性に精通している場合は、ステップ4のmillisDelayライブラリの使用にスキップできます。

前のスケッチの重要な部分はテストです

 (millis()-delayStart)> = 10000 

このテストを機能させるには、この非常に具体的な方法でコード化する必要があります。

符号なしロングとオーバーフロー

millis()組み込み関数から返されるdelayStart変数と数値は、 符号なしlongです。 これは、0から4, 294, 967, 295までの数値です。

最大値4, 294, 967, 295を保持する符号なしlongに 1を追加すると、答えは0(ゼロ)になります。 それはオーバーフローした数値であり、0に戻ります。オーバーフロービットがドロップされただけであることが想像できます。 たとえば、3ビットの符号なし111では最大値(7)で、1を追加すると1000(8)になりますが、先頭の1は3ビットストレージをオーバーフローし、000に戻ります。

これは、最終的に、cpuがmillis()結果を保持する変数をもう1つ追加すると、0にラップアラウンドすることを意味します。つまり、 millis()は再び0からカウントを開始します。 これは、Arduinoボードを4, 294, 967, 295ミリ秒(つまり、49日17時間、たとえば50日)実行している場合に発生します。

次に、テストをコーディングする別の方法を考えてみましょう(millis()-delayStart)> = 10000

算術的に、このテストはmillis()> =(delayStart + 10000)に等しい

ただし、 millis()が4, 294, 966, 300 mSを返す場合など、ほぼ50日後に遅延を開始すると、 delayStart + 10000は995にオーバーフローし、テスト、millis()> =(delayStart + 10000)はすぐにtrueになります遅延はまったくありません。 そのため、この形式のテストは常に機能するとは限りません。

残念ながら、テスト中にこれに出くわすことはほとんどありませんが、ガレージドアの制御のように長時間稼働するデバイスでは予期せずに発生する可能性があります。 試して使用すると、同様の問題が発生します
delayEnd = millis()+ 10000
そして、テスト(millis()> = delayEnd)

最後に、 delayStart変数は符号なしlongでなければなりません。 代わりにlong (つまりlong int )またはintまたはunsigned intを使用する場合、保持できる最大値は、 millis()から返されるunsigned longよりも小さくなります。 最終的に、 millis()から再調整された値は、格納されている小さな変数をオーバーフローさせ、時間が逆になっていることがわかります。 たとえば、 startDelayunsigned intを使用する場合、これはUnoボードで65秒後に発生します。

符号なしの減算

もう1つの興味深い点は、 millis()の結果に何が起こるかです。delayStartが4, 294, 966, 300で、10000mSの遅延が必要な場合のdelayStartです。

millis()は、それが起こる前に0に戻ります。 符号なしlongの最大値に1を追加すると、ラップアラウンドを0に戻すことができます。したがって、millis()の計算を見る1つの方法-delayStartは、 millis()がラップアラウンドしてdelayStartよりも小さい場合ですhatnumberは、millis()に等しくなるようにdelayStartに追加する必要があります(オーバーフロー後)? "つまり、delayStart + X == millis()の式のXは何ですか

たとえば、再び3ビットの符号なし変数を使用して、2〜4(符号なし)を計算するには、0から開始して111(7)まで1を追加し、0に戻す時計面を考えます。 2に6(5, 6, 7, 0, 1, 2)を追加する必要があるため、2-4 = 6となり、CPUが異なる計算を実行しますが、これは実際に計算がどのように機能するかです。

したがって、2つの符号なしlongの差は、常に0から4, 294, 967, 295の範囲の正数になります。 たとえば、 startDelayが1で、 millis()が0にラップした場合(50日後)、 millis()-startDelayは4, 294, 967, 295になります。 これは、0から4, 294, 967, 295 mSの範囲の任意の場所でDELAY_TIMEを指定でき、 (millis()-delayStart)> = DELAY_TIMEは、遅延がいつ開始されるかに関係なく、常に期待どおりに機能することを意味します。

ステップ4:MillisDelayライブラリの使用

millisDelayライブラリをインストールします。 millisDelay.zipファイルをダウンロードしました。

このファイルをArduino / librariesディレクトリに解凍します(IDEファイル->設定ウィンドウを開いて、ローカルArduinoディレクトリの場所を確認します)。 ライブラリをインストールする方法-自動インストールが機能する場合もありますが、常にではない場合があります。 ファイルを手動で解凍するのが最も安全です。 millisDelayライブラリをインストールすると、Arduinoボードにロードして、9600ボーでシリアルモニター(5秒以内)を開いて使用できる3つのインタラクティブなサンプルが利用可能になります。 以下は、millisDelayライブラリを使用して書き換えられた以前の非ブロッキング遅延スケッチです。

 #include "millisDelay.h" int led = 13; millisDelay ledDelay; void setup(){pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、HIGH); // ledDelay.start(10000)をオンにします; // 10秒の遅延を開始} void loop(){//遅延がタイムアウトしたかどうかを確認if(ledDelay.justFinished()){digitalWrite(led、LOW); // ledをオフSerial.println( "Turned LED Off"); } //ここに他のループコード。 。 。 Serial.println( "Run Other Code"); } 

millisDelayライブラリコードを見ると、前のスケッチのコードがライブラリのstart ()メソッドとFinished()メソッドに移動したことがわかります。

これはledDelayですか、またはledTimerですか? 好きな用語を使用できます。 一度実行するシングルショット遅延には... delayを使用し、繰り返しを実行するには…timerを使用する傾向があります。

ステップ5:遅延とタイマーの例

以下に、2つの基本的な遅延とタイマーのスケッチ、および対応するmillisDelayライブラリを示します。 これらの例は、1回限りの(単発)遅延と繰り返しの遅延/タイマー用です。

シングルショット遅延

シングルショット遅延は、一度だけ実行されてから停止する遅延です。 Arduino delay()メソッドの最も直接的な代替品です。 遅延を開始し、それが終了したら何かをします。 BasicSingleShotDelayはプレーンコードであり、SingleShotMillisDelayはmillisDelayライブラリを使用します。

BasicSingleShotDelay

このスケッチはBasicSingleShotDelay.inoで利用可能です

 int led = 13; //ピン13には、ほとんどのArduinoボードにLEDが接続されています。 
符号なしlong DELAY_TIME = 10000; // 10秒の符号なしlong delayStart = 0; //遅延が開始した時間bool delayRunning = false; //遅延の完了を待機している場合はtrue void setup(){pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、HIGH); // ledをオンにします//遅延を開始しますdelayStart = millis(); delayRunning = true; } void loop(){//遅延がタイムアウトしたかどうかを確認if(delayRunning &&((millis()-delayStart)> = DELAY_TIME)){delayRunning = false; //遅延の終了-シングルショット、一度だけdigitalWrite(led、LOW); // ledをオフにする}}

上記のコードでは、遅延の期限が切れるのを待つことなくloop()が実行を続けています。
loop()の各パスで、現在のmillis()delayStart時間の差がDELAY_TIMEと比較されます。 タイマーが間隔の値を超えると、目的のアクションが実行されます。 この例では、遅延タイマーが停止し、LEDが消灯します。

SingleShotMillisDelay

以下は、millisDelayライブラリを使用して書き換えられたBasicSingleShotDelayスケッチです。 このスケッチは、SingleShotMillisDelay.inoで利用できます。

上記のコードがmillisDelayクラスのクラスメソッドにラップされているmillisDelayバージョンを次に示します。

 #含める 

int led = 13; //ピン13には、ほとんどのArduinoボードにLEDが接続されています。 millisDelay ledDelay; void setup(){//デジタルピンを出力として初期化します。 pinMode(led、OUTPUT); digitalWrite(led、HIGH); // ledをオンにします//遅延を開始しましたledDelay.start(10000); } void loop(){//遅延がタイムアウトしたかどうかを確認if(ledDelay.justFinished()){digitalWrite(led、LOW); // ledをオフにする}}

繰り返しタイマー

これらは、遅延/タイマーの繰り返しの簡単な例です。 BasicRepeatingDelayはプレーンコードで、RepeatingMillisDelayはmillisDelayライブラリを使用します。

BasicRepeatingDelay

このスケッチはBasicRepeatingDelay.inoで利用可能です

 int led = 13; //ピン13には、ほとんどのArduinoボードにLEDが接続されています。 
符号なしlong DELAY_TIME = 1500; // 1.5秒の符号なしlong delayStart = 0; //遅延が開始した時間bool delayRunning = false; //遅延が完了するまで待機している場合はtrue bool ledOn = false; // led状態を追跡するvoid setup(){pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、LOW); // ledをオフにしますledOn = false; //遅延を開始しますdelayStart = millis(); delayRunning = true; } void loop(){//遅延がタイムアウトしたかどうかを確認if(delayRunning &&((millis()-delayStart)> = DELAY_TIME)){delayStart + = DELAY_TIME; //これは遅延のドリフトを防止します// led ledOn =!ledOnを切り替えます; if(ledOn){digitalWrite(led、HIGH); // ledをオンにする} else {digitalWrite(led、LOW); // ledをオフにする}}}

使用する理由
delayStart + = DELAY_TIME;
遅延を再実行するためにリセットするために、 millis()-delayStartが> DELAY_TIMEになる可能性を考慮します。これは、 millis()が増加したか、 loop()内の他のコードが原因でスローダウンするためです。 たとえば、長い印刷ステートメント。 (手順7のループモントーの追加を参照)

もう1つのポイントは、 startup()の最後に遅延を開始することです。 これにより、 startup()の実行に時間がかかる場合でも、 loop()の開始時にタイマーが正確になります

RepeatingMillisDelay

以下は、millisDelayライブラリを使用して書き換えられたBasicRepeatingDelayスケッチです。 このスケッチはRepeatingMillisDelay.inoで利用可能です

 #含める 
int led = 13; //ピン13には、ほとんどのArduinoボードにLEDが接続されています。 bool ledOn = false; // led状態を追跡するmillisDelay ledDelay; void setup(){//デジタルピンを出力として初期化します。 pinMode(led、OUTPUT); //デジタルピンを出力として初期化します。 digitalWrite(led、LOW); // ledをオフにしますledOn = false; //遅延を開始ledDelay.start(1500); } void loop(){//遅延がタイムアウトしたかどうかを確認if(ledDelay.justFinished()){ledDelay.repeat(); //ドリフトなしで遅延を再開します// led ledOn =!ledOn;を切り替えます if(ledOn){digitalWrite(led、HIGH); // ledをオンにする} else {digitalWrite(led、LOW); // ledをオフにする}}}

ステップ6:その他のMillisDelayライブラリ関数

上記のstart(delay)Finished()およびrepeat()関数に加えて、millisDelayライブラリには
stop()で遅延タイムアウトを停止します。

isRunning()は、まだタイムアウトしておらず、停止されていないかどうかを確認します。

restart()は、同じ遅延間隔を使用して、今から遅延を再開します。

finish()により、遅延を強制的に早期に期限切れにします。

remaining()は、遅延が終了するまでのミリ秒数を返し、

delay()は、 start()に渡された遅延値を返します

ライブラリのマイクロ秒バージョン

millisDelayは、遅延をミリ秒単位でカウントします。 マイクロ秒単位で時間を計測することもできます。 そのクラスを書くのは読者の練習問題です。 ( ヒント:クラスの名前をmicroDelayに変更し、発生したmillis()をmicros()に置き換えます

遅延の凍結/一時停止

remaining()ミリ秒を保存して遅延を停止することにより、遅延をフリーズまたは一時停止できます。その後、残りのmSを遅延として再起動することにより、遅延をフリーズ解除できます。 例:FreezeDelay.inoの例を参照してください

 mainRemainingTime = mainDelay.remaining(); //メイン遅延mainDelay.stop()で実行するために残っている時間を思い出します; // mainDelayを停止します注:mainDelay.justFinished()は、stop()…mainDelay.start(mainRemainingTime);の後に決して真になりません。 //フリーズ後に再起動します 

ステップ7:警告の言葉–ループモニターを追加する

残念ながら、標準のArduinoライブラリの多くはdelay()を使用するか、AnalogReadやSoftwareSerialなどの一時停止を導入します。 通常、これらの遅延はわずかですが、加算される可能性があるため、 loop()の先頭にモニターを追加して、実行速度を確認することをお勧めします。

ループモニターは、点滅の例と非常によく似ています。 loop()メソッドの上部にある小さなコードは、 loop()が実行されるたびにLedを切り替えるだけです。 その後、Hzスケールのデジタルマルチメーターを使用して、LEDピン(この場合はピン13)の出力の周波数を測定できます。

コードは次のとおりです。

 //ループモニター-これにより、loop()が1msごとに少なくとも1回実行されることがチェックされます 
//(c)2013 Forward Computing and Control Pty。Ltd. // www.forward.com.au> // //このサンプルコードはパブリックドメインです。 int led = 13; //バッテリーが接続されている場合、FioV3では使用しない//ほとんどのArduinoボードでピン13にLEDが接続されている。 // Arduino IDE 1.5以上を使用している場合、「led」の代わりに事前定義された// LED_BUILTINを使用できます//リセットを押すと、セットアップルーチンが1回実行されます:void setup(){//デジタルピンを出力として初期化します。 pinMode(led、OUTPUT); //ここに他のセットアップコードを追加します} //ループルーチンは永遠に繰り返し実行されます:void loop(){//各ループのled出力を切り替えますled周波数は500Hz以上を測定する必要があります(つまり<1mSオフおよび<1mSオン)if(digitalRead(led)){digitalWrite(led、LOW); //電圧をLOWにしてLEDをオフにします} else {digitalWrite(led、HIGH); // LEDをオンにします(HIGHは電圧レベルです)} //ここにループコードの残りを追加します}

こちらからモニターコードをダウンロードできます。 Unoボードでこのコードを実行すると、ピン13とGNDの間に接続されたHz範囲のマルチメーターは57.6Khzになります。 すなわち、約100回> 500hz。

loop()にコードを追加すると、Hzの読み取りが減少します。 すべての状況で、500Hz(loop()実行ごとに1mS)をはるかに超えていることを確認してください。

関連記事