基礎 · 読了まで7分

PWMとは?ArduinoのanalogWrite、デューティ比、周波数のガイド

PWM(パルス幅変調)とは何で、どう動くのか。デューティ比と周波数、ArduinoのPWMピン、analogWriteでのLEDの明るさとモーターの速度、サーボとの違い、ESP32のLEDCとRaspberry Pi PicoのPWMを解説します。

Arduinoのデジタルピンが知っているのは0 Vか5 Vの2つだけです。では、LEDを半分の明るさにしたり、モーターをゆっくり回したりするにはどうすればよいのでしょうか。答えはPWM(Pulse Width Modulation:パルス幅変調)です。ピンをとても速くオン・オフし、オンになっている時間の割合を変えることで、負荷に届く平均の電力を設定します。LEDの調光、モーターの速度制御、サーボの駆動、音を出すこと、さらにはスマホの充電器まで、この技術を使っています。

ArduinoでPWM駆動しているLED
PWMを使うと、LEDを1秒に何百回もオン・オフして、明るさを好きなレベルに設定できます。

PWMはどう動く?

PWM信号は矩形波です。しばらくHIGHになり、次にLOWになり、このサイクルを何度も繰り返します。オン・オフ1サイクルの長さが周期で、周期のうち信号がHIGHになっている割合がデューティ比です。デューティ比25%では信号は4分の1の時間だけオンなので、5 Vのピンは平均すると1.25 Vのようにふるまいます。LEDは1秒に約490回点滅しますが、目はそれについていけず、平均の明るさだけを見ます。モーターは慣性のおかげでパルスをならし、平均の速さで回ります。

デューティ比analogWriteの値平均電圧(5 V)LED
0%00 V消灯
25%641.25 V暗い
50%1272.5 V半分の明るさ
75%1913.75 V明るい
100%2555 V最大の明るさ

人の目は光に対して直線的には反応しません。暗いレベルでの変化にはとても敏感で、明るいレベルでの変化にはずっと鈍感です。そのため、デューティ比50%のLEDは半分よりずっと明るく見えます。自然に見える調光には、下で紹介するガンマ補正を使います。

周波数:PWMはどのくらい速い?

周波数は、信号が1秒に何回オン・オフするかです。Arduino Unoでは、5番と6番ピンが約980 Hz、3、9、10、11番ピンが約490 Hzで動きます。LEDにはこれで十分すぎるほどです。しかしモーターの場合、490 Hzは耳に聞こえる範囲なので、モーターがかすかにうなることがあります。ESP32やPicoなら周波数を20 kHzまで上げて静かにできます。ただし、必要以上に周波数を上げると、トランジスタやMOSFETが発熱します。

ボードPWMピン初期周波数分解能
Arduino Uno / Nano3, 5, 6, 9, 10, 11490 Hz(5と6:980 Hz)8ビット(0〜255)
Arduino Mega2〜13、44〜46490 Hz(4と13:980 Hz)8ビット(0〜255)
ESP32ほぼすべての出力ピン調整可能(analogWrite:1 kHz)1〜16ビット
Raspberry Pi PicoすべてのGPIOピン調整可能16ビット(0〜65535)

analogWriteでLEDの明るさを変える

ArduinoではPWMをanalogWrite(pin, value)で出力し、値は0〜255です。紛らわしい名前ですが、この命令は本当のアナログ電圧ではなくPWMを出します。使えるのは~の付いたピンだけです。下のコードはLEDをゆっくり明るくしたり暗くしたりして、「呼吸する」ような効果を出します。

C++ (Arduino IDE)
1const int LED_PIN = 9;   // ~ işaretli PWM pini / PWM pin marked with ~
2
3void setup() {
4  pinMode(LED_PIN, OUTPUT);
5}
6
7void loop() {
8  for (int v = 0; v <= 255; v++) {   // yavaşça yan / fade in
9    analogWrite(LED_PIN, v);
10    delay(8);
11  }
12  for (int v = 255; v >= 0; v--) {   // yavaşça sön / fade out
13    analogWrite(LED_PIN, v);
14    delay(8);
15  }
16}
より自然な調光にするには、値を2乗してスケールします:analogWrite(LED_PIN, (long)v * v / 255);。ここでは(long)が重要です。Unoのintは32,767を超える数を扱えないので、255 × 255がオーバーフローしてしまいます。

ポテンショメーターで明るさを調整する

A0にポテンショメーターをつなげば、LEDの明るさを手で設定できます。analogRead()は0〜1023の値を返すので、4で割るとanalogWriteが必要とする0〜255の範囲になります。ポテンショメーターの配線はポテンショメーターとは?で解説しています。

C++ (Arduino IDE)
1void loop() {
2  int pot = analogRead(A0);   // 0–1023
3  analogWrite(9, pot / 4);    // 0–255
4}

PWMでDCモーターの速度を変える

小さなDCモーターでも数百ミリアンペアを消費しますが、Arduinoのピンが安全に出せるのは約20 mAだけです。そのため、モーターはトランジスタ、MOSFET、またはL298Nのようなモータードライバを通して駆動し、PWM信号はドライバの速度入力に送ります。L298Nではこの入力がENAピン(2つ目のモーターはENB)で、PWMを使うにはそこに付いているジャンパーを外す必要があります。IN1とIN2のピンで回転方向を決めます。

L298Nモータードライバモジュール
L298Nモータードライバ:ENAとENBピンのPWMで、2つのDCモーターの速度を別々に設定します。
C++ (Arduino IDE)
1const int ENA = 9;   // hız (PWM) / speed (PWM)
2const int IN1 = 8;   // yön / direction
3const int IN2 = 7;
4
5void setup() {
6  pinMode(ENA, OUTPUT);
7  pinMode(IN1, OUTPUT);
8  pinMode(IN2, OUTPUT);
9  digitalWrite(IN1, HIGH);   // ileri / forward
10  digitalWrite(IN2, LOW);
11}
12
13void loop() {
14  analogWrite(ENA, 255);  delay(2000);   // tam hız / full speed
15  analogWrite(ENA, 150);  delay(2000);   // orta hız / medium speed
16  analogWrite(ENA, 0);    delay(2000);   // dur / stop
17}
モーターをArduinoのピンに直接つながないでください。モーターが消費する電流と発生する逆電圧でピンが壊れます。また、デューティ値がとても低いと、モーターは回らずにうなるだけです。ほとんどのモーターは、動き出すのに80〜120程度の最低値が必要です。

サーボモーターとPWMは同じもの?

サーボモーターもパルスで制御しますが、考え方が違います。位置を決めるのはデューティ比ではなくパルスの幅です。サーボは1秒に50回(50 Hz)のパルスを受け取り、1ミリ秒のパルスで約0°、1.5 msで90°、2 msで180°になります。analogWriteではこのタイミングを作れないので、サーボにはServoライブラリを使います。詳しくはサーボモーターとは?で解説しています。

PWMから本当のアナログ電圧を得る:RCフィルター

オペアンプに基準電圧を与えたり、音声を出力したりするときのように、本当に一定の電圧が必要になることもあります。その場合は、PWMを抵抗とコンデンサで作ったローパスフィルターに通します。10 kΩと10 µFなら時定数は0.1秒で、出力はかなりなめらかになりますが、変化への反応は遅くなります。いろいろな値はRC時定数計算ツールで試せます。また、初代ESP32にはGPIO 25と26に2つのDACがあり、本当のアナログ出力を出せます。

ESP32のPWM:LEDC

ESP32では、PWMはLEDCというハードウェアで作られ、周波数と分解能を自分で選べます。Arduinoコア3.xではledcAttach(pin, frequency, resolution)とledcWrite(pin, value)を使います。古い2.xでは、チャンネルを使うledcSetupとledcAttachPinという命令でした。下の例は、5 kHz、8ビット分解能でLEDをフェードさせます。

C++ (Arduino IDE)
1const int LED_PIN = 2;
2
3void setup() {
4  ledcAttach(LED_PIN, 5000, 8);   // 5 kHz, 8 bit (0–255) — ESP32 Arduino core 3.x
5}
6
7void loop() {
8  for (int d = 0; d <= 255; d++) { ledcWrite(LED_PIN, d); delay(5); }
9  for (int d = 255; d >= 0; d--) { ledcWrite(LED_PIN, d); delay(5); }
10}

Raspberry Pi PicoのPWM(MicroPython)

PicoはすべてのGPIOピンでPWMを出力できます。MicroPythonではPWMクラスを使い、周波数をfreq()で、デューティ比を0〜65535の値をとるduty_u16()で設定します。

MicroPython
1from machine import Pin, PWM
2import time
3
4led = PWM(Pin(15))
5led.freq(1000)                        # 1 kHz
6
7while True:
8    for d in range(0, 65536, 512):    # yavaşça yan / fade in
9        led.duty_u16(d)
10        time.sleep_ms(5)
11    for d in range(65535, -1, -512):  # yavaşça sön / fade out
12        led.duty_u16(d)
13        time.sleep_ms(5)

PWMのプロジェクトアイデア

  • 呼吸するLED:スタンバイ中の機器のように、ゆっくり明るくなったり暗くなったりする状態表示ランプです。
  • 調光できるデスクライト:ポテンショメーターで明るさを設定できるLEDランプです。
  • 温度で動くファン:DHT11で測った温度が上がるにつれてファンを速くします。
  • 混色:3つのPWMチャンネルで、RGB LEDに何百万もの色を出します。
  • ロボットカー:2つのモーターの速度を別々に設定して、曲がるときの動きをなめらかにします。
  • 日の出ランプ:朝のアラームの前に、15分かけてゆっくり明かりを強くします。

よくあるミスと直し方

  • PWM非対応のピンでanalogWriteを使う:Unoでは、~のないピンはオンかオフにしかなりません。128以上はオン、それ未満はオフとして扱われます。
  • Servoライブラリと9番・10番ピン:UnoではServoライブラリがTimer1を使い、この2本のピンのPWMを無効にします。LEDやモーターは別のPWMピンに移してください。
  • tone()と3番・11番ピン:tone()はTimer2を使うので、これらのピンのPWMが乱れます。
  • モーターをピンに直接つなぐ:必ずトランジスタ、MOSFET、またはモータードライバを使ってください。
  • ガンマ計算のオーバーフロー:v * vがintの上限を超えます。(long)のキャストを加えてください。
  • PWMを本当のアナログとして扱う:テスターは平均値を表示しますが、信号は相変わらず0 Vと5 Vの間を行き来しています。繊細な回路にはRCフィルターかDACを使ってください。

PWMは、Arduinoで学ぶ考え方のなかでもとくに役立つものです。LED、モーター、ファン、スピーカーは、すべてこのシンプルなしくみで制御されています。基本についてはLEDとは?を見てみてください。BlueGraysアプリには、PWMでのLEDのフェード、ポテンショメーターでの明るさ調整、RGBの色のフェードのプロジェクトがコード付きでステップごとに載っています。

よくある質問

PWMを簡単にいうと何ですか?

PWMは、ピンをとても速くオン・オフし、オンになっている時間の割合(デューティ比)を変えることで、負荷に届く平均の電力を設定する方法です。0 Vか5 Vしか出せないデジタルピンで、LEDを暗くしたりモーターを遅くしたりできます。

Arduino UnoでPWMに対応しているのはどのピンですか?

UnoとNanoでは、3、5、6、9、10、11番ピンがPWMに対応していて、ボード上に~の印があります。Megaでは2〜13番と44〜46番ピンがPWMを出力できます。

analogWriteは本当のアナログ出力ですか?

いいえ。analogWriteが出すのはPWMで、ピンは0 Vと5 Vの間を高速で切り替わります。平均電圧は設定できますが、本当に一定の電圧が必要なら、PWMをRCフィルターに通すか、ESP32にあるようなDACを使ってください。

デューティ比はどうやって計算しますか?

デューティ比は、信号がHIGHになっている時間を周期全体で割ったもので、(HIGHの時間 / 周期)× 100で求めます。Arduinoでは、analogWriteの値を255で割って100をかけます。たとえば64なら約25%です。

ArduinoでPWMの周波数を変えるには?

analogWriteでは周波数は変わりません。Unoではタイマーのレジスタ(たとえば9番と10番ピンならTCCR1B)を変えて調整できますが、millisやライブラリに影響することがあります。ESP32ではledcAttachで、Picoではfreq()で周波数を直接設定します。

PWMでLEDが傷むことはありますか?

いいえ。LEDはパルスのたびに最大の電流で光りますが、その電流はもともと直列の抵抗で制限されていて、平均の電流はデューティ比に比例して下がります。正しい抵抗を使っている限り、PWMでLEDの寿命が縮むことはありません。

全プロジェクトを見る
ESP32のPWMでLEDをフェード
#110初級

ESP32のPWMでLEDをフェード

LEDの明るさを0から最大へ、最大から0へと滑らかに変化させます。Arduinoと同じようにanalogWrite()でPWMを制御します。ESP32 Arduino Core 3.0+では同じ構文がそのまま使えます。ピンの定義とpinMode、analogWriteだけで実現できます。

4.8(36)
54
Raspberry Pi PicoのPWMでLEDをフェード(呼吸のような明滅)
#160初級

Raspberry Pi PicoのPWMでLEDをフェード(呼吸のような明滅)

LEDを単に点滅させるだけでなく、呼吸するように明滅させます!Raspberry Pi PicoのPWM(パルス幅変調)を使い、スリープ中のMacBookのランプのように、明るさをゆっくり上げてからゆっくり下げます。machine.PWMクラス、duty_u16()(0–65535の16ビットの明るさ)、PWM周波数(freq(1000)で1秒間に1000回オン・オフし、ちらつきを感じさせない)、range()による段階的なループ(増加と減少)、デジタルピンからのアナログのような出力を学びます。PWMはRGBの混色やモーターなどに使われる、組み込み開発の基本技術です。

0.0(0)
4
ESP32と可変抵抗でLEDの明るさを制御(アナログ→PWM)
#115中級
プレミアム

ESP32と可変抵抗でLEDの明るさを制御(アナログ→PWM)

可変抵抗を使ってLEDの明るさをアナログ操作で調整します。ESP32のADCピンで可変抵抗からの0-3.3Vの電圧を読み取り、PWM(パルス幅変調)信号に変換してLEDに出力します。可変抵抗を回すと、LEDが徐々に明るくなったり暗くなったりします。Arduino Core 3.0+ではanalogWrite()を直接使えます。ESP32-S2/S3ではADCの分解能を12ビットに固定します。

4.5(19)
32
RGB LEDで滑らかに色を変える
#40初級
プレミアム

RGB LEDで滑らかに色を変える

1つのRGB LEDが赤から緑、緑から青、そして再び赤へとゆっくり変わり、虹色のサイクルを繰り返します。1回の色の変化には約2.5秒かかり、色が混ざることで、その間に黄色、ターコイズ、紫が自然に現れます。光が滑らかに暗くなったり明るくなったりする様子を、自分の目で確かめられます。

4.8(58)
76
ESP32でRGB LEDを制御
#111初級

ESP32でRGB LEDを制御

3個の個別のLEDの代わりに、1個のRGB LEDで無数の色を作ります。Arduinoと同じようにanalogWrite()でR/G/Bの値を調整し、虹色に変化させます。赤と緑を混ぜると黄色になるなど、色の仕組みを実際に確認できます。

4.6(29)
51
Raspberry Pi PicoでRGB LEDの色を順に切り替える(3チャンネルPWM)
#161初級

Raspberry Pi PicoでRGB LEDの色を順に切り替える(3チャンネルPWM)

1つのLEDで何百万もの色を表現できます!RGB LEDには、実は赤、緑、青の3つのLEDが入っています。それぞれをPicoの個別のPWMチャンネルで制御すると、赤+緑=黄、緑+青=シアン、3色すべて=白のように色を混ぜられます。このプロジェクトでは、LEDの色を8色に順番に切り替えます。複数のPWMチャンネル、関数の定義(def set_color(...))、RGBの混色(画面やスマート電球の仕組み)、8ビットから16ビットへの変換(x257で0–255を0–65535に変換)、LEDのカソードコモンとアノードコモンの違いを学びます。

0.0(0)
6
ESP32サーボモーター(ポテンショメーター制御)
#118中級
プレミアム

ESP32サーボモーター(ポテンショメーター制御)

このプロジェクトでは、ポテンショメーターでSG90サーボモーターを制御します。ポテンショメーターから読み取ったアナログ値を0~180度の範囲に変換し、サーボの角度を調整します。ESP32Servoライブラリは、ESP32の出力に対応した適切なGPIOでサーボ信号を生成します。このプロジェクトではGPIO 18を使います。ロボットアームやカメラ用ジンバル、自動ドアの機構に使われる基本的な制御方法です。

4.3(29)
42
ESP32の調色ライト(可変抵抗3個 + RGB LED + OLED)
#138上級
プレミアム

ESP32の調色ライト(可変抵抗3個 + RGB LED + OLED)

ESP32と3個の可変抵抗で、好みの色を作れるスマート調色ライトです。赤・緑・青の可変抵抗を回すと、カソードコモンのRGB LEDの色がリアルタイムに変わります。SSD1306 OLEDディスプレイには、各色の0~255の値、色の強さを示すバー、#FF8040などのWeb用HEXカラーコードをリアルタイムで表示します。小さなプロ仕様のカラースタジオとして、RGBの色の仕組み、ADCによるアナログ値の読み取り、PWMによる明るさの制御、I2Cディスプレイの表示設計を1つのプロジェクトで学べます。

4.7(6)
10