前面第 8、9 章,我們已經掌握:
GPIO Output
+
GPIO Input
Raspberry Pi 已經可以:
Button
↓
GPIO Input
↓
Python
↓
GPIO Output
↓
LED
但是到目前為止,LED 仍然只有:
ON
OFF
兩種主要狀態。
也就是:
0
1
或:
LOW
HIGH
如果我們希望 LED:
10% 亮度
30% 亮度
50% 亮度
80% 亮度
一般 Digital Output 本身並沒有直接產生「1.65V 半亮」這種真正連續類比輸出的功能。
這時就可以使用:
PWM
也就是:
Pulse Width Modulation
脈衝寬度調變。
Raspberry Pi 官方目前列出所有 GPIO 都可以進行 Software PWM,而 Hardware PWM 則可在 GPIO12、GPIO13、GPIO18、GPIO19 使用。
本章將從 PWM 原理一路實作到:
LED Dimming
Breathing LED
Buzzer
Tone
Button Brightness Control
1. PWM 是什麼?
PWM 全名:
Pulse Width Modulation
中文通常翻譯:
脈衝寬度調變
它並不是讓 GPIO 真正輸出任意連續電壓。
而是讓 GPIO:
HIGH
LOW
HIGH
LOW
HIGH
LOW
快速切換。
透過改變:
HIGH 維持的時間比例
來改變負載看到的平均效果。
2. Digital Output 原本只有 HIGH / LOW
例如:
led.on()
GPIO 是:
HIGH
而:
led.off()
GPIO 是:
LOW
所以一般 Digital LED:
LOW
=
OFF
HIGH
=
ON
3. PWM 則是在 HIGH / LOW 之間快速切換
例如:
HIGH ── LOW ── HIGH ── LOW
如果切換速度足夠快,人眼就不容易直接看出每一次 ON / OFF。
而會感覺:
LED 比較亮
或:
LED 比較暗
因此 PWM 很適合控制 LED 的平均亮度。
4. PWM 有三個重要概念
本章要先掌握:
Frequency
Period
Duty Cycle
三個名詞。
5. Period 是什麼?
Period:
週期
表示一個完整 PWM:
HIGH + LOW
所需要的時間。
例如:
HIGH 5 ms
LOW 5 ms
則一個完整週期:
10 ms
6. Frequency 是什麼?
Frequency:
頻率
表示:
一秒鐘完成多少個 PWM 週期。
單位:
Hz
例如:
100 Hz
代表:
每秒 100 個週期
Frequency 和 Period 的基本關係:
Frequency = 1 / Period
如果:
Period = 0.01 sec
則:
Frequency = 100 Hz
GPIO Zero 的 PWMLED 預設 PWM Frequency 為 100Hz。
7. Duty Cycle 是什麼?
Duty Cycle:
占空比
表示一個完整週期中:
HIGH
所佔的比例。
公式可以理解為:
Duty Cycle
=
HIGH Time / Total Period × 100%
8. 0% Duty Cycle
如果:
HIGH Time = 0
那麼:
Duty Cycle = 0%
訊號:
LOW LOW LOW LOW LOW
對 LED:
OFF
9. 25% Duty Cycle
例如:
HIGH HIGH LOW LOW LOW LOW LOW LOW
大約四分之一時間為 HIGH。
可以理解:
Duty Cycle = 25%
LED 看起來會比較暗。
10. 50% Duty Cycle
HIGH HIGH LOW LOW
HIGH HIGH LOW LOW
一半時間 HIGH。
一半時間 LOW。
所以:
Duty Cycle = 50%
11. 75% Duty Cycle
大部分時間 HIGH:
HIGH HIGH HIGH LOW
HIGH HIGH HIGH LOW
因此:
Duty Cycle = 75%
LED 看起來比 50% 更亮。
12. 100% Duty Cycle
如果一直 HIGH:
HIGH HIGH HIGH HIGH
則:
Duty Cycle = 100%
效果接近一般:
led.on()
13. PWM 不代表真正輸出一半電壓
這個觀念很重要。
例如:
Duty Cycle = 50%
並不代表 GPIO 真的固定輸出:
1.65V
而是:
3.3V
0V
3.3V
0V
快速切換。
負載因為電氣與物理特性而呈現平均效果。
Raspberry Pi 一般 GPIO 是 3.3V 邏輯,Output HIGH 屬於約 3.3V 的訊號等級。
14. PWM 可以拿來做什麼?
常見用途:
LED Brightness
RGB LED
Buzzer Tone
Motor Driver Speed
Fan Control Signal
Servo Control Signal
Power Control Driver
但是不同負載需要不同 Driver。
例如:
GPIO PWM
可以直接控制小電流 LED。
但不能因此推論:
GPIO PWM
↓
DC Motor
可以直接連接。
15. 本章 LED 使用哪個 GPIO?
本章主要使用:
BCM GPIO18
=
Physical Pin 12
Raspberry Pi 官方列出 GPIO18 同時也是可提供 Hardware PWM Alternative Function 的 GPIO 之一。
不過要特別注意:
使用 PWMLED(18) 不等於保證正在使用 Raspberry Pi Hardware PWM Peripheral。
GPIO Zero 的 PWMLED 是高階 PWM Output Device,實際底層產生 PWM 的方式取決於所使用的 Pin Factory。GPIO Zero 的 PWM Device 本身提供 Duty Cycle 與 Frequency API;Raspberry Pi 官方所稱的 Hardware PWM 則是 GPIO 的特定 Alternate Function。
本章主要學習:
PWM 應用概念
而不是直接操作 RP1 Hardware PWM Register。
16. Raspberry Pi 5 Hardware PWM Pin
Raspberry Pi 官方目前列出:
GPIO12
GPIO13
GPIO18
GPIO19
可以提供 Hardware PWM Alternative Function。
對應常見 Physical Pin:
| BCM GPIO | Physical Pin |
|---|---|
| GPIO12 | Pin 32 |
| GPIO13 | Pin 33 |
| GPIO18 | Pin 12 |
| GPIO19 | Pin 35 |
17. Raspberry Pi 5 PWM 由誰提供?
Raspberry Pi 5 的大部分外部 I/O 由:
RP1
I/O Controller 管理。
RP1 官方文件列出的低速 Peripheral 包含:
- SPI
- UART
- I²C
- PWM
- GPIO
- I²S
RP1 透過 PCIe 2.0 x4 與 Raspberry Pi 5 的 BCM2712 主處理器連接。
18. Software PWM 與 Hardware PWM 差在哪?
可以先用簡化方式理解。
Software PWM
由軟體與 GPIO Driver 產生切換時序。
優點:
彈性高
多個 GPIO 可使用
簡單 LED 調光很方便
Raspberry Pi 官方目前說明 Software PWM 可用於所有 GPIO。
Hardware PWM
由專門的 PWM Hardware Peripheral 產生訊號。
適合對:
Timing
Frequency Stability
CPU Load
要求較高的用途。
Raspberry Pi 官方列出的 Hardware PWM GPIO 為 GPIO12、13、18、19。
19. GPIO Zero PWMLED 應該怎麼理解?
本章使用:
from gpiozero import PWMLED
PWMLED 是 GPIO Zero 提供的可變亮度 LED Device,繼承自 PWMOutputDevice。它使用 0.0~1.0 的 value 表示 Duty Cycle,預設 PWM Frequency 為 100Hz。
因此:
led.value = 0.5
代表:
50% Duty Cycle
而不是直接指定:
1.65V
20. 本章材料
LED PWM 實驗:
- Raspberry Pi 5
- LED × 1
- 330Ω Resistor × 1
- Breadboard
- Female-to-Male Jumper Wire
按鍵亮度控制:
- Push Button × 2
蜂鳴器實驗:
- Active Buzzer 或合適 Buzzer Module
- Passive / Tonal Buzzer
- 必要時 Transistor Driver
- Resistor
- 外部 Power Supply(視 Buzzer 規格)
21. LED 接線
使用:
GPIO18
Physical Pin 12
接:
GPIO18
│
▼
330Ω
│
▼
LED Anode
LED Cathode
│
▼
GND
Physical Pin 14
GPIO Zero PWMLED 官方文件使用同樣的基本概念:GPIO 接 LED、LED 另一端接 Ground,並使用限流電阻避免 LED 過流。
22. 接線前先關機
執行:
sudo poweroff
等系統停止後:
拔除 USB-C
↓
接 GPIO18
↓
接 330Ω
↓
接 LED
↓
接 GND
↓
重新檢查
↓
再通電
尤其不要把 Header 的:
5V
誤接到一般 GPIO Signal。
Raspberry Pi 官方說明一般 GPIO 是 3.3V General-purpose Pin,而 Header 的 5V Pin 是獨立 Power Rail。
23. 建立第 10 章 Project
cd ~
建立:
mkdir -p raspberrypi-course/chapter10
進入:
cd raspberrypi-course/chapter10
確認:
pwd
24. 第一支 PWMLED 程式
建立:
nano pwm_led.py
內容:
from gpiozero import PWMLED
from time import sleep
led = PWMLED(18)
led.value = 0.25
sleep(2)
led.value = 0.50
sleep(2)
led.value = 0.75
sleep(2)
led.value = 1.00
sleep(2)
led.value = 0
led.close()
執行:
python3 pwm_led.py
25. led.value 的範圍
GPIO Zero PWMLED.value 可以設定:
0.0
~
1.0
其中:
0.0
=
0% Duty Cycle
=
Off
1.0
=
100% Duty Cycle
=
Fully On
中間值可用來產生不同程度的輸出。
26. 25% 亮度
led.value = 0.25
代表:
Duty Cycle
=
25%
LED 平均亮度較低。
27. 50% 亮度
led.value = 0.5
表示:
Duty Cycle
=
50%
28. 75% 亮度
led.value = 0.75
表示:
Duty Cycle
=
75%
29. 100%
led.value = 1
就是:
Fully On
GPIO Zero PWM Device 的 value=1.0 代表完全開啟。
30. 漸亮 LED
現在不要直接:
25%
50%
75%
100%
而是每次增加:
1%
建立:
nano fade_in.py
內容:
from gpiozero import PWMLED
from time import sleep
led = PWMLED(18)
try:
for step in range(101):
led.value = step / 100
sleep(0.02)
sleep(1)
finally:
led.off()
led.close()
31. step / 100 是什麼?
range(101):
0
1
2
...
100
因此:
step / 100
會產生:
0.00
0.01
0.02
...
1.00
剛好符合:
PWMLED.value
的有效範圍。
32. 漸暗 LED
反向:
for step in range(100, -1, -1):
led.value = step / 100
sleep(0.02)
流程:
1.00
0.99
0.98
...
0.00
因此 LED 逐漸變暗。
33. 完整 Fade In / Fade Out
from gpiozero import PWMLED
from time import sleep
led = PWMLED(18)
try:
while True:
for step in range(101):
led.value = step / 100
sleep(0.01)
for step in range(100, -1, -1):
led.value = step / 100
sleep(0.01)
except KeyboardInterrupt:
print("\nProgram stopped.")
finally:
led.off()
led.close()
視覺效果:
暗
↓
慢慢變亮
↓
最亮
↓
慢慢變暗
↓
暗
↓
Repeat
這就是:
Breathing LED
呼吸燈。
34. GPIO Zero 已經有 pulse()
GPIO Zero 的 PWM LED Device 已提供:
pulse()
可以自動:
Fade In
+
Fade Out
官方 pulse() 預設 Fade In 1 秒、Fade Out 1 秒,n=None 則持續執行。
35. 最簡單呼吸燈
from gpiozero import PWMLED
from signal import pause
led = PWMLED(18)
led.pulse()
pause()
GPIO Zero 會在 Background 執行 PWM Fade,而 pause() 讓主程式繼續保持運作。
36. 自訂呼吸速度
例如:
from gpiozero import PWMLED
from signal import pause
led = PWMLED(18)
led.pulse(
fade_in_time=2,
fade_out_time=2
)
pause()
效果:
2 秒漸亮
+
2 秒漸暗
pulse() 的 fade_in_time 與 fade_out_time 都以秒為單位。
37. 只呼吸五次
from gpiozero import PWMLED
led = PWMLED(18)
led.pulse(
fade_in_time=1,
fade_out_time=1,
n=5,
background=False
)
led.close()
GPIO Zero pulse() 可以指定 n 次數;background=False 會等 Pulse 完成才返回。
38. PWM Frequency 可以設定嗎?
可以。
PWMLED 建構參數包含:
frequency=
預設是:
100 Hz
例如:
led = PWMLED(
18,
frequency=200
)
表示要求 PWM Device 使用:
200 Hz
的 PWM Frequency。
39. Frequency 越高 LED 越亮嗎?
不一定。
LED 平均亮度的主要控制量是:
Duty Cycle
不是單純 Frequency 越高越亮。
例如:
100Hz / 50%
和:
500Hz / 50%
Duty Cycle 都是 50%。
它們的切換速度不同,但不是把 500Hz 簡單理解成亮度是 100Hz 的五倍。
40. Frequency 太低會怎樣?
如果 PWM Frequency 太低,人眼可能開始看到:
Flicker
而不是平順的亮度。
因此 LED Dimming 一般會使用足夠高的 Frequency。
GPIO Zero 的 PWMLED 預設值為 100Hz。
41. Duty Cycle 和 Frequency 不要混在一起
Frequency:
一秒有幾個週期
Duty Cycle:
每個週期中 HIGH 占多少比例
所以它們是兩個不同參數。
42. PWMLED toggle() 也和普通 LED 不完全一樣
假設:
led.value = 0.2
再:
led.toggle()
GPIO Zero 對 PWM Device 會將:
0.2
反轉成:
0.8
而不只是簡單 0 ↔ 1。官方 toggle() 對 PWM Output Device 的定義就是將 Duty Cycle 反轉。
43. 按鍵控制 LED 亮度
現在使用第 9 章的:
Button +
Button -
控制:
Brightness Up
Brightness Down
Pin:
LED:
GPIO18 / Pin 12
Brightness +:
GPIO20 / Pin 38
Brightness -:
GPIO21 / Pin 40
Button 使用 Internal Pull-up,另一端接 GND。
44. 亮度調整範圍
定義:
MIN_BRIGHTNESS = 0.0
MAX_BRIGHTNESS = 1.0
STEP = 0.1
每按一次:
+10%
或:
-10%
45. 完整按鍵調光程式
from gpiozero import PWMLED, Button
from signal import pause
LED_PIN = 18
UP_BUTTON_PIN = 20
DOWN_BUTTON_PIN = 21
STEP = 0.1
led = PWMLED(LED_PIN)
button_up = Button(
UP_BUTTON_PIN,
bounce_time=0.05
)
button_down = Button(
DOWN_BUTTON_PIN,
bounce_time=0.05
)
def brightness_up():
led.value = min(
1.0,
led.value + STEP
)
print(
f"Brightness: "
f"{led.value * 100:.0f}%"
)
def brightness_down():
led.value = max(
0.0,
led.value - STEP
)
print(
f"Brightness: "
f"{led.value * 100:.0f}%"
)
button_up.when_pressed = (
brightness_up
)
button_down.when_pressed = (
brightness_down
)
print("Button + : brighter")
print("Button - : darker")
try:
pause()
except KeyboardInterrupt:
print("\nProgram stopped.")
finally:
led.off()
button_up.close()
button_down.close()
led.close()
46. 這個專案整合了前三章內容
現在架構:
Button +
│
▼
GPIO Input
│
▼
Python
│
▼
PWM Duty Cycle
│
▼
LED Brightness
以及:
Button -
│
▼
Brightness Down
因此已經整合:
GPIO Input
+
PWM Output
+
Event Handler
+
Debounce
47. 下一階段:蜂鳴器
LED 是視覺 Output。
接著加入:
Buzzer
蜂鳴器。
它可以讓 Raspberry Pi 提供:
Beep
Alarm
Warning
Tone
Melody
等聲音輸出。
48. Active Buzzer 與 Passive Buzzer 不一樣
購買蜂鳴器時一定要先知道:
Active Buzzer
或:
Passive Buzzer
兩者控制方式不同。
49. Active Buzzer 是什麼?
Active Buzzer 內部通常已具有振盪電路。
提供合適 DC 電源/控制訊號後,就會:
BEEP
因此它最適合:
ON
OFF
控制。
GPIO Zero 的 Buzzer 類別就是 Digital Buzzer Device,只提供簡單 On/Off,而不能用來播放多種 Tone。
50. Passive Buzzer 是什麼?
Passive Buzzer 本身沒有固定振盪器。
需要外部產生:
Frequency
驅動。
例如:
440 Hz
523 Hz
659 Hz
就能形成不同音高。
這類元件適合搭配:
PWM / Tonal Output
51. 如何辨認 Active / Passive Buzzer?
不要只靠:
- 黑色外觀
- 貼紙
- 腳長
判斷。
最可靠的是:
Part Number
+
Datasheet
+
Module Specification
因為外觀非常相似。
52. Buzzer 可以直接接 GPIO 嗎?
GPIO Zero 官方的簡單 Buzzer API 範例是 Buzzer 正極接 GPIO、負極接 GND。
但是實際硬體設計仍必須確認:
Operating Voltage
Operating Current
Module Input Circuit
如果蜂鳴器需要的電流超出 GPIO 適合直接驅動的範圍,或需要 5V / 更高電壓:
應使用 Transistor / MOSFET Driver。
不要只因為元件名稱是「Buzzer」就認為一定能直接接 GPIO。
53. 小型合適 Active Buzzer 接線概念
如果經規格確認可以使用 Raspberry Pi 3.3V GPIO 直接控制:
GPIO23
Physical Pin 16
│
▼
Active Buzzer +
Active Buzzer -
│
▼
GND
GPIO Zero Buzzer 官方示例就是這種 Digital Buzzer 架構。
54. Active Buzzer 程式
from gpiozero import Buzzer
from time import sleep
buzzer = Buzzer(23)
buzzer.on()
sleep(1)
buzzer.off()
buzzer.close()
GPIO Zero Buzzer.on() 開啟蜂鳴器,off() 關閉;該類別只提供 Digital On/Off。
55. Beep 五次
GPIO Zero Buzzer 還提供:
beep()
例如:
from gpiozero import Buzzer
buzzer = Buzzer(23)
buzzer.beep(
on_time=0.2,
off_time=0.2,
n=5,
background=False
)
buzzer.close()
官方 beep() 可以指定 On Time、Off Time、次數與 Background 模式。
56. Active Buzzer 為什麼不適合直接做音階?
GPIO Zero 官方文件特別說明:
Buzzer 這個 Digital Interface 只能:
ON
OFF
不能用它播放多種 Tone;要改用:
TonalBuzzer
57. TonalBuzzer
GPIO Zero 提供:
TonalBuzzer
用來產生不同音調。
官方 TonalBuzzer 可以接受:
- Tone Object
- Frequency
- MIDI Note
- Note Name
等形式播放音高。
58. 第一個 TonalBuzzer 程式
假設合適的 Passive / Tonal Buzzer 接:
GPIO23
程式:
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
buzzer.play("A4")
sleep(1)
buzzer.stop()
buzzer.close()
GPIO Zero 官方範例直接支援:
b.play("A4")
形式。
59. A4 是多少 Hz?
GPIO Zero Tone 文件指出:
A4
=
440 Hz
=
MIDI Note 69
因此以下幾種寫法都可以表示相同的 Concert A:
Tone(440.0)
Tone(69)
Tone("A4")
60. 使用 Frequency 播放
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
buzzer.play(440.0)
sleep(1)
buzzer.stop()
buzzer.close()
GPIO Zero 官方 TonalBuzzer.play() 可以接受 Frequency;Tone 內部也使用 Hz 表示音調。
61. Tone.from_frequency()
如果不想讓 Tone Constructor 猜測數值是 Frequency 還是 MIDI Note,可以明確:
from gpiozero.tones import Tone
tone = Tone.from_frequency(440)
GPIO Zero 官方提供 from_frequency()、from_midi()、from_note() 來避免低頻數值範圍的格式歧義。
62. 播放不同音符
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
notes = [
"C4",
"D4",
"E4",
"F4",
"G4",
"A4",
"B4",
"C5"
]
for note in notes:
buzzer.play(note)
sleep(0.3)
buzzer.stop()
sleep(0.05)
buzzer.close()
GPIO Zero Tone 支援 A~G Note Name、Sharp/Flat 以及 Octave Number。
63. 簡單警報聲
例如在兩個 Tone 之間切換:
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
try:
while True:
buzzer.play("A4")
sleep(0.3)
buzzer.play("A5")
sleep(0.3)
except KeyboardInterrupt:
print("\nAlarm stopped.")
finally:
buzzer.stop()
buzzer.close()
這就可以形成:
低音
高音
低音
高音
的警報效果。
64. 模擬溫度警報
第 6 章學過:
Temperature
↓
Normal
Warning
Danger
現在可以加入 Buzzer。
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
def alarm(temperature):
if temperature >= 35:
buzzer.play("A5")
sleep(0.5)
buzzer.stop()
elif temperature >= 30:
buzzer.play("A4")
sleep(0.2)
buzzer.stop()
else:
buzzer.stop()
try:
while True:
temperature = float(
input(
"Temperature: "
)
)
alarm(temperature)
except KeyboardInterrupt:
print("\nProgram stopped.")
finally:
buzzer.stop()
buzzer.close()
現在 Output 不只是:
文字
而變成:
聲音警報
65. 下一步可以把 Keyboard 換成 Sensor
目前:
Temperature
=
Keyboard Input
後面第 11 章會換成:
DHT11 / DHT22
所以架構變成:
DHT Sensor
↓
Temperature
↓
Python
↓
if / elif
↓
Buzzer
這已經非常接近真正環境警報器。
66. PWM 與 TonalBuzzer 的 Frequency 概念
LED PWM 通常關心:
Duty Cycle
控制亮度。
Tonal Buzzer 則更重視:
Frequency
因為 Frequency 直接對應聽到的 Pitch。
也就是:
LED
→ Duty Cycle
Buzzer
→ Frequency
雖然兩者底層都可能利用週期訊號。
67. Frequency 高代表音調高
例如:
220 Hz
比:
440 Hz
音調低。
而:
880 Hz
又比 440Hz 高。
GPIO Zero Tone 支援正 Frequency 並提供 Note / MIDI / Hz 之間的表示方式。
68. TonalBuzzer 可以無限播放任何 Frequency 嗎?
不是。
Tone 類別允許 Frequency:
0 < freq <= 20000
但實際 TonalBuzzer 仍有由 mid_tone 與 octaves 定義的可用音域。
而真正 Buzzer 本身也有:
- Resonant Frequency
- Effective Frequency Range
- Sound Pressure
等物理限制。
所以程式接受某個值,不代表每個 Buzzer 都能同樣清楚地發出該聲音。
69. GPIO PWM 不能直接控制大功率 Fan
如果你要控制:
5V Fan
12V Fan
DC Motor
Pump
不要:
GPIO
↓
Fan Power
直接串接。
GPIO 應該控制:
Transistor
MOSFET
Motor Driver
Fan PWM Control Input
而實際負載能量由外部電源提供。
GPIO Zero 的 Motor 類別本身也是以 H-bridge Motor Driver 為控制架構,而不是 GPIO 直接接馬達。
70. PWM 控制馬達的正確概念
Raspberry Pi
GPIO PWM
│
▼
Motor Driver
│
├── External Power
│
▼
Motor
GPIO 提供:
Control Signal
Motor Driver 負責:
Current
Voltage
Switching
Protection
71. MOSFET PWM 架構
例如控制單方向 DC Load:
Raspberry Pi GPIO
│
▼
Gate Resistor
│
▼
Logic-level MOSFET
│
▼
Load
│
▼
External Supply
如果是 Inductive Load:
Motor
Relay
Solenoid
還要考慮:
Flyback Protection
等電路。
72. 不要用 PWM 直接「降低 GPIO 電壓」去供電
例如有人可能想:
GPIO = 50% PWM
就拿它當:
1.65V Power Supply
這是不正確的設計思維。
PWM 是:
切換訊號
不是一般穩壓電源。
如果真的需要 Analog Voltage:
DAC
PWM + Filter
Voltage Regulator
要根據需求設計。
73. Servo 也會使用 Pulse Control,但概念稍有不同
Servo 控制也使用週期性 Pulse。
但是 Servo 關注的是:
Pulse Width
代表位置。
它並不等同於本章 LED Dimming 中:
任意 Duty Cycle = Brightness
後面第 15 章控制 Servo 時會重新建立正確 Servo Timing 概念。
74. PWMLED 是否必須接 Hardware PWM Pin?
不一定。
Raspberry Pi 官方列出:
Software PWM
=
All GPIO
因此高階 Library 可以在更多 GPIO 上提供一般 PWM 用途。
本章使用 GPIO18,是因為:
- 容易辨識
- 也是官方 Hardware PWM-capable GPIO
- 方便之後比較 Software / Hardware PWM
但不是因為 PWMLED 只能用 GPIO18。
75. Hardware PWM 適合什麼時候研究?
當 Project 開始要求:
Precise Frequency
Stable Duty Cycle
Low Jitter
Multiple Timing-sensitive Outputs
就需要進一步了解:
- RP1 PWM
- Linux PWM Subsystem
- Device Tree
- Hardware Channel
- Pin Alternate Function
而不是只停留在 GPIO Zero PWMLED。
本章目標是:
先掌握 PWM 應用與程式設計。
76. Pi 5 與舊 PWM 教學要注意
Raspberry Pi 5 的外部 I/O 底層主要由 RP1 管理,RP1 本身包含 PWM Peripheral。
因此涉及:
直接 Register 操作
/sys/class/pwm
Device Tree Overlay
Legacy SoC PWM Register
的舊 Pi 3 / Pi 4 教學,不應假設可以原封不動套到 Pi 5。
77. 呼吸燈不平順怎麼辦?
先確認:
- 使用 PWMLED,不是 LED。
- value 每次變化不要太大。
- Delay 不要太長。
- 程式沒有其他長時間 Blocking 工作。
- GPIO Zero Pin Factory 正常。
- 系統 Load 是否過高。
簡單呼吸燈其實優先可以直接使用:
led.pulse()
由 GPIO Zero 自己處理 Fade。pulse() 就是官方為 PWM Output 提供的 Fade In / Fade Out 方法。
78. LED 在低亮度時看起來變化不線性
例如:
10%
20%
30%
人眼感覺不一定會是:
等距增加
因為:
- 人眼亮度感受非線性
- LED 電氣特性也不是單純線性
- 環境光線會影響觀察
因此:
led.value = 0.5
代表 50% Duty Cycle,
不是保證人眼感覺為「剛好一半亮」。
79. LED 閃爍怎麼 Debug?
先建立:
from gpiozero import PWMLED
from time import sleep
led = PWMLED(18)
led.value = 0.5
sleep(10)
led.off()
led.close()
如果固定 50% Duty Cycle LED 仍明顯閃爍:
再檢查:
Frequency
Power
Wiring
Library
System Load
80. LED 完全不亮
檢查:
GPIO18
=
Physical Pin 12
以及:
330Ω
LED Anode
LED Cathode
GND
不要把:
GPIO18
誤解為:
Physical Pin 18
本系列所有 GPIO Zero 整數 Pin 都是 BCM Number。
81. PWMLED value 超出範圍
例如:
led.value = 50
這不是:
50%
GPIO Zero PWM Device 使用:
0.0 ~ 1.0
表示 Duty Cycle。
所以 50%:
led.value = 0.5
82. 把百分比轉成 value
如果使用者輸入:
75
可以:
percent = 75
led.value = percent / 100
得到:
0.75
83. 使用 input() 控制亮度
from gpiozero import PWMLED
led = PWMLED(18)
try:
while True:
percent = float(
input(
"Brightness 0-100: "
)
)
percent = max(
0,
min(100, percent)
)
led.value = (
percent / 100
)
except KeyboardInterrupt:
print("\nProgram stopped.")
finally:
led.off()
led.close()
84. Active Buzzer 沒聲音怎麼辦?
先檢查:
Active / Passive 類型
Voltage
Current
Polarity
GPIO Pin
GND
尤其不要假設:
Buzzer 不響
=
Python 錯
有些 Buzzer:
5V Active Buzzer
可能不適合直接以 3.3V GPIO 驅動。
85. Passive Buzzer 只有喀一聲
如果用:
Buzzer(23).on()
控制 Passive Buzzer,
可能不會產生你期待的持續音調。
因為 GPIO Zero 的 Buzzer 只是 Digital On/Off Interface;需要不同音調時官方要求使用 TonalBuzzer。
86. Active Buzzer 用 TonalBuzzer 不一定有效
Active Buzzer 內部已經自行產生固定 Tone。
即使外部改變 Frequency,實際聲音可能仍受到內建 Oscillator 主導。
所以:
Active Buzzer
→ Buzzer()
Passive / Tonal Buzzer
→ TonalBuzzer()
是比較容易理解的分類方式。
最終仍應依 Datasheet。
87. TonalBuzzer 播放前先確認元件規格
GPIO Zero TonalBuzzer 是軟體介面。
但你的硬體仍需要:
- 適當工作電壓
- 適當負載電流
- 適當 Frequency Range
如果 Current Requirement 不適合 GPIO 直接驅動:
GPIO
↓
Transistor Driver
↓
Buzzer
才是正確架構。
88. 程式結束時一定要 Silence Buzzer
對 LED:
led.off()
很重要。
對 Buzzer 更重要:
buzzer.stop()
不然程式異常退出時,警報硬體狀態可能不符合預期。
GPIO Zero TonalBuzzer.stop() 就是官方提供的停止 Tone 方法。
89. 建議使用 try / finally
例如:
from gpiozero import TonalBuzzer
from time import sleep
buzzer = TonalBuzzer(23)
try:
buzzer.play("A4")
sleep(5)
finally:
buzzer.stop()
buzzer.close()
這樣即使執行過程發生 Exception,也會執行 Cleanup。
90. LED + Buzzer 警報系統
可以整合:
Normal
→ LED 30%
→ No Sound
Warning
→ LED 70%
→ Short Beep
Danger
→ LED 100%
→ High Tone
這就是:
Input
↓
Decision
↓
Multiple Outputs
的控制架構。
91. 模擬警報完整版
from gpiozero import PWMLED
from gpiozero import TonalBuzzer
from time import sleep
led = PWMLED(18)
buzzer = TonalBuzzer(23)
def normal_state():
led.value = 0.3
buzzer.stop()
def warning_state():
led.value = 0.7
buzzer.play("A4")
sleep(0.2)
buzzer.stop()
def danger_state():
led.value = 1.0
buzzer.play("A5")
sleep(0.5)
buzzer.stop()
try:
while True:
temperature = float(
input(
"Temperature: "
)
)
if temperature >= 35:
danger_state()
elif temperature >= 30:
warning_state()
else:
normal_state()
except KeyboardInterrupt:
print("\nProgram stopped.")
finally:
led.off()
buzzer.stop()
led.close()
buzzer.close()
92. 這個系統已經很接近第 11 章
目前:
Keyboard
↓
Temperature
下一章改成:
DHT11 / DHT22
↓
Temperature + Humidity
其餘:
if
PWM LED
Buzzer
控制邏輯幾乎可以保留。
93. PWM 不應當成高功率控制介面
本章最重要的硬體安全觀念之一:
PWM
只是:
控制訊號技術。
PWM 並沒有讓 GPIO 自動變成:
High Current Output
因此:
LED
可以用小電流 GPIO Lab。
但是:
Fan
Motor
Pump
Solenoid
High-power LED
Heater
都要另外設計 Driver。
94. Raspberry Pi GPIO 仍然是 3.3V
不論:
Digital Output
或:
PWM Output
一般 GPIO 邏輯仍是 3.3V。
Raspberry Pi 官方目前明確說明一般 Header GPIO 是 3.3V General-purpose Pin。
所以:
PWM 不會讓 GPIO 變成 5V-tolerant。
95. 不要把外部 5V PWM Signal 直接送進 Pi
如果外部裝置輸出:
5V PWM
到 Raspberry Pi Input:
必須先確認電氣相容性。
一般 Pi GPIO Input 為 3.3V Logic,不能把 5V Signal 當成安全的 GPIO Input。
必要時使用:
Level Shifter
Voltage Divider
Interface Driver
視訊號形式決定。
96. PWM Output Debug 流程
1. GPIO Zero 可以 Import?
↓
2. PWMLED 可以建立?
↓
3. BCM GPIO 正確?
↓
4. Physical Pin 正確?
↓
5. LED / Buzzer 極性?
↓
6. GND 正確?
↓
7. 元件規格正確?
↓
8. Duty Cycle 正確?
↓
9. Frequency 合理?
↓
10. 是否需要 Driver?
97. PWMLED 常用 API
| API | 功能 |
|---|---|
| PWMLED(pin) | 建立 PWM LED |
| value | Duty Cycle 0.0~1.0 |
| on() | 100% ON |
| off() | OFF |
| toggle() | 反轉 Duty Cycle |
| blink() | 閃爍/可加入 Fade |
| pulse() | 漸亮漸暗 |
| frequency | PWM Frequency |
| close() | 釋放 GPIO |
GPIO Zero PWMLED 與底層 PWMOutputDevice 官方 API 提供上述功能,預設 Frequency 為 100Hz。
98. Buzzer 常用 API
| API | 功能 |
|---|---|
| Buzzer(pin) | Digital Buzzer |
| on() | Buzzer ON |
| off() | Buzzer OFF |
| beep() | 間歇 Beep |
| toggle() | 切換狀態 |
| close() | 釋放 Device |
GPIO Zero Buzzer 是 Digital Output Device,不支援多音調。
99. TonalBuzzer 常用 API
| API | 功能 |
|---|---|
| TonalBuzzer(pin) | 建立 Tonal Buzzer |
| play("A4") | 播放 Note |
| play(440.0) | 播放 Frequency |
| stop() | 停止 |
| tone | 目前 Tone |
| is_active | 是否正在播放 |
| close() | 釋放 Device |
GPIO Zero 官方 TonalBuzzer 支援 Note、Frequency 與 MIDI 格式。
100. 本章 PWM 快速整理
Frequency
=
每秒週期數
Period
=
單一完整週期時間
Duty Cycle
=
HIGH Time / Period
LED:
Duty Cycle
↓
Brightness
Passive Buzzer:
Frequency
↓
Pitch
Motor / Fan:
PWM
↓
Driver
↓
Power Load
101. 本章完整學習流程
Digital Output
↓
PWM
↓
Frequency
↓
Period
↓
Duty Cycle
↓
PWMLED
↓
25 / 50 / 75 / 100%
↓
Fade In
↓
Fade Out
↓
pulse()
↓
Breathing LED
↓
Button Brightness
↓
Active Buzzer
↓
Buzzer()
↓
Passive Buzzer
↓
TonalBuzzer
↓
Frequency / Tone
↓
Alarm System
102. 本章重點整理
完成本章後,應該掌握:
- PWM 是 Pulse Width Modulation。
- PWM 使用快速 HIGH / LOW 切換產生平均控制效果。
- Frequency 表示每秒 PWM Cycle 數。
- Period 表示一個 PWM Cycle 所需要的時間。
- Frequency 與 Period 互為倒數。
- Duty Cycle 是 HIGH Time 佔完整 Period 的比例。
- 0% Duty Cycle 代表完全關閉。
- 50% Duty Cycle 代表一半週期為 HIGH。
- 100% Duty Cycle 代表完全開啟。
- PWM 不等於真正固定類比輸出電壓。
- Raspberry Pi 一般 GPIO 使用 3.3V Logic。
- Raspberry Pi 官方列出所有 GPIO 可使用 Software PWM。
- Hardware PWM 可在 GPIO12、GPIO13、GPIO18、GPIO19 使用。
- Raspberry Pi 5 的 RP1 提供 PWM Peripheral。
- GPIO Zero PWMLED 可以控制 LED Variable Brightness。
- PWMLED.value 使用 0.0~1.0 表示 Duty Cycle。
- PWMLED 預設 Frequency 為 100Hz。
- led.value=0.25 表示 25% Duty Cycle。
- led.value=0.5 表示 50% Duty Cycle。
- led.value=1 表示 Fully On。
- for Loop 可以製作漸亮/漸暗。
- GPIO Zero pulse() 可以直接製作呼吸燈。
- pulse() 預設 Fade In / Out 各 1 秒。
- pulse(n=5) 可以限制呼吸次數。
- frequency 可以調整 PWM Frequency。
- Frequency 與 Duty Cycle 是不同控制參數。
- Frequency 高低不代表 LED 亮度等比例增減。
- PWMLED 可以配合 Button 改變 Brightness。
- GPIO Zero Button Event 可以和 PWM Output 整合。
- Active Buzzer 適合 Digital On/Off。
- GPIO Zero Buzzer 只能進行簡單 On/Off,不支援多 Tone。
- Buzzer.beep() 可以建立間歇警報。
- Passive / Tonal Buzzer 需要 Frequency 訊號。
- GPIO Zero TonalBuzzer 可播放不同 Tone。
- TonalBuzzer.play("A4") 可播放 A4。
- A4 為 440Hz、MIDI Note 69。
- Tone 可以使用 Hz、MIDI 或 Musical Note 表示。
- Tone.from_frequency() 可明確指定 Frequency。
- buzzer.stop() 可以停止 TonalBuzzer。
- 實際 Buzzer 必須確認 Operating Voltage 與 Current。
- 不同 Buzzer 的 Frequency Response 不相同。
- GPIO 不應直接驅動高電流負載。
- Motor 應透過 Motor Driver / H-bridge 控制。
- Fan / Motor / Pump 等通常需要 External Power。
- GPIO PWM 的角色是 Control Signal,不是 Power Supply。
- Inductive Load 應考慮 Driver 與保護電路。
- PWMLED(18) 不應直接等同於「正在使用 Pi Hardware PWM Peripheral」。
- GPIO Zero PWM 與 Raspberry Pi Hardware PWM Alternate Function 是兩個不同層次的概念。
- Pi 5 底層 I/O 架構使用 RP1,舊式低階 PWM 教學需重新確認相容性。
- 本章完成後已具備 GPIO Input、Digital Output 與 PWM Output 三種重要控制能力。
FAQ:Raspberry Pi PWM 常見問題
Q1:Raspberry Pi PWM 是什麼?
PWM 是 Pulse Width Modulation,透過快速切換 GPIO HIGH / LOW,並改變 HIGH 在每個週期所占比例,產生 LED 亮度、Tone 或 Driver 控制等效果。
Q2:Duty Cycle 是什麼?
Duty Cycle 是:
HIGH Time
÷
Total Period
×
100%
例如 50% 表示每個週期有一半時間 HIGH。
Q3:Frequency 是什麼?
表示每秒產生多少個完整週期。
例如:
100 Hz
代表:
100 cycles / second
GPIO Zero PWMLED 預設 Frequency 為 100Hz。
Q4:50% PWM 是不是 1.65V?
不是固定 1.65V Analog Output。
GPIO 仍在:
0V
與
約 3.3V
之間切換,只是 HIGH 約占一半時間。Raspberry Pi Header 一般 GPIO 為 3.3V Logic。
Q5:Raspberry Pi 哪些 Pin 有 Hardware PWM?
官方目前列出:
GPIO12
GPIO13
GPIO18
GPIO19
Q6:PWMLED 一定要用 GPIO18 嗎?
不一定。
Raspberry Pi 官方說明 Software PWM 可在所有 GPIO 使用。
本章選 GPIO18 是因為它也屬於官方 Hardware PWM-capable GPIO,方便理解兩個概念。
Q7:PWMLED(18) 就是 Hardware PWM 嗎?
不能這樣直接判定。
PWMLED 是 GPIO Zero 高階 PWM Device;Hardware PWM 是 Raspberry Pi GPIO Alternative Function 的硬體能力。實際 PWM 實現取決於所使用的 Library / Pin Factory / 系統方式。
Q8:如何設定 50% LED 亮度?
from gpiozero import PWMLED
led = PWMLED(18)
led.value = 0.5
value 介於 0.0~1.0。
Q9:如何做 Raspberry Pi 呼吸燈?
最簡單:
from gpiozero import PWMLED
from signal import pause
led = PWMLED(18)
led.pulse()
pause()
GPIO Zero pulse() 專門用來反覆 Fade In / Fade Out。
Q10:如何改呼吸燈速度?
例如:
led.pulse(
fade_in_time=2,
fade_out_time=2
)
代表兩秒漸亮、兩秒漸暗。
Q11:Active Buzzer 和 Passive Buzzer 有什麼差異?
Active Buzzer 通常有自己的振盪電路,適合:
ON / OFF
Passive Buzzer 則需要外部 Frequency Signal,才能形成不同 Tone。
Q12:GPIO Zero Active Buzzer 用什麼?
使用:
from gpiozero import Buzzer
Buzzer 是 Digital Buzzer,只支援簡單 On / Off。
Q13:如何讓 Active Buzzer 響?
from gpiozero import Buzzer
buzzer = Buzzer(23)
buzzer.on()
GPIO Zero 官方 Buzzer API 就是 Digital Output Device。
Q14:如何播放不同音調?
使用:
from gpiozero import TonalBuzzer
buzzer = TonalBuzzer(23)
buzzer.play("A4")
GPIO Zero TonalBuzzer 可以使用 Note、Frequency 或 MIDI 表示音高。
Q15:A4 是多少 Hz?
A4
=
440 Hz
也是:
MIDI Note 69
Q16:Buzzer 可以直接接 Raspberry Pi GPIO 嗎?
只有在元件規格確認:
- 3.3V Logic 合適
- Current 合理
- 電氣接法合適
時才考慮直接控制。
如果 Buzzer / Module 需要更高電流或 5V 等供電,應使用:
GPIO
↓
Transistor / MOSFET Driver
↓
Buzzer
Q17:PWM 可以直接控制馬達嗎?
PWM 可以作為:
Motor Speed Control Signal
但馬達本身不應由 GPIO 直接供電。
正確架構:
GPIO PWM
↓
Motor Driver
↓
External Power
↓
Motor
GPIO Zero 的 Motor Device 也以 Motor Driver / H-bridge 架構為基礎。
Q18:PWM 可以控制 Fan 嗎?
可以作為適當 Fan Driver 或 PWM Control Interface 的控制訊號。
但要依 Fan 類型:
- 2-wire
- 3-wire
- 4-wire
以及 Voltage / Current / PWM Specification 設計。
不要直接用 GPIO 當 Fan Power Supply。
Q19:為什麼 LED 低亮度不太線性?
因為:
- 人眼亮度感知非線性
- LED 光輸出特性
- PWM Frequency
- 環境亮度
都會影響實際視覺結果。
value=0.5 的確代表 50% Duty Cycle,但不保證人眼主觀感受剛好是 50% 亮度。
Q20:PWM GPIO 還是 3.3V 嗎?
是。
PWM 只是把:
HIGH
LOW
快速切換。
Raspberry Pi 一般 GPIO Logic 仍屬 3.3V。
結語
完成第 10 章之後,我們的 Raspberry Pi GPIO 能力已經從最初:
LED ON
LED OFF
進展到:
Digital Input
+
Digital Output
+
PWM Output
現在 Raspberry Pi 已經可以:
Button
│
▼
GPIO Input
│
▼
Python
│
├── Digital Output
│ ↓
│ LED
│
└── PWM Output
│
├── LED Brightness
└── Buzzer Tone
這表示我們已經具備三種非常重要的控制能力:
讀取、判斷、調變。
更重要的是,本章已經製作出:
PWMLED
↓
Breathing LED
以及:
Temperature
↓
Python Decision
↓
LED Brightness
+
Buzzer Alarm
這套架構距離真正的環境監測系統只差最後一個重要元素:
真正的 Sensor。
下一章我們不再使用:
Keyboard Input
假裝溫度。
而會真正接上:
DHT11 / DHT22 溫溼度感測器
讓 Raspberry Pi 自己讀取:
Temperature
+
Humidity
再把資料送入我們前面已經建立好的:
Python
if / else
LED
Buzzer
控制架構。
下一章
第 11 章 Raspberry Pi 感測器入門:DHT11 / DHT22 溫溼度監測
下一章將完整學習:
- Sensor 是什麼
- Analog Sensor
- Digital Sensor
- DHT11
- DHT22
- DHT11 與 DHT22 差異
- VCC
- DATA
- GND
- 3-pin Module 與 4-pin Sensor 差異
- Pull-up Resistor
- Raspberry Pi 3.3V Logic
- DHT Sensor 接線
- Python Library 選擇
- Raspberry Pi OS Virtual Environment
- DHT Temperature
- DHT Humidity
- Sensor Reading
- Sensor Read Failure
- Retry
- Exception Handling
- 每 2 秒更新一次
- Terminal 即時環境監測
- 溫度 Warning / Danger
- PWM LED 顯示警報程度
- Buzzer 高溫警報
- Sensor Data Filtering 基本概念
- 建立完整 Raspberry Pi 溫溼度監測器
完成第 11 章後,就會首次建立:
Real-world Sensor
↓
Raspberry Pi
↓
Python
↓
Decision
↓
LED / Buzzer
也就是一個真正具備:
感知 → 判斷 → 動作
能力的 Raspberry Pi 自動控制系統。