DEV Community

codemee
codemee

Posted on • Updated on

使用 esp32.RMT 設計不同頻率的 PWM

由於 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)
Enter fullscreen mode Exit fullscreen mode
  • 第 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)
Enter fullscreen mode Exit fullscreen mode
  • 第 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
Enter fullscreen mode Exit fullscreen mode

如果想讓訊號反覆不斷發送, 可以使用 loop()

>>> r.loop(True)
Enter fullscreen mode Exit fullscreen mode

只要傳入 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)
Enter fullscreen mode Exit fullscreen mode

以下的完整程式就可以唱出嗡嗡嗡囉:

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()
Enter fullscreen mode Exit fullscreen mode

由於 RMT 有 8 個通道, 所以我們就等於有 8 個可隨意自訂頻率的 PWM 可以使用了。

相關功能可以參考我所撰寫的 esp32_rmt_pwm 程式庫。

Top comments (0)