第10章 Raspberry Pi PWM 控制:LED 調光、呼吸燈與蜂鳴器實作

發布日期: |更新日期:
文章主圖:第10章 Raspberry Pi PWM 控制:LED 調光、呼吸燈與蜂鳴器實作

前面第 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:

Raspberry Pi 5 Hardware PWM GPIO 對照表
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. 呼吸燈不平順怎麼辦?

先確認:

  1. 使用 PWMLED,不是 LED。
  2. value 每次變化不要太大。
  3. Delay 不要太長。
  4. 程式沒有其他長時間 Blocking 工作。
  5. GPIO Zero Pin Factory 正常。
  6. 系統 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

GPIO Zero 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

GPIO Zero 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

GPIO Zero 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. 本章重點整理

完成本章後,應該掌握:

  1. PWM 是 Pulse Width Modulation。
  2. PWM 使用快速 HIGH / LOW 切換產生平均控制效果。
  3. Frequency 表示每秒 PWM Cycle 數。
  4. Period 表示一個 PWM Cycle 所需要的時間。
  5. Frequency 與 Period 互為倒數。
  6. Duty Cycle 是 HIGH Time 佔完整 Period 的比例。
  7. 0% Duty Cycle 代表完全關閉。
  8. 50% Duty Cycle 代表一半週期為 HIGH。
  9. 100% Duty Cycle 代表完全開啟。
  10. PWM 不等於真正固定類比輸出電壓。
  11. Raspberry Pi 一般 GPIO 使用 3.3V Logic。
  12. Raspberry Pi 官方列出所有 GPIO 可使用 Software PWM。
  13. Hardware PWM 可在 GPIO12、GPIO13、GPIO18、GPIO19 使用。
  14. Raspberry Pi 5 的 RP1 提供 PWM Peripheral。
  15. GPIO Zero PWMLED 可以控制 LED Variable Brightness。
  16. PWMLED.value 使用 0.0~1.0 表示 Duty Cycle。
  17. PWMLED 預設 Frequency 為 100Hz。
  18. led.value=0.25 表示 25% Duty Cycle。
  19. led.value=0.5 表示 50% Duty Cycle。
  20. led.value=1 表示 Fully On。
  21. for Loop 可以製作漸亮/漸暗。
  22. GPIO Zero pulse() 可以直接製作呼吸燈。
  23. pulse() 預設 Fade In / Out 各 1 秒。
  24. pulse(n=5) 可以限制呼吸次數。
  25. frequency 可以調整 PWM Frequency。
  26. Frequency 與 Duty Cycle 是不同控制參數。
  27. Frequency 高低不代表 LED 亮度等比例增減。
  28. PWMLED 可以配合 Button 改變 Brightness。
  29. GPIO Zero Button Event 可以和 PWM Output 整合。
  30. Active Buzzer 適合 Digital On/Off。
  31. GPIO Zero Buzzer 只能進行簡單 On/Off,不支援多 Tone。
  32. Buzzer.beep() 可以建立間歇警報。
  33. Passive / Tonal Buzzer 需要 Frequency 訊號。
  34. GPIO Zero TonalBuzzer 可播放不同 Tone。
  35. TonalBuzzer.play("A4") 可播放 A4。
  36. A4 為 440Hz、MIDI Note 69。
  37. Tone 可以使用 Hz、MIDI 或 Musical Note 表示。
  38. Tone.from_frequency() 可明確指定 Frequency。
  39. buzzer.stop() 可以停止 TonalBuzzer。
  40. 實際 Buzzer 必須確認 Operating Voltage 與 Current。
  41. 不同 Buzzer 的 Frequency Response 不相同。
  42. GPIO 不應直接驅動高電流負載。
  43. Motor 應透過 Motor Driver / H-bridge 控制。
  44. Fan / Motor / Pump 等通常需要 External Power。
  45. GPIO PWM 的角色是 Control Signal,不是 Power Supply。
  46. Inductive Load 應考慮 Driver 與保護電路。
  47. PWMLED(18) 不應直接等同於「正在使用 Pi Hardware PWM Peripheral」。
  48. GPIO Zero PWM 與 Raspberry Pi Hardware PWM Alternate Function 是兩個不同層次的概念。
  49. Pi 5 底層 I/O 架構使用 RP1,舊式低階 PWM 教學需重新確認相容性。
  50. 本章完成後已具備 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 自動控制系統。

下一步學習

不要停在這一篇,接著把內容做成自己的實作

目前主題:Raspberry Pi。從完整學習路線、免費資源、會員下載,再接到實作所需材料。

同系列:Raspberry-Pi |  已是會員?直接前往 Getfile
相關資源與會員文章下載
文章可免費閱讀;下載完整 DOCX 或 PDF 需登入免費基本會員。
會員免費下載 DOCX 會員免費 PDF/列印

← 回文章列表