PUBLIC
PRACTICEPWM10 MIN READ

PWM で明るさを変える高速な ON/OFF で LED の明るさとサーボの角度を制御する

GPIO は 3.3V か 0V しか出せませんが、高速に ON/OFF を繰り返してその比率を変えると、平均として「中間の明るさ」を作れます。これが PWM。LED の調光、モーターの速度、サーボの角度、すべて同じ仕組みです。

  • ESP32-S3
  • Raspberry Pi 5 8GB
CONTENTSこの記事の目次

01 — WHAT IS PWM

PWM とは

Duty 25% (暗い)平均: 0.8VDuty 50% (中間)平均: 1.65VDuty 90% (明るい)平均: 3.0V← 周期 T (高速にON/OFFを繰り返す) →
DIAGRAM

PWMデューティ比の波形。1周期のうちHIGH(3.3V)を出力する時間の割合(デューティ比)を変えることで、見かけ上の平均電圧を連続的に変化させます。

  • 周波数:1 秒に何回 ON/OFF するか。LED なら 1kHz 以上あればちらつかない
  • デューティ比:1 周期のうち ON の割合。0%(消灯)〜 100%(全点灯)
  • 分解能:デューティ比を何段階で指定できるか。8bit なら 0〜255

L チカで sleep を短くしたときに「暗く点灯」に見えたのが、まさに PWM です。

02 — ESP32-S3

ESP32-S3 で LED を調光する

ESP32 の Arduino では analogWrite(pin, 0〜255) がそのまま使えます(esp32 core 3.x)。内部では LEDC という PWM 回路が動いています。配線は L チカと同じ(GPIO4 → 220Ω → LED → GND)。

CPPfade.ino
const int LED_PIN = 4;

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

void loop() {
  for (int duty = 0; duty <= 255; duty++) {     // だんだん明るく
    analogWrite(LED_PIN, duty);
    delay(5);
  }
  for (int duty = 255; duty >= 0; duty--) {     // だんだん暗く
    analogWrite(LED_PIN, duty);
    delay(5);
  }
}

周波数や分解能を指定したいときは ledcAttach(pin, 5000, 8) で設定して ledcWrite(pin, duty) で書きます(esp32 core 3.x の書き方。2.x では ledcSetup / ledcAttachPin)。

人の目は明るさを対数的に感じるので、duty を一定に増やしても「最初に急に明るくなって、後半は変化が分かりにくい」と感じます。duty = (i * i) / 255 のように二乗にすると自然に見えます。

03 — PI 5

Raspberry Pi 5 で調光する

PYTHONfade.py
from gpiozero import PWMLED
from time import sleep

led = PWMLED(17)

while True:
    for i in range(0, 101):
        led.value = i / 100      # 0.0(消灯)〜 1.0(全点灯)
        sleep(0.01)
    for i in range(100, -1, -1):
        led.value = i / 100
        sleep(0.01)

led.pulse() と書くだけでも同じ動きをします。Pi 5 の PWM はソフトウェアで作っているので、OS の都合で少しふらつきます。LED なら気になりませんが、サーボを精密に動かすならマイコン向きです。

04 — SERVO

サーボを PWM で動かす

ホビー用サーボ(SG90 など)は、20ms 周期(50Hz)のパルスの「幅」で角度を決めます。0.5ms で 0°、1.5ms で 90°、2.5ms で 180°(製品で少し違う)。スタックチャンの首がうなずいたり振り向いたりするのも、この 2 本のサーボです。

0° (0.5ms)狭いパルス90° (1.5ms)中間パルス180° (2.5ms)広いパルス周期は 20ms (50Hz) 固定で、HIGHの時間幅だけで角度が決まる
DIAGRAM

周期は 20ms(50Hz)固定。HIGH の時間幅だけでサーボの目標角度が決まります。

  • 配線:茶 = GND、赤 = 5V、橙 = 信号(GPIO)。信号は 3.3V で OK
  • 電源:動く瞬間に数百 mA 流れる。USB 給電のボードの 5V ピンから 1 個なら動くが、リセットするなら別電源 + GND 共通(→ 電源と電池)
  • ESP32:ライブラリマネージャで「ESP32Servo」を入れる
  • Pi 5:gpiozero の AngularServo(17, min_pulse_width=0.0005, max_pulse_width=0.0025)
CPPservo.ino(ESP32-S3)
#include <ESP32Servo.h>

Servo servo;

void setup() {
  servo.attach(6);          // 信号線を GPIO6 に
}

void loop() {
  servo.write(0);
  delay(1000);
  servo.write(90);
  delay(1000);
  servo.write(180);
  delay(1000);
}

05 — MOTOR

DC モーターの速度

モーターはトランジスタ(MOSFET)経由で PWM をかけると速度が変わります(→ トランジスタでスイッチする)。周波数は 1kHz 以上にすると「キーン」という音が減り、20kHz 以上なら聞こえなくなります。逆回転もさせたいならモータードライバ(DRV8833 など)を使います。

06 — WATCH OUT

注意点

サーボの電源は 3V3 から取らない

レギュレータの容量を超えてマイコンがリセットします。5V ピンか別電源にします。

周波数は用途で決まる

LED は数 kHz、サーボは 50Hz 固定、モーターは可聴域を避けて 20kHz 以上。同じ PWM でも設定が違います。

Pi 5 の PWM は精度に限界がある

ソフトウェア生成なので、サーボがぴくぴく震えることがあります。精密に動かすなら ESP32 に任せます。

07 — CHECK YOURSELF

理解チェック

Q1デューティ比 25% の LED はどう見える?

全点灯の約 1/4 の明るさ(目にはもう少し明るく感じる)。

Q2サーボを 90° にするパルス幅は?

約 1.5ms(20ms 周期のうち)。