ESP32でLEDフェード機能を試してみた:LEDCの仕組みと時間ずれの理由
ESP32でエルチカ(LED点滅)ができるようになったので、次のステップとしてLEDのフェード機能を試してみました。 じわじわ明るくなったり暗くなったりする、あの演出です。
Arduino IDE の [ファイル] → [スケッチ例] → [ESP32] → [Analog Out] → [LEDC Fade] を開くと、フェード機能を使ったサンプルが確認できます。
この記事では、
- LEDCとは何か
- フェードの仕組み
- 実際のプログラム構造
- フェード時間が理論値とズレる理由
をまとめて紹介します。
1. LEDCとは?ESP32のPWMフェード機能
ESP32にはLEDC(LED Control)というPWM制御用のハードウェアモジュールがあります。 フェード機能もこのLEDCに含まれています。
LEDCのフェードは、
- デューティ比(ON/OFF比)を少しずつ変化させる
- カウンターが目標値に達したら割り込みが発生
- 割り込みでフェード終了を検知
という仕組みで動作します。
私たちが設定するのは、
- カウンターの分解能(例:12bit)
- PWM周波数(例:5kHz)
- フェード時間(ミリ秒)
の3つだけ。 あとはLEDCが自動でフェード処理をしてくれます。
2. PWM波形を観察してみる
実際の波形を見ると、1周期の中でON時間(デューティ比)が徐々に変化しているのが分かります。
- 周波数:5kHz
- 1周期:200µs
- デューティ比が少しずつ変化 → 明るさが滑らかに変わる
LEDCは高速・低速の2グループを持ち、それぞれに
- 4つのタイマー
- 8つのチャンネル
が割り当てられています。 Arduinoのサンプルでは高速グループが使われます。
3. プログラムの流れ(タイマー設定 → チャンネル設定)
サンプルコードの流れを簡単に整理すると次の通りです。
① タイマー設定
- 分解能(例:12bit)
- 周波数(例:5kHz)
- 使用するタイマー番号
- 高速/低速モード
これらを構造体にまとめて ledc_timer_config() に渡します。
② チャンネル設定
- 使用するチャンネル番号
- 割り当てるGPIOピン
- 初期デューティ比
- 使用するタイマー番号
これを ledc_channel_config() に渡して設定します。
③ フェード開始
ledcFadeWithInterrupt() を呼ぶとフェードがスタートし、 目標デューティ比に達すると割り込みが発生してフラグが立ちます。
4. フェード時間が理論値とズレる問題
ここからが本題です。
例えば、
- フェード時間:3000ms
- 明→暗→明 の往復:6000ms
になるはずですが、実際に測ると約5秒しかかかりません。
他の値でも同様で、
| フェードタイム設定 | 理論値(往復) | 実測値 |
|---|---|---|
| 1000ms | 2000ms | 約1600ms |
| 1500ms | 3000ms | 約1600ms(なぜか同じ) |
| 2000ms | 4000ms | 約3200ms |
どれも 約0.8秒短い という結果になりました。
5. なぜズレるのか?原因は「ステップ間隔の自動計算」
フェード時間は次の式で決まります。
ここで問題なのが、
- ステップ間隔は整数値しか使えない
- ESP32側が自動で近似値を割り当てる
という点です。
例えば12bit分解能ならカウンター最大値は4096。 周波数5kHzなら1カウントあたり約0.2ms。
フェードタイム1000msを設定しても、 内部計算で最適なステップ間隔が1に丸められ、 結果として 1.6秒 になってしまう、というわけです。
6. 対策:フェード時間は「ズレる前提」で調整する
結論として、
- LEDCのフェード時間は設定値どおりにはならない
- 内部のステップ間隔が整数に丸められるため誤差が出る
- 正確なフェード時間が必要なら自分で補正する必要がある
という仕様になっています。
まとめ
- ESP32のLEDCはPWMフェードを簡単に実装できる便利な機能
- しかしフェード時間は内部計算の都合で設定値とズレる
- 正確な時間が必要な場合は補正や自前制御が必要
ESP32のLEDCは強力ですが、内部仕様を理解しておかないと「なんで?」とハマるポイントもあります。 今回の検証が、同じように悩んでいる方の参考になれば幸いです。
