由於 ESP32 的 MicroPython 實作中所有的 PWM 通道 (channel) 都是使用同樣的頻率, 如果需要用在不同的情境, 例如用 D26 和 D27 同時控制不同的喇叭發聲, 就沒有辦法做到了。不過在 esp32 模組中, 有個 RMT 類別,雖然原始用途並不是拿來做 PWM, 但卻因為設計上的巧合, 剛好可以用來變造成可自訂不同頻率的 PWM。
RMT 是 Remote Control Module 之意, 原本是為了能控制紅外線元件發射/接收特定高低電位交替的波形而設計, 另外像是 HC-SR04 超音波測距模組、DHT 系列溫濕度感測器在使用時也需要發送/接收類似的訊號, 因此在 ESP32 上就有這樣的功能, 可以依照指定的時間長度, 依序發出高低電位交替的訊號。
建立 RMT 物件
要使用 RMT, 首先要從 esp32
模組中匯入並建立 RMT
類別:
>>> from esp32 import RMT
>>> from machine import Pin
>>> r = RMT(0, pin=Pin(26), clock_div=80)
- 第 1 個參數是 RMT 通道 (channel) 編號, 有 0~7 共 8 個通道。
- 具名參數
pin
是腳位, 可使用任意腳位。 - 具名參數
clock_div
是時脈除頻器 (clock devider), 可用的值為 1~255。預設的時脈是 80MHz, 除以時脈除頻器就可以得到真正的頻率, 計算出單一週期的時間。以上例來說單一週期就是 1000000us / (80Mhz / 80) = 1us。
傳送訊號
建立好 RMT 物件後, 就可以使用 write_pulse()
送出信號, 例如:
>>> r.write_pulses((10, 30, 10), start=1)
- 第 1 個參數必須是串列或是元素組 (tuple), 指定交替高低電位的持續時間, 可以是 0~32767, 時間單位就是剛剛建立 RMT 物件時計算所得的單一週期。以上例來說, 因為單一週期是 1us, 所以高低電位交替信號持續時間就分別是 10us、30us、10us。
- 具名參數
start
是指定第 1 個訊號要從高電位 (1) 還是低電位 (0) 開始, 沒有指定則預設為 1。以上例來說, 因為start
為 1, 所以依序會送出 10us 的高電位、30us 的低電位、10us 的高電位, 訊號送完後會自動回復成低電位。
如果需要知道脈波序列是否已經發送完畢, 可以使用 wait_done()
:
>>> r.wait_done()
True
如果想讓訊號反覆不斷發送, 可以使用 loop()
:
>>> r.loop(True)
只要傳入 True
, 就會反覆發送訊號, 傳入 False
即停止發送。
請注意:在 1.13 版的韌體, loop(True)
要在 write_pulses()
前執行才會生效。
利用 RMT 客製 PWM
綜合上述, 只要結合 loop(), 我們就可以利用 RMT 來設計 PWM。舉例來說, 如果想要讓喇叭發出 261Hz 的音頻, 可以計算出週期為 1000000us / 261 = 3831.418us, 以佔空比 (duty cycle) 為 50% 讓喇叭震幅最大聲, 也就是 3831.418us / 2 = 1915.709us 為高電位與低電位各自持續的時間, 以下的程式就可以發出 261Hz 的 Do:
>>> r.loop(True)
>>> r.write_pulses((1915, 1915), start=1)
以下的完整程式就可以唱出嗡嗡嗡囉:
from machine import Pin
from esp32 import RMT
from utime import sleep
def sound(freq):
if freq == 0:
sleep(0.5)
else:
ticks = int(1000000/freq/2)
r.loop(True)
r.write_pulses((ticks, ticks), start=1)
sleep(0.25)
r.loop(False)
sleep(0.25)
r = RMT(0, pin=Pin(26), clock_div=80)
notes = (
392, 329, 329, 0,
349, 293, 293, 0,
261, 293, 329, 349, 392, 392, 392)
for note in notes:
sound(note)
r.deinit()
由於 RMT 有 8 個通道, 所以我們就等於有 8 個可隨意自訂頻率的 PWM 可以使用了。
相關功能可以參考我所撰寫的 esp32_rmt_pwm 程式庫。