DEV Community

codemee
codemee

Posted on • Updated on

控制蜂鳴器後伺服馬達就失效?

今天遇到有人想要同時控制蜂鳴器發聲與伺服馬達轉動, 但是卻發現只要使用蜂鳴器發聲後, 伺服馬達就失控, 可以參考以下的程式:

from machine import Pin,PWM
import time

# 伺服馬達接 D1, 頻率 50Hz
servo = PWM(Pin(5), freq=50)
servo.duty(100)    # 轉到角度 A
time.sleep(1)      # 等待 1 秒

# 無緣蜂鳴器接 D8
buzzer = PWM(Pin(15))
buzzer.duty(0)

servo.duty(30)     # 轉到角度 B
print('開門')
time.sleep(1)      # 等待 1 秒

buzzer.freq(261)   # 設定聲音頻率
buzzer.duty(512)   # 設定音量
time.sleep(0.5)    # 持續播放半秒
buzzer.duty(0)     # 停止播放

time.sleep(1)      # 等待 1 秒
servo.duty(100)    # 轉回角度 A
print('關門')
Enter fullscreen mode Exit fullscreen mode

播完聲音後, 伺服馬達就轉不回來了, 這主要是因為 PWM 的頻率是所有腳位共用, 播完聲音後頻率已經被改為 261Hz, 不再是控制伺服馬達所需要的 50Hz, 因此無法將馬達轉回來。要解決這問題, 最陽春的做法就是在控制伺服馬達前, 重新設定 PWM 的頻率:

from machine import Pin,PWM
import time

# 伺服馬達接 D1, 頻率 50Hz
servo = PWM(Pin(5), freq=50)
servo.duty(100)    # 轉到角度 A
time.sleep(1)      # 等待 1 秒

# 無緣蜂鳴器接 D8
buzzer = PWM(Pin(15))
buzzer.duty(0)

servo.duty(30)     # 轉到角度 B
print('開門')
time.sleep(1)      # 等待 1 秒

buzzer.freq(261)   # 設定聲音頻率
buzzer.duty(512)   # 設定音量
time.sleep(0.5)    # 持續播放半秒
buzzer.duty(0)     # 停止播放

time.sleep(1)      # 等待 1 秒
servo.freq(50)     # 重新設定頻率
servo.duty(100)    # 轉回角度 A
print('關門')
Enter fullscreen mode Exit fullscreen mode

這樣就轉得回來了。不過這樣的作法在播放聲音的時候, 伺服馬達可能還是會受到頻率變換的影響, 導致偶有抖動的狀況, 最根本的做法就是在控制完 PWM 訊號的裝置後, 卸除 PWM 功能:

from machine import Pin,PWM
import time

def set_PWM(
    p,        # 腳位編號
    d,        # 工作週期
    f,        # 頻率
    t,        # 持續時間
):
    pwm = PWM(Pin(p), freq=f) # 建立 PWM 物件
    pwm.duty(d)               # 設定工作週期
    time.sleep(t)             # 等待持續時間
    pwm.deinit()              # 卸除 PWM


# 伺服馬達接 D1, 轉到角度 A, 1 秒
set_PWM(5, 100, 50, 1)
# 伺服馬達接 D1, 轉到角度 B, 1 秒
print('開門')
set_PWM(5, 30, 50, 1)

# 無緣蜂鳴器接 D8, 播聲音 0.5 秒
set_PWM(15, 512, 261, 0.5)

time.sleep(1)     # 等待 1 秒

print('關門')
# 伺服馬達接 D1, 轉到角度 A, 1 秒
set_PWM(5, 100, 50, 1)
Enter fullscreen mode Exit fullscreen mode

如果是使用 ESP32, 那也可以參考使用 esp32.RMT 設計不同頻率的 PWM讓不同腳位以不同的頻率進行 PWM。

Top comments (0)