第8回 スイッチ制御の問題点を解明する

「スイッチを押したら、LEDをピカッと光らせる」というプログラムが思った通りに動作しなかった原因を解明します。

目次

プログラムの確認部分

前回の記事で「スイッチを押したら、LEDをピカッと点灯する」というプログラムを作成したはずなのに、動作確認したところ「スイッチを押している間、LEDが点灯する」という結果になってしまいました。

今回の記事では、なぜそのような動作になってしまうのか、原因を解明していきます。


プログラムの中で「スイッチを押したら、LEDをピカッと点灯する」ように書いた部分を取り出してみます。

void loop() {
  // 左スイッチ状態を読み取り、ONだったらLEDを一定時間点灯する
  if( digitalRead(SWITCH_HIDARI) == ON ) {
    digitalWrite(LED_BLUE, HIGH); // LEDを点灯
    delay(TENTOU_JIKAN);          // 一定時間待つ
    digitalWrite(LED_BLUE, LOW);  // LEDを消す
  }
}

これから、この部分がなぜ「スイッチを押している間、LEDが点灯する」という動作になるのか、考えていきます。

スイッチを押す動作

このスケッチは、人が操作するスイッチの状態を確認しながら動作しています。

そこで、人がスイッチを操作する様子を振り返ってみましょう。

ここで質問ですが、スケッチの動作確認をしたとき、どの程度の時間スイッチを押したでしょうか?

おそらく「プチッ」っという感じで短い時間押したと思います。

この「プチッ」と押した時間はどのぐらいですかね…?

人によって違うと思いますが、おそらく長くて1秒程度、短くても0.2〜0.3秒程度ではないでしょうか。

これからスケッチの動作を追っていきますが、スイッチを押す時間は0.5秒と仮定して話を進めたいと思います。

スイッチの状態は、次のように最初はOFF、0.5秒間ON、そのあとOFFとして話を進めます。

タイムチャート1

プログラムの動作速度

人がスイッチを押す時間を0.5秒としました。

ところで、Arduino Microはスケッチをどのぐらいの速度で処理しているのでしょうか?(次のスケッチは#defineの定義名ではなく、端子名で書いています)

void loop() {
  // 左スイッチ状態を読み取り、ONだったらLEDを一定時間点灯する
  if( digitalRead(A5) == 0 ) {
    digitalWrite(12, HIGH); // LEDを点灯
    delay(50);          // 一定時間待つ
    digitalWrite(12, LOW);  // LEDを消す
  }
}

このスケッチでは ifdigitalRead()digitalWrite()delay()を利用しています。

このうち、ifdigitalRead()digitalWrite()は処理時間がほとんどかかりません。厳密な計算はしていませんが、いずれもだいたい1秒間に数十万回〜100万回程度処理できます。

つまり、1つの処理にかかる時間はだいたい0.000001秒程度といったところでしょうか。

一方、delay()は引数の数字によって処理にかかる時間が変わります。今回のプログラムでは引数に50を指定していますので、delay関数では0.05秒かかっています。

スケッチとしては、いずれの処理も1行で書かれていますが、同じ1行でも処理時間にはかなり差がある、という点に注意してください。

これから上のスケッチについて、スイッチがOFFのときとONのときの処理の様子を確認しますが、この時間感覚を頭に入れておいていただければと思います。

スイッチがOFFのときの処理のようす

最初はスイッチがOFFのとき、Arduino Microはどのような処理をしているのか確認します。

このスケッチで、スイッチがOFFのときはdigitalRead(A5)の戻り値は1になります。

ifの条件はdigitalRead(A5) == 0ですので、スイッチがOFFのときは条件は成立しません。

loop関数はArduino Microが動作している限り何度も繰り返し実行します。

つまり、スイッチがOFFの間、「ifの条件を確認し、成立しないので何もしない」という動作を1秒間に何十万回も繰り返しています

スイッチがONのときの処理のようす

次に、スイッチがONのとき(=スイッチをONにしている0.5秒間)、Arduino Microはどのような処理をしているのか確認します。

スイッチがONのときはdigitalRead(A5)の戻り値は0になります。

ifの条件はdigitalRead(A5) == 0ですので、条件成立となります。

条件が成立しますので、ifの中の次の処理が実行されます。

    digitalWrite(12, HIGH); // LEDを点灯
    delay(50);          // 一定時間待つ
    digitalWrite(12, LOW);  // LEDを消す

これらの処理のうち、digitalWrite関数は1秒間に100万回程度実行できるほどの速度ですので、処理時間はほとんど無視できます。

時間がかかるのはdelay関数で、このスケッチでは引数として50を指定していますので、delayで関数の処理時間は0.05秒です。

人がスイッチを押す時間の長さは0.5秒と仮定しましたので、Arduino Microの処理時間とはかなり違う、ということがわかりました。

次にこれらの情報を元にして、スイッチを押すとLEDがピカッと光らない原因を考えていきます。

ピカッと光らない原因

それでは、スイッチが押されたときにArduino Microの内部でどのような処理がされているのか、図で考えてみましょう。

Arduino Microが動作を開始した後、しばらくしてから人がスイッチを押しますので、それまでの間スイッチはOFFです。

スイッチがOFFの間は、次のイラストのように膨大な回数のifの条件成立を確認しています。

条件は成立しないので、「条件が成立しているか確認する」という処理をすごいスピードで何度も繰り返します

タイムチャート2

次に、人がスイッチを押すと、押したタイミング以降、最初のifの条件判断で条件が成立し、ifの中の処理を行います。(次のイラストの赤矢印部分)

タイムチャート3

このifの条件が成立したときの処理を詳しく考えていきます。

上のイラストの赤矢印を拡大すると、次のような処理をしています。

タイムチャート4

この部分の処理時間は、delay(50);以外はほとんど時間がかかりませんので、ほぼ0.05秒の処理時間です。


この処理が終わるとloopの処理は終わりますので、Arduino Microは再度loopの処理を繰り返します。つまり、再度ifの条件判断を行います。

スイッチはONのままですから、条件が成立して再度ifの中の処理を繰り返します。(次のイラストのオレンジ部分)

タイムチャート5

ここで、1回目の処理と2回目の処理の境目をを見てみてください。(次のイラストの黒点線枠部分)

タイムチャート6

上の黒点線枠で囲んだ部分は、digitalWriteifdigitalWriteを処理しています。

ここで、とても重要なポイントがあります。

黒点線枠内の処理では、digitalWrite関数でLEDを一度OFF、その後ONにしていますが、OFFとONの間はifの条件判断処理しかありません。
これらの処理はほとんど時間がかかりませんので、見た目ではLEDは消えたようには見えず、LEDは点灯したままに見えてしまうんです!

つまり、上のイラストの処理は、見た目には次のようにLEDが点灯したままに見えてしまっているわけです。

タイムチャート7

さらにこのあとも、スイッチが押されている間は同じように処理が続くため、LEDは点灯したままに見えるわけです


スイッチを押している間、LEDが点灯してしまう原因がわかりました。

それでは、「スイッチを押すと、LEDがピカッと光る」というスケッチはどのように考えればいいのでしょうか。

次回の記事で、対策を考えてスケッチを作成していきます。

ミニチャレンジ課題

課題

ミニチャレンジ課題を用意してみました。今回はスケッチ作成ではなく、考える内容にしてみました。

前回の記事で最初に作成した「スイッチが押されたら、LEDを3秒間点灯する」というスケッチは問題なく動作しました。(次のスケッチです)

/*
 *  スイッチ制御プログラム
 *    スイッチを押すと一定時間LEDを点灯する
 */

#define LED_BLUE  12     // 青色LEDの接続端子名
#define SWITCH_HIDARI A5 // スイッチの接続端子名

// スイッチの状態
#define OFF 1
#define ON  0

// LEDの点灯時間(単位:ms)
#define TENTOU_JIKAN 3000


void setup() {
  pinMode(LED_BLUE, OUTPUT);  // LEDのピンを出力に設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);  // スイッチのピンを入力に設定
  digitalWrite(LED_BLUE, LOW);  // 最初はLEDをOFFに設定しておく
}


void loop() {
  // 左スイッチ状態を読み取り、ONだったらLEDを一定時間点灯する
  if( digitalRead(SWITCH_HIDARI) == ON ) {
    digitalWrite(LED_BLUE, HIGH); // LEDを点灯
    delay(TENTOU_JIKAN);          // 一定時間待つ
    digitalWrite(LED_BLUE, LOW);  // LEDを消す
  }
}

このスケッチはなぜうまく動作していたのでしょうか?

解答例

上のスケッチでは、スイッチがONのときにifの条件が成立してifの中の処理が実行されます。

    digitalWrite(LED_BLUE, HIGH); // LEDを点灯
    delay(TENTOU_JIKAN);          // 一定時間待つ
    digitalWrite(LED_BLUE, LOW);  // LEDを消す

この処理でdigitalWriteの処理時間はほとんどかかりませんが、delayでは3秒待ちをしています。

人がスイッチを「プチッ」と押す時間は長くても1秒程度です。

そのため、delayの待ち時間である3秒の間に、人はスイッチを離してしまいます。

結果として、上の処理が終わる頃にはスイッチがOFFになっていて、loopで次に処理をするときはifの条件は成立しないため、結果としてうまく動いていました。

更新履歴

日付内容
2021.8.6新規投稿
2025.2.4説明内容変更
ミニチャレンジ課題内容変更・解答例追加目次に戻る
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次