前回は複数のLEDをフォンフォンさせました。
今回は単独のLEDの明るさを制御して「じわ~」っと光らせてみましょう。
本家freenoveのテキストでは単体のLEDで回路を組みなおしていますが、ちょっと面倒なので、このままバーLEDアレイのGPIO2に接続しているLEDを使って明るさ制御をしていきます。
アナログとデジタルについて
アナログ信号は、時間と値の両方が連続的な信号です。一方、デジタル信号、または離散時間信号は、一連の量からなる時系列データです。実生活におけるほとんどの信号はアナログ信号です。身近なアナログ信号の例としては、一日の気温があります。気温は常に変化し続け、0°Cから10°Cに瞬間的にジャンプすることはできません。しかし、デジタル信号は瞬間的に値を変えることができます。この変化は、1と0(バイナリコードの基本)という数字で表現されます。これらの違いは、以下のようなグラフで比較するとよりわかりやすくなります。
プログラムにおいてはデジタル信号としてバイナリ(2進数)、つまり0と1の並びを使用することが多いです。バイナリ信号は2つの値(0または1)しか持たないため、安定性と信頼性に優れています。また、アナログ信号とデジタル信号は、相互に変換することが可能です。アナログからデジタルに変換する回路をADC、デジタルからアナログに変換する回路をDACといいます。
では、デジタル信号では強弱をどのように制御すればよいのでしょうか?その答えがPWMです。
PWM
PWM (Pulse-Width Modulation: パルス幅変調) は、デジタル信号を使ってアナログ回路を制御するための非常に効果的な手法です。一般的なプロセッサは、アナログ信号を直接出力することはできません。PWM技術は、この変換(デジタル信号からアナログ信号への変換)を非常に便利に実現します。
PWM 技術は、デジタルピンを使用して特定の周波数の矩形波を送信します。つまり、ハイレベルとローレベルの出力を交互に、一定時間ずつ持続させる方法です。ハイレベルとローレベルの各セットの合計時間は通常固定されており、これを周期と呼びます(注:周期の逆数は周波数です)。ハイレベル出力の時間は一般に「パルス幅」と呼ばれ、デューティサイクルとは、パルス幅 (PW) と波形の全周期 (T) の比をパーセンテージで表したものです。ハイレベルの出力時間が長くなればなるほど、デューティサイクルが長くなり、対応するアナログ信号の電圧が高くなります。以下の図は、パルス幅 0~100% に対応して、アナログ信号の電圧が 0~5V (ハイレベルは 5V) の間でどのように変化するかを示しています。
PWMのデューティサイクルが長いほど、出力は高くなります。この関係性が理解できれば、PWMを使ってLEDの明るさやDCモーターの速度などを制御することが可能です。以上の説明からわかるように、PWM は真のアナログ信号ではなく、電圧の実効値(PWM信号を平均化した際の電圧値に相当するもの)が対応するアナログ値に相当します。したがって、LEDなどの出力モジュールの出力を制御して、さまざまな効果を実現することができます。
ESP32-S3とPWM
ESP32-S3のLEDCコントローラー(LEDCは “LED Controller” の略で、ESP32-S3 内に搭載された高度なPWM信号を生成する機能を持つモジュールです。)は8つの独立したチャンネルを備えています。各チャンネルは、周波数、デューティサイクル、さらに精度(PWM信号の細かさを表します。ビット精度が高いほど、より細かい制御が可能になります。)を個別に制御することができます。従来のPWMピンとは異なり、ESP32-S3のPWM出力ピンは設定可能で、チャンネルごとに1つ以上のPWM出力ピンを持つことができます。最大周波数とビット精度の関係は次の式で表されます。式中のbitの最大値は31です。
分母のところはビットシフトという処理を表していますが、数学的には2のbit乗です。
例:8ビット精度(2^8 = 256。値の範囲は0~255)で、最大周波数80,000,000/256 = 312,500HzのPWMを生成します。
8ビット精度: この設定は、PWM信号の分解能を決定します。8ビット精度の場合は、256個の異なるデューティサイクル値を生成することができます。
最大周波数312,500Hz: この設定は、PWM信号の出力速度を決定します。312,500Hzの周波数では、1秒間に312,500回のオン/オフサイクルが発生します。
コードはこうです。
/**********************************************************************
Filename : BreathingLight
Description : Make led light fade in and out, just like breathing.
Auther : www.freenove.com
Modification: 2022/10/20
**********************************************************************/
#define PIN_LED 2 //define the led pin
#define CHN 0 //define the pwm channel
#define FRQ 1000 //define the pwm frequency
#define PWM_BIT 8 //define the pwm precision
void setup() {
ledcSetup(CHN, FRQ, PWM_BIT); //setup pwm channel
ledcAttachPin(PIN_LED, CHN); //attach the led pin to pwm channel
}
void loop() {
for (int i = 0; i < 255; i++) { //make light fade in
ledcWrite(CHN, i);
delay(10);
}
for (int i = 255; i > -1; i--) { //make light fade out
ledcWrite(CHN, i);
delay(10);
}
}
まずはマクロ定義部分。
PIN_LEDはLEDのピン番号ですね。GPIO2です。
CHN はチャンネル(チャネルとも)チャンネル0です。
FRQ は周波数、1KHzです。
PWM_BIT は精度、8ビットです。
次に12~13行目でマクロ定義した定数を使ってledcを初期化しています。
ledcSetup(CHN, FRQ, PWM_BIT); //setup pwm channel
ledcAttachPin(PIN_LED, CHN); //attach the led pin to pwm channel
ついにメインループです。
for (int i = 0; i < 255; i++) { //make light fade in
ledcWrite(CHN, i);
delay(10);
}
for (int i = 255; i > -1; i--) { //make light fade out
ledcWrite(CHN, i);
delay(10);
}
メインループにはforが2個あります。明るくするループと暗くするループですね。ループの継続条件式あるいは初期式には8ビットの精度を10進数に直した255が定義されています。255段階で徐々に明るくしたり、暗くしたりしています。255をべた書きせずにビットシフト演算子を使ってもよいです。ビットシフトは慣れるととても便利ですし、プログラムの処理速度も速いのですが、直感的ではないので使わなくても全然良いです。
1 << PWM_BIT
と書くと255と同じ意味になります。
実際の明るさを変更している箇所がledcWrite関数ですね。
そして、10ミリ秒ごとに明るさを変える動きですので、10ミリ秒×255=2.5秒かけて明るさが変わるという制御です。
次回は複数のLEDの明るさを制御して、おしゃれにフォンフォンさせてみましょう。
コメント