本篇測試樹莓派 GPIO 的 PWM (Pulse Width Modulation) 功能, 這是利用調整數位方波的頻率與高準位寬度來模擬類比信號的技術, 常用於資料傳輸與通訊. 由於 PWM 實際上是指定頻率下高低位準交替的方波, 比使用真實的類比信號傳輸具有抗雜訊與增加傳輸距離之優點. 在物聯網專案中, PWM 常用來控制燈光的亮度或聲音的大小, 參考 :
本系列之前的文章參考 :
以下測試將分別使用 RPi.GPIO 與 gpiozero 模組之範例以茲對照. 為了實驗接線方便, 將 BCM 與 BOARD 兩種腳位對照圖重貼如下 :
4. 使用 RPi.GPIO 的 PWM 類別 :
RPi.GPIO 模組的 PWM 類別用來處理 GPIO 的 PWM 輸出功能, 呼叫其建構式 PWM(pin, freq) 並傳入輸出腳位編號與頻率即可建立一個 PWM 物件 :
pwm=PWM(pin, freq)
但在呼叫 PWM() 之前必須先呼叫 setup() 函式將該腳位設為輸出模式, 例如 :
>>> import RPi.GPIO as GPIO
>>> GPIO.setmode(GPIO.BOARD) # 使用板子位置編號
>>> GPIO.setup(3, GPIO.OUT) # 必須設為輸出腳
>>> pwm=GPIO.PWM(3, 1000) # 板子位置編號 3=GPIO2, 頻率為 1000 Hz
>>> type(pwm)
<class 'RPi.GPIO.PWM'>
可見已建立一個 PWM 物件, 可用前一篇使用的自訂模組 members 來檢視其成員 :
>>> import members
>>> members.list_members(pwm)
ChangeDutyCycle <class 'builtin_function_or_method'>
ChangeFrequency <class 'builtin_function_or_method'>
start <class 'builtin_function_or_method'>
stop <class 'builtin_function_or_method'>
可見 PWM 有四個方法, 用法摘要如下表 :
PWM 物件方法 | 說明 |
start(dc) | 開始 PWM 輸出, 傳入 dc (duty cycle, 值 0~100) |
stop() | 停止 PWM 輸出 |
ChangeDutyCycle(dc) | 更新 duty cycle, 傳入新 dc (duty cycle, 值 0~100) |
ChangeFrequency(freq) | 更新頻率, 傳入新頻率 freq (Hz) |
例如 :
# rpi-gpio-led-test-4-1.py
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BOARD)
GPIO.setup(3, GPIO.OUT)
pwm=GPIO.PWM(3, 10) # Pin3= GPIO2, 頻率 10 Hz
while True:
for dc in range(5, 101, 5): # dc=5~100 增幅為 5
pwm.start(dc)
sleep(0.5)
此例將 GPIO2 (pin3) 設為 PWM 輸出, 頻率為 10 Hz, 然後在迴圈中將 dc 從 5 增幅 5 增加到 100, 可以看到 LED 在 dc 值小時明顯會閃爍 (因為頻率才 10 Hz), 當 dc 變大時閃爍現象就越來越小 (亮的時間較多了). 如果將頻率提高為 1000 Hz, 則即使 dc 值小, 閃爍也不明顯, LED 將慢慢地增亮.
下面是改編自官網的範例, 參考 :
# rpi-gpio-led-test-4-2.py
import RPi.GPIO as GPIO
form time import sleep
GPIO.setmode(GPIO.BOARD)
GPIO.setup(3, GPIO.OUT)
pwm=GPIO.PWM(3, 50) # Pin3= GPIO2, 頻率 50 Hz
pwm.start(0)
try:
while 1:
for dc in range(0, 101, 5): # 遞增 dc 由 0 至 100 增幅 5
pwm.ChangeDutyCycle(dc)
sleep(0.1)
for dc in range(100, -1, -5): # 遞減 dc 由 100 至 0 減幅 5
pwm.ChangeDutyCycle(dc)
sleep(0.1)
except KeyboardInterrupt:
pass
pwm.stop()
GPIO.cleanup()
此例在無限迴圈中先將 dc 由 0 遞增 5 至 100, 然後由 100 遞減 5 至 0, 使用 try except 處理 Ctrl+C 鍵盤中斷.
5. 使用 gpiozero 的 PWMLED 類別 :
gpiozero 的 PWMLED 類別可用來控制 PWM 輸出腳位, 只要將 BCM 腳位編號傳入建構式 PWMLED() 就會傳回一個 PWMLED 物件 :
pwm=PWMLED(pin [, initial_value=0, frequency=100])
若只傳入 pin 參數, 則預設 duty cycle 為 0, 頻率為 100 Hz, 可以用 value 屬性設定其 duty cycle (值 0~1), 或使用 frequency 屬性變更其頻率, 例如 :
>>> from gpiozero import PWMLED
>>> pwm=PWMLED(2) # 預設為 BCM 編號=GPIO2
>>> type(pwm)
<class 'gpiozero.output_devices.PWMLED'> # 建立 PWMLED 物件
>>> pwm.frequency # 預設頻率為 100 Hz
100
>>> pwm.value # 預設 duty cycle 為 0
0.0
>>> pwm.value=0.5 # 設定 duty cycle 為 0.5 (50%) LED 開始亮
用自訂模組 members 檢視 PWMLED 物件的成員 :
>>> members.list_members(pwm)
active_high <class 'bool'>
blink <class 'method'>
close <class 'method'>
closed <class 'bool'>
frequency <class 'int'>
is_active <class 'bool'>
is_lit <class 'bool'>
off <class 'method'>
on <class 'method'>
pin <class 'gpiozero.pins.rpigpio.RPiGPIOPin'>
pin_factory <class 'gpiozero.pins.rpigpio.RPiGPIOFactory'>
pulse <class 'method'>
source <class 'NoneType'>
source_delay <class 'float'>
toggle <class 'method'>
value <class 'float'>
values <class 'generator'>
常用屬性如下表 :
PWMLED 物件常用屬性 | 說明 |
value | 檢視或設定 duty cycle (值 0~1) |
frequency | 檢視或設定頻率 (單位 Hz) |
is_active | 若 PWM 有輸出脈波傳回 True, 否則 False |
is_lit | 若 PWM 有輸出脈波傳回 True, 否則 False |
pin | 傳回此 PWM 腳之 BCM 編號 (GPIOxx) |
closed | PWM 功能是否已被關閉 (True/False) |
active_high | 是否 active 狀態為高位準 (True/False) |
常用方法如下表 :
PWMLED 物件常用方法 | 說明 |
toggle() | 呼叫後 duty cyle 會從 0 變 1 或 1 變 0 (亮滅交替) |
pulse(fade_in_time, fade_out_time) | 呼吸燈效果 (參數為淡入淡出秒數) |
blink(on_time, off_time) | 輸出脈波使 LED 閃爍 (參數單位為秒) |
off() | 關掉脈波 (熄燈, duty ccycle=0) |
on() | 開啟脈波 (亮燈, duty ccycle=1) |
close() | 結束 PWM 功能 (清除物件) |
用法參考 :
茲將上面 RPi.GPIO 範例改為如下 gpiozero 版 :
# rpi-gpio-led-test-5-1.py
from gpiozero import PWMLED
from time import sleep
pwm=PWMLED(2, frequency=10)
dc=0.05
while True:
if dc <= 1.0:
pwm.value=dc
dc += 0.05
else:
dc=0.05
sleep(0.5)
此例由於 value 屬性的值為 0~1 的浮點數, 不能使用產生整數序列的 range(), 所以改用 if else 判斷, 結果與上面用 RPi.GPIO 的相同. 如果有安裝 Numpy 就可以用 np.arange() 來產生浮點數序列, 程式碼會更簡短.
下面範例是上面 4-2 的 gpiozero 版 :
# rpi-gpio-led-test-5-2.py
from gpiozero import PWMLED
from time import sleep
import numpy as np
pwm=PWMLED(2, frequency=50)
try:
while 1:
for dc in np.arange(0, 1, 0.05):
pwm.value=dc
sleep(0.1)
for dc in np.arange(1, 0, -0.05):
pwm.value=dc
sleep(0.1)
except KeyboardInterrupt:
pass
此例使用 numpy 的 arange() 來產生 0~1 的 PWMLED 物件之 value 以控制 duty cycle, 因為 Python 本身的 range() 函式只能用在產生整數序列.
沒有留言:
張貼留言