第10章 Arduino PWM 教學:analogWrite、占空比與 LED 調光實作

發布日期: |更新日期:
文章主圖:第10章 Arduino PWM 教學:analogWrite、占空比與 LED 調光實作

PWM(Pulse Width Modulation,脈衝寬度調變)是 Arduino 常用的輸出控制技術,可用來調整 LED 亮度、直流馬達轉速與部分負載的平均功率。

Arduino Uno 並沒有真正的類比電壓輸出。analogWrite() 會讓指定腳位快速切換 HIGH 與 LOW,透過改變高電位所占的時間比例,產生近似類比輸出的平均效果。

本章將從 Duty Cycle(占空比)開始,說明 Arduino Uno 的 PWM 腳位、analogWrite() 數值範圍、LED 調光與呼吸燈實作,並整理常見錯誤與安全注意事項。

本章學習目標

  • 了解 PWM(Pulse Width Modulation)的基本原理。
  • 知道 Arduino Uno 為什麼沒有真正的類比輸出。
  • 熟悉 analogWrite() 的語法與使用方式。
  • 理解 Duty Cycle(占空比)與 LED 亮度之間的關係。
  • 使用 PWM 控制 LED,並了解蜂鳴器與直流馬達的應用限制。

什麼是 PWM?

PWM 是 Pulse Width Modulation 的縮寫,中文稱為脈衝寬度調變。

PWM 並不是直接輸出連續變化的類比電壓,而是以固定頻率快速切換 HIGH(Arduino Uno 通常約為 5V)與 LOW(約為 0V),再改變 HIGH 持續時間占整個週期的比例。

當切換速度足夠快時,人眼或負載通常會感受到平均效果。例如 LED 會呈現不同亮度,直流馬達會產生不同轉速。蜂鳴器的音調控制則通常應搭配 tone() 等更適合的方式。

知識卡:PWM
項目 說明
全名 Pulse Width Modulation
中文名稱 脈衝寬度調變
Arduino 函式 analogWrite()
Arduino Uno PWM 腳位 D3、D5、D6、D9、D10、D11
數值範圍 0~255(8 位元)

Arduino Uno 有真正的類比輸出嗎?

Arduino Uno 使用的 ATmega328P 並沒有內建 DAC(Digital to Analog Converter),因此不能直接從一般輸出腳位產生真正連續的 2V、3V 或 4V 類比電壓。

analogWrite() 的名稱容易讓初學者誤會。它實際輸出的是 PWM 訊號,也就是 HIGH 與 LOW 的高速切換,而不是 DAC 類比電壓。

analogRead() 與 analogWrite() 的工作方向不同。analogRead() 使用 ADC(Analog to Digital Converter)把輸入電壓轉成數位值;analogWrite() 則以 PWM 方式控制輸出的平均效果。

PWM 的工作原理與 Duty Cycle

Duty Cycle(占空比)表示在一個 PWM 週期內,HIGH 所占的時間百分比。

占空比為 0% 時,輸出會持續維持 LOW;占空比為 100% 時,輸出會持續維持 HIGH。介於兩者之間時,腳位會快速切換 HIGH 與 LOW。

LED 因為人眼的視覺暫留而呈現不同亮度,通常不會直接看到高速閃爍。對馬達等負載而言,PWM 則會改變其接收到的平均功率。

Duty Cycle 與輸出效果
Duty Cycle 輸出狀態 LED 約略效果
0% 永遠 LOW 熄滅
25% HIGH 時間較短 偏暗
50% HIGH 與 LOW 時間約相等 中等亮度
75% HIGH 時間較長 很亮
100% 永遠 HIGH 最亮
0%    ____________________
25%   ████____████____
50%   ████████________
75%   ████████████____
100%  ████████████████

Arduino Uno 哪些腳位支援 PWM?

Arduino Uno R3 提供 6 個 PWM 輸出腳位,分別是 D3、D5、D6、D9、D10 與 D11。

開發板上的 PWM 腳位旁通常會印有波浪符號「~」。進行接線前,應先確認所使用的腳位是否具備 PWM 功能。

在不支援 PWM 的一般數位腳位上呼叫 analogWrite(),無法得到預期的連續占空比調整效果。

  • D3
  • D5
  • D6
  • D9
  • D10
  • D11

analogWrite() 基本語法

analogWrite(pin, value);

pin 是支援 PWM 的輸出腳位,value 是 0~255 的整數。

例如 analogWrite(9, 128) 表示在 D9 輸出約 50% 占空比的 PWM 訊號。

analogWrite(9, 128);

PWM 數值範圍為什麼是 0~255?

Arduino Uno 的 analogWrite() 使用 8 位元 PWM 數值。8 位元可以表示 2 的 8 次方,也就是 256 個不同數值。

由於數值從 0 開始,因此可用範圍為 0~255。數值越大,HIGH 所占的時間通常越長,LED 也會看起來越亮。

analogWrite() 數值與 LED 約略效果
PWM 數值 約略占空比 LED 約略效果
0 0% 熄滅
64 約 25% 偏暗
128 約 50% 半亮
192 約 75% 很亮
255 100% 全亮

實驗一:使用 PWM 控制 LED 亮度

準備材料

LED 調光實驗材料
元件 數量
Arduino Uno R3 1
LED 1
220Ω 電阻 1
Breadboard 1
杜邦線 若干

LED 接線方式

將 D9 經過 220Ω 限流電阻連接至 LED 正極,LED 負極再接到 GND。

D9 必須是支援 PWM 的腳位。LED 一定要串接適當的限流電阻,避免 LED 或 Arduino 腳位因電流過大而損壞。

D9
│
220Ω 電阻
│
LED
│
GND

固定亮度程式

const int LED_PIN = 9;

void setup()
{
}

void loop()
{
  analogWrite(LED_PIN, 50);
}

程式會持續在 D9 輸出 PWM 值 50,LED 預期會呈現偏暗的亮度。

analogWrite() 設定後,PWM 會由硬體計時器持續輸出,因此不需要在 loop() 內不斷改變數值,除非程式需要動態調光。

測試不同亮度

analogWrite(LED_PIN, 0);    // 熄滅
analogWrite(LED_PIN, 128);  // 約半亮
analogWrite(LED_PIN, 255);  // 最亮

可逐一測試 0、64、128、192 與 255,觀察 LED 亮度的變化。

人眼對亮度的感受並不是完全線性,因此 PWM 數值增加一倍時,主觀亮度不一定也正好增加一倍。

實驗二:製作 LED 呼吸燈

呼吸燈會讓 LED 從暗逐漸變亮,再從亮逐漸變暗。做法是使用 for 迴圈依序調整 analogWrite() 的數值。

delay(5) 用來控制每次亮度變化之間的等待時間。數值越大,呼吸速度越慢;數值越小,變化速度越快。

const int LED_PIN = 9;

void setup()
{
}

void loop()
{
  for (int i = 0; i <= 255; i++)
  {
    analogWrite(LED_PIN, i);
    delay(5);
  }

  for (int i = 255; i >= 0; i--)
  {
    analogWrite(LED_PIN, i);
    delay(5);
  }
}

PWM 可以控制哪些元件?

  • LED 調光。
  • RGB LED 顏色混合。
  • 直流馬達速度控制,但必須搭配 MOSFET、H-Bridge 或 Motor Driver。
  • 部分蜂鳴器音效;需要指定音調時,通常使用 tone() 較合適。
  • 加熱器或其他功率負載的平均功率控制,但必須搭配符合電流與電壓需求的驅動電路。

Arduino 腳位能提供的電流有限。PWM 只應作為控制訊號,不可直接驅動大電流馬達、加熱器或其他高功率負載。

控制感性負載時,還應考慮反向電動勢與保護元件。例如直流馬達通常需要續流二極體或由馬達驅動模組提供相應保護。

PWM 能直接控制 Servo 嗎?

不建議使用 analogWrite() 直接控制 Servo。

Servo 需要固定週期與特定脈衝寬度的控制訊號,其控制方式與一般 analogWrite() 的 PWM 調光不同。Arduino 程式通常使用 Servo 函式庫與 servo.write() 進行控制。

#include <Servo.h>

Servo servo;

void setup()
{
  servo.attach(9);
  servo.write(90);
}

void loop()
{
}

常見錯誤與故障排除

analogWrite() 沒有產生預期的調光效果

先確認接線使用的是 D3、D5、D6、D9、D10 或 D11。若使用 D8 等不支援 PWM 的腳位,就無法得到預期的占空比調整效果。

也應確認 LED 極性、GND 是否共地、限流電阻是否正確串接,以及程式指定的腳位是否與實際接線一致。

量測到的電壓不像穩定的類比電壓

這是正常現象。analogWrite() 輸出的是 HIGH 與 LOW 的 PWM 方波,不是 DAC 產生的連續電壓。

一般萬用電表可能顯示近似平均值,但示波器會清楚顯示快速切換的脈衝波形。需要真正平滑的類比電壓時,應使用具備 DAC 的控制器,或搭配低通濾波與適當的緩衝電路。

LED 沒有串接限流電阻

LED 不可直接接在 Arduino 輸出腳位與 GND 之間。即使使用 PWM,LED 仍需要適當的限流電阻。

本章示範使用 220Ω 電阻。實際阻值應依供電電壓、LED 順向壓降與目標電流計算。

馬達轉不動或 Arduino 重啟

不要直接從 Arduino I/O 腳位驅動直流馬達。馬達啟動電流可能遠高於腳位可承受範圍,造成電壓下降、系統重啟或硬體損壞。

應使用 MOSFET、H-Bridge 或 Motor Driver,並依負載需求提供獨立電源。Arduino 與外部驅動電路通常需要共地。

本章實作練習

練習一:設定五種 LED 亮度

使用 analogWrite() 依序輸出五個不同數值,例如 0、64、128、192 與 255,觀察 LED 亮度變化。

練習二:調整呼吸燈速度

修改呼吸燈程式中的 delay() 數值,分別測試快速、一般與緩慢的呼吸效果,並記錄不同設定的差異。

練習三:使用可變電阻控制 LED

使用上一章的可變電阻與 analogRead() 讀取 0~1023 的 ADC 數值,再使用 map() 轉換成 0~255,最後交給 analogWrite() 控制 LED 亮度。

const int POT_PIN = A0;
const int LED_PIN = 9;

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

void loop()
{
  int adcValue = analogRead(POT_PIN);
  int pwmValue = map(adcValue, 0, 1023, 0, 255);

  analogWrite(LED_PIN, pwmValue);

  Serial.print("ADC: ");
  Serial.print(adcValue);
  Serial.print("  PWM: ");
  Serial.println(pwmValue);

  delay(20);
}

Challenge:加入平滑效果

在旋鈕調光器中加入多次取樣平均,並在 Serial Monitor 同時顯示 ADC 值與 PWM 值。

比較加入平均前後的 LED 穩定度,觀察可變電阻輸入雜訊是否造成亮度抖動。

本章重點整理

  • PWM 並非真正的類比輸出,而是透過高速切換 HIGH 與 LOW 產生平均效果。
  • Duty Cycle 表示一個 PWM 週期內 HIGH 所占的時間比例。
  • Arduino Uno 的 analogWrite() 使用 0~255 的 8 位元數值。
  • Arduino Uno 的 PWM 腳位為 D3、D5、D6、D9、D10 與 D11。
  • PWM 可用於 LED 調光與馬達調速,但高功率負載必須搭配適當驅動電路。
  • Servo 的控制訊號不同於一般 analogWrite(),應使用 Servo 函式庫。

下一章預告

第 11 章將介紹 UART(TTL Serial)與 Serial 通訊,包括 TX 與 RX 接線、Serial.begin()、Serial.print()、Serial.println(),以及 TTL UART 與傳統 RS-232 的差異。

相關資源與文章匯出
下載 Arduino 入門基礎教材 下載 DOCX PDF/列印

← 回文章列表