Arduino Lチカ最短コード導出

Arduino Lチカ最短コード導出

2024-10/26

マイコンの生存確認のために、Lチカさせることがあります。ここでのLチカは、他の処理と共存させる必要があり、ループを止めない非同期的なコードでなければなりません。

しかし、毎回非同期的なコードを書くのは少々手間だったりするので、手軽な書き方を模索しました。

↓ 現時点での最短コードです。

const int LED_PIN = 0;

void setup()
{
    pinMode(LED_PIN, OUTPUT);
}

void loop()
{
    digitalWrite(LED_PIN, millis() % 2000 > 1000);
}

解説

millis 関数

millis 関数は、マイコンが起動してからの経過時間をミリ秒単位で返してくれる関数です。

まずは millis 関数の返す値をシリアルプロッタで見てみます。

void setup()
{
    Serial.begin(115200);
}

void loop()
{
    Serial.println(millis());
}

時刻に比例して増加していきます。

millis関数の値

剰余算をしてみる

この値に剰余算をしてみます。

void loop()
{
    Serial.println(millis() % 2000);
}

次のようなのこぎり波になります。2 秒経過すると 0 に戻ります。

剰余算後のmillis

閾値を設けてみる

このノコギリ波に閾値を設け、閾値を超えているか判定すると L チカ出来そうです。

また閾値を調整することで、点灯時間の調整もできそう 👀

閾値を設けるイメージ

完成

const int LED_PIN = 0;

void setup()
{
    pinMode(LED_PIN, OUTPUT);
}

void loop()
{
    digitalWrite(LED_PIN, millis() % 2000 > 1000);
}

厳密には次のようにした方がいいかもです。HIGH == 1, LOW == 0 であることを Arduino が保証しているか不明なので。

digitalWrite(LED_PIN, (millis() % 2000 > 1000) ? HIGH : LOW);

マイコンボード上に搭載のLED

このLEDと接続されているピン番号は LED_BUILTIN というマクロで定義されているため、次のように書けます。

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
    digitalWrite(LED_BUILTIN, millis() % 2000 > 1000);
}

複数点滅

非同期的に処理できる (ループを止めない) 証拠に、複数の LED を別々の周期で点滅させられます。

const int LED_PIN0 = 0;
const int LED_PIN1 = 1;

void setup()
{
    pinMode(LED_PIN0, OUTPUT);
    pinMode(LED_PIN1, OUTPUT);
}

void loop()
{
    digitalWrite(LED_PIN0, millis() % 1000 > 500);
    digitalWrite(LED_PIN1, millis() % 100 > 50);
}

ダメポイント

次のような欠陥があるのですが、動作確認用途であれば問題ないと思います。

  • millis() のオーバーフローに対応できない。オーバーフローした直後一瞬、点滅周期がおかしくなる。

  • ループに入った時に millis が 0 から始まらない場合(setup 内に delay がある場合など)、点滅の始まりの点滅周期がおかしくなる。

↓ これらに対応したスケッチ

厳密な非同期Lチカ
const int LED_PIN = 0;

void setup()
{
    pinMode(LED_PIN, OUTPUT);
}

static uint32_t invertedMs;  ///< 最後に反転した時刻
static bool     isHigh;      ///< 出力ピンの状態

void loop()
{
    if (millis() - invertedMs > 1000)
    {
        isHigh = !isHigh;     // 出力を反転
        digitalWrite(LED_PIN, isHigh);
        invertedMs = millis();
    }
}

剰余算が重い 👊

剰余算の処理時間が気になる場合、キャストや論理積によって(無理矢理)置き換えられます。

digitalWrite(LED_PIN, (uint8_t)millis() > 200);
// 同義: digitalWrite(LED_PIN, millis() % 256 > 200);
digitalWrite(LED_PIN, (millis() & 0b111111111) > 0b011111111);
// 同義: digitalWrite(LED_PIN, (millis() % 512) > 255);

おわり

今回説明した、のこぎり波に閾値を設けて HIGH、LOW を切り替える手法は、PWM 波形の生成原理を参考にしています。L チカを超周期の長い PWM と見ることができるのでは、と思いこのコードに至りました。

最後までご覧頂き有難うございました~ 👋