###### tags: `MicroPython`, `ESP32`, `PWM` # 使用 esp32.RMT 設計不同頻率的 PWM 由於 ESP32 的 MicroPython 實作中所有的 PWM 通道 (channel) 都是使用同樣的頻率, 如果需要用在不同的情境, 例如用 D26 和 D27 同時控制不同的喇叭發聲, 就沒有辦法做到了。不過在 esp32 模組中, 有個 [RMT](http://docs.micropython.org/en/latest/library/esp32.html#rmt) 類別,雖然原始用途並不是拿來做 PWM, 但卻因為設計上的巧合, 剛好可以用來變造成可**自訂不同頻率的 PWM**。 RMT 是 **Remote Control Module** 之意, 原本是為了能控制紅外線元件發射/接收特定**高低電位交替**的波形而設計, 另外像是 HC-SR04 超音波測距模組、DHT 系列溫濕度感測器在使用時也需要發送/接收類似的訊號, 因此在 ESP32 上就有這樣的功能, 可以依照指定的時間長度, 依序發出高低電位交替的訊號。 ### 建立 RMT 物件 要使用 RMT, 首先要從 `esp32` 模組中匯入並建立 `RMT` 類別: ```python >>> 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()` 送出信號, 例如: ```python >>> 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()`: ```python >>> r.wait_done() True ``` 如果想讓訊號反覆不斷發送, 可以使用 `loop()`: ```python >>> r.loop(True) ``` 只要傳入 `True`, 就會反覆發送訊號, 傳入 `False` 即停止發送。 :::info 在 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: ```python >>> r.loop(True) >>> r.write_pulses((1915, 1915), start=1) ``` 以下的完整程式就可以唱出嗡嗡嗡囉: ```python 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](https://github.com/codemee/esp32_rmt_pwm) 程式庫。