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() 等更適合的方式。
| 項目 | 說明 |
|---|---|
| 全名 | 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 | 輸出狀態 | 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 也會看起來越亮。
| PWM 數值 | 約略占空比 | LED 約略效果 |
|---|---|---|
| 0 | 0% | 熄滅 |
| 64 | 約 25% | 偏暗 |
| 128 | 約 50% | 半亮 |
| 192 | 約 75% | 很亮 |
| 255 | 100% | 全亮 |
實驗一:使用 PWM 控制 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 的差異。