delay() Arduino 関数: タイトなループとブロッキングコード
Arudino プロジェクトを作っていて、ある時間間隔で何かを発生させたいと思ったことはありませんか? たとえば、3 秒ごとにサーボを動かしたいとか、1 分ごとに Web サーバーにステータスのアップデートを送信したいとか、
どのようにそれを行うのでしょうか。 これを助けてくれる、シンプルでわかりやすい関数はないでしょうか。 はい、あります! 以下のビデオで、millis() 関数と同様に delay 関数についても説明します。
これは、millis() 関数ミニシリーズのパート 2 です。 パート 1 では millis() 関数が何を行うのかを理解し、パート 2 ではタイト ループとブロック コードについて、パート 3 では millis() 関数が delay() 関数より優れている場合について説明します。
このレッスンのトピック
- タイト ループ
- ブロック コード
- 音楽を刺激する古代のレシピ
Millis() vs Delay()
そこで、何かを設定した間隔で発生させたい、ソリューションを探しているのだとします。 あなたの答えが遅延関数を使用することであるなら、まあ、あなたは一種の正解です。
よりクールで洗練されたオプションは、Arduino の millis() 関数です。 そして、Arduinoのコードでイベントの時間を計るためにmillis関数を使い慣れれば慣れるほど、後でプログラムに他のパーツを組み込むのが簡単になります。
一度millis()関数を使い始めると、より幸せに、より明るく、そしてなんと、人に好かれるようになります。
Tight Loop
最初にタイトループという概念について説明しておきましょう。
タイトループのデモのために、Arduino のスケッチを見てみましょう。 最も基本的なスケッチから始めて、2つの関数だけを持っています:void setupとvoid loopです。
Void loopはループ内(中括弧内)にあるすべてのコード行を通過させることができます。
最初の行を実行し、次に 2 行目を実行し、3 行目を実行し、といった具合に、一番下まで実行されます。
ループの実行速度はどのくらいですか。 どのArduinoボードを使用しているかによりますが、Arduino Unoのクロックスピードは16メガヘルツです。 つまり、1秒間に1600万回の命令がArduino上で起こっていることになります!
コードの1行は必ずしも1命令とは限らないのですね。 実際には、複数の命令である可能性が高いです。 しかしそれでも、これは比較的高速です (コンピュータのプロセッサはギガヘルツの速度で動作していると思われます… これは数十億です)。
では、その空のスケッチをタイトループと見なしますか。 間違いなく、それはあなたがループを作ることができるのと同じくらい速く、タイトなものです。 ループの内部には実行するものがないので、スケッチを通過するのにかかる時間は実質的にゼロに等しいのです。 別の言い方をすると、ループの開始から終了までの間隔が短い (したがって、高速、または「タイト」である)。
これはタイトループでしょうか。 つまり、ループの開始から終了まで、かなりの時間がかかるのでしょうか? 3034>
しかし、このループは前の例ほどタイトではないことに注意する必要があります。 前の例では、何もコードがありませんでした。 そのため、ループ内をただただ駆け抜けるだけでした。 今は serial print という関数があるので、シリアル モニターに “Ice Ice Baby” と表示するのに(ほんの)少し時間がかかるでしょう。 そこで、もう少しコードを追加してみましょう。 ボタンが押されたかどうかを確認し、押された場合はシリアルモニタに何か新しい情報を送るようにします
そこで、ボタンを宣言してif文を使ってボタンが押されたのかどうか確認しました。 ピン 5 の電圧は高いですか。
これはタイトループでしょうか。 ループの開始から終了まで、かなり速いですか。
はい、まだかなり速いです。 これは、かなりタイトなループです。 4 行のコードを持っています。 シリアル モニターに出力し、ボタンが押されているかどうかを簡単にチェックしています。 そして、もしボタンが押されていたら、何かをプリントアウトしています。
次に、Arduinoのdelay()関数を使って、このプログラムに遅延を追加してみましょう。
これでもタイトループか? ループの開始から終了までの、時間が長いのでしょうか? いいえ、これは間違いなくタイトループではありません。
プログラム全体は、この遅延コードが終了するのを待つ間、停止します。 あなたは12品目以下の列に並びますが、あなたの前にいる人がチェックブックを取り出し、小切手を書き始めるのです。 ちょっとの間、そこにいることになるんだ。 ここでも同じことが言える。
だからこれはタイトループではないんだよ。 ループの開始から終了までの時間はかなり重要です。 特に、ここ数回のプログラムと比較すると 時間の桁が大きいのです。
さて、ここで実証しようとしたことは、タイトなループに関するこの全体の考え方は相対的であるということです。 すべてはアプリケーションに依存します。 1,000 万分の 1 秒ごとにセンサーの状態をチェックする必要がある場合、3 行のコードを持つプログラムは十分にタイトではないかもしれませんが、それは場合によります。
もう 1 つのポイントは、すべてのコード行の実行に同じ時間がかかるわけではないことです。 たとえば、serial print のような多くのことを行う関数を呼び出している場合、その 1 行のコードは他の 10 行のコードよりもはるかに長い時間がかかる可能性があります。
Blocking Code
プログラムがある時点で停止して、いくつかのコードを実行するのにやや時間がかかる場合、このコードはブロック コードと呼ぶことがあります。
私たちのプログラムでは、ブロッキング・コードとして動作する遅延関数を持っています。
ブロッキングコードは、delay()関数を使用したときだけではありません。
さて、このループはどのくらい続くのでしょうか。
そして、このforループの後のコードはどうでしょうか。 それは実行できるのでしょうか。 いいえ、for ループによってブロックされているため、待機する必要があります。
この for ループは、メインループの内部にネストされています。 forループは “きつい “ループですか? 回答する前に、どのように考えるべきかを再度強調しておきます。この for ループを上から下へ通過するのに多くの時間がかかりますか。
まあ、そうでもありません。 コード行数はわずか 2 行です。
しかし、プログラムがその下のコードに到達する前に、多くの反復を経なければならないタイトなループなのです。
Delay() 関数の詳細
この遅延関数についてもう少し話しましょう。
最初に、delay 関数はループの緊密性を低下させると述べました。 タイトなループがある場合に遅延関数を追加すると、より多くの時間がかかり、タイトでなくなります。 つまり、ループの開始から終了までにかかる時間の量は、遅延関数によって増加します。
また、遅延関数がコードをブロックすることもわかっています。 これは、先ほどの話と密接に関係しています。 遅延関数が実行されているとき、それが遅延している間、他のコードが実行されるのをブロックしています。
この遅延関数は完全に怠け者だと思うかもしれません! この遅延関数は完全に怠け者だと思うかもしれません!私たちのプロジェクトでは決して意味をなさないでしょう。 しかし、私たちが書く多くの単純なプログラムでは、delay 関数は素晴らしい働きをします。
ですから、必ずしもプログラミングのツールボックスからdelay()を排除する必要はありません。 多くのインスタンスで機能するシンプルなプログラミング関数であることを認識すべきです。
ただし、問題に直面し始める時があります。
シリーズの次のレッスンでは、パート 3 で、これが本当に問題になる場所を特定します。 プログラムで delay() 関数を使用する意味があるのはいつなのか、そして millis() 関数の使用に切り替えるのはいつなのかを学びます。
Review
最初に、ループの緊密性について話をしました。 ループのきつさは相対的なものであると述べました。
次に、ブロッキング コード (ブロックするコード) について説明しました。 基本的に、これは実行に時間がかかり、実行中にプログラムの他の部分の実行を停止するコードの総称です。
このレッスンを楽しんでいただけましたか。 次回は、millis関数を使って、Arduinoのコードに時間的、反復的なイベントを作成する方法を学びたいと思います。 次回もお楽しみに!