ArduinoでLED点滅(マルチタスク/並列処理)

 マイコンで基本のLED点滅(Lチカ)ですが、delay()を使った基本的なものから、ArduinoUNO(シングルコア)でマルチタスク/スレッド的な考え方で行うLチカの方法です。

 マルチタスクのライブラリもあるようですがいつものようにライブラリは無しで行ってみたいと思います。

スポンサーリンク

まずは普通のLチカ

 まずは普通のLチカdelay()を使った方法をいくつか紹介します。

  digitalWrite(13, HIGH);  
  delay(200);                      
  digitalWrite(13, LOW);   
  delay(200);                 

▲UnoのオンボードLED(13ピン)のLチカです。単純にI/OピンのHIGH/LOWをdelay()で待機させながらループさせているだけの処理です。delay()を使った待ち時間が無いと高速にLEDがOn/Offしてしまい、点灯してるようにしか見えません。

  if (digitalRead(13) == 0)digitalWrite(13, HIGH);
  else digitalWrite(13, LOW);
  delay(100);

▲ピンの状態を if 分で確認し、状態を反転してます。

  digitalWrite(13, digitalRead(13) == LOW ? 1 : 0);
  delay(500);

▲if 分の別表記方法です。少し記述が簡素になります。

bool LED = 0;

void loop() {
  LED = 1 - LED;
  digitalWrite(13, LED);
  delay(150);
}

▲変数を使ってHIGH/LOW(1,0)を反転させてます。

void loop() {
  LED = !LED;
  digitalWrite(13, LED);
  delay(100);
}

▲反転はこの式でもOKです。(ビットが反転します)。

  PORTB ^= B00100000;
  delay(500);

 ポートをビット操作で直接反転する方法です。記述的にはこれが簡単で処理も高速ですがArduinoIDE使ってる意味がほとんどなくなるのと、見通し悪くなりますね。

 次からが本題のマルチタスク(並列処理)についての内容です。

スポンサーリンク

delay()を使わない場合(マルチタスク風な方法)

 次からはdelay()を使わずに行う方法です。delay()を使うとどうしても無駄な待ち時間が発生してしまい、その間は他のことができなくなってしまうのですが、時間を監視することで平行して処理を行えるようになります。

void loop() {
  currentTime = micros();

  if (currentTime - LEDTimer > 500000) {
    LEDTimer = currentTime;
    digitalWrite(13, digitalRead(13) == LOW ? 1 : 0);
  }
}

▲時間を監視してLEDを点滅させてます。少し複雑に見えますがやってることはそれほど難しくないです。処理した時刻を記憶して、前回の処理時刻から0.5秒経過したらにLED(13ピン)をOn/Offさせているだけです。delay()を使うと待ち時間中は何もできなくなってしまいますが、こういった方法であれば0.5秒の待ち時間の間に平行して他の処理が可能となります。どういうことかというと、

▼例えば、

void loop() {
  currentTime = micros();

  if (currentTime - LEDTimer13 > 500000) {
    LEDTimer13 =currentTime;
    digitalWrite(13, digitalRead(13) == LOW ? 1 : 0);
  }
  if (currentTime - LEDTimer12 > 1000000) {
    LEDTimer12 = currentTime;
    digitalWrite(12, digitalRead(12) == LOW ? 1 : 0);
  }
}

▲13ピンを0.5秒間隔、12ピンを1秒間隔で 2つのLEDを違う間隔でOn/Off させてます。こういった感じで異なる処理を平行して行えます。2つのLEDを違う間隔で点滅させるのをdelay()でやろうとすると少し複雑になります。

▼また、さらにこういった形で

void loop() {
  currentTime = micros();

  if (digitalRead(13) == HIGH && currentTime - LEDTimer > 100000) {
    LEDTimer = currentTime;
    digitalWrite(13, LOW);
  }
  else if (digitalRead(13) == LOW && currentTime - LEDTimer > 900000) {
    LEDTimer = currentTime;
    digitalWrite(13, HIGH);
  }
}

▲一つのLEDで、ピン状態を確認追加することで、Onを0.1秒、Offを0.9秒といったOn/Offの間隔を変えた制御も可能になります。ちょっとしたPWM波形ですね。

 例えばですが、LEDを2つ準備して、

LED1はON:350m秒、OFF:650m秒で点滅
LED2はON:150m秒、OFF:550m秒で点滅

▲このようなに、On/Offのパルス幅も周期も違う2つのLEDを制御する場合でも、先ほどの方法を増やすだけで簡単に実現できます。逆にこれをdelay()で実現するのはかなり難しいと思います。

 こういった考え方であれば ArduinoUNO のようにシングルコアであっても、すべてのピンを使ってPWM出力やサーボを同時に各々に制御するなど、あたかもマルチタスク(スレッド)的な動作をしているような動きを行うことも可能となります。昔からPWM波形作るときとかは何気にこういったことをやっていたのですが紹介ががてらまとめておきました。

 ほかにもいろいろと方法があるかと思いますが参考例です。Lチカ一つとってもいろいろ記述する方法がありますし、その内容で非常に濃いことも考えることができますという話です。

▼参考▼

コメント