DEV Community

codemee
codemee

Posted on • Edited on

1

使用 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 程式庫。

Image of Stellar post

How a Hackathon Win Led to My Startup Getting Funded

In this episode, you'll see:

  • The hackathon wins that sparked the journey.
  • The moment José and Joseph decided to go all-in.
  • Building a working prototype on Stellar.
  • Using the PassKeys feature of Soroban.
  • Getting funded via the Stellar Community Fund.

Watch the video

Top comments (0)

ACI image

ACI.dev: The Only MCP Server Your AI Agents Need

ACI.dev’s open-source tool-use platform and Unified MCP Server turns 600+ functions into two simple MCP tools on one server—search and execute. Comes with multi-tenant auth and natural-language permission scopes. 100% open-source under Apache 2.0.

Star our GitHub!

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay