3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RaspberryPiのPythonによるPWM出力について

Last updated at Posted at 2023-10-26

RPi.GPIOを用いたPWM出力

ラズパイでPWM出力といえばRPi.GPIOを使ってこんなコードを書くかと思います。

import time
import sys
import RPi.GPIO as GPIO

def main():
    BZPIN = 19
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(BZPIN , GPIO.OUT)
    BZ = GPIO.PWM(BZPIN, 440)
    BZ.start(50)
    time.sleep(10)
    BZ.cleanup(BZPIN)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt :
        print("!!Exit!!")
        sys.exit()

このプログラムでは、19番ピンに接続したブザーから440Hz(ラ)の音が流れることが期待できるわけですが、

なんか変ですね。その原因はこちらの波形を見ればわかります。100Hzを出力した場合の波形ですが、波形が乱れて残像が出ています(ジッタが出ている)。
TEK00001.png
こちらのフォーラムの投稿によると、RPi.GPIOのPWMはソフトウェアPWMを使用しており、OSやPythonの処理時間の乱れの影響を受けるようです。逆にきれいな音を鳴らすためにはハードウェアPWMを用いてソフトの処理遅れの影響を排除する必要があります。

ハードウェアPWM

ラズパイのチップセット(bcm2711)には、データシート8章に記載がある通り、クロックからハード的にPWMを生成する回路が存在します。

image.png

この回路のレジスタに設定を書き込むと、自動的にPWMの波形が生成されるので、ジッタが発生しません。
ただし、ハードウェアPWMを利用できるのは、GPIO12/18もしくはGPIO13/19だけで、しかもGPIO12⇔GPIO18や、GPIO13⇔GPIO19で違うPWM波形を出すことはできません。(12⇔13、18⇔19は独立したPWM波形を生成可能)
PythonでこのハードウェアPWMを生成する方法を2つ紹介します。

方法1:rpi-hardware-pwm

このライブラリは、ハードウェアPWMを生成する専用のライブラリです。

インストール

rpi-hardware-pwmはハードウェアPWMを出すためのライブラリです。まずはインストールします。

sudo pip install rpi-hardware-pwm

externally-managed-environment エラーが出る場合があります。
sudo pip install --break-system-packages rpi-hardware-pwmなんて治安の悪いことをしましたが、気にする人は仮想環境を使うなりしてもらえればよいかと思います。

設定

このライブラリでは事前にPWMを出す端子を決めて/boot/config.txtに記載しておく必要があります。Channel0に設定したPWMをGPIO18から、Channel1に設定したPWMをGPIO19から出力する場合は以下の記載をすればOKです。

[all]
## 中略 ##
dtoverlay=pwm-2chan

GPIO12、GPIO13から出す場合はこうなります。

[all]
## 中略 ##d
toverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4

実行結果

import time
import sys
from rpi_hardware_pwm import HardwarePWM

def main():
    BZPIN = 19
    BZ = HardwarePWM(pwm_channel=0, hz=100)
    BZ.start(50)
    time.sleep(100)
    BZ.stop()

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt :
        print("!!Exit!!")
        sys.exit()

波形を見るとジッタは一切出ていません。

TEK00002.png

1kHzを設定した波形も全く問題ありません。(RPi.GPIOで1kHzはぶれすぎて計測が困難でした)
TEK00004.png

方法2:pigpio

こちらは、RPi.GPIOの置き換えを念頭においたライブラリです。

デーモンがバックグラウンドで動作すること、ハードウェアPWMをサポートしていないポートでもある程度(例:5μs)の精度が実現出来るようです。

インストール方法

pip install pigpio

こちらも、適宜仮想環境を使うほうが良いかと思われます。
また、事前にデーモンの実行が必要です。

sudo pigpiod

本格的に使うなら、自動実行させても良いでしょう。

sudo systemctl enable pigpiod
sudo systemctl start pigpiod

実行結果

このコードで動作を確認しました。

import time
import sys
import pigpio

def main():
    BZPIN = 19
    BZPIN2 = 20
    DUTY = 0.5
    gpio = pigpio.pi()
    # BZPINから1kHz, Duty=50%の波形を出力
    gpio.hardware_PWM(BZPIN, 1000, int(DUTY *10000))
    # BZPIN2から1kHz, Duty=50%の波形を出力
    gpio.set_PWM_frequency(BZ2PIN, 1000)
    gpio.set_PWM_dutycycle(BZ2PIN, int(DUTY * 255))
    time.sleep(100)
    gpio.stop()
    

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt :
        print("!!Exit!!")
        sys.exit()

bcm2711には搭載されているハードウェアPWMが使えるBZPIN(GPIO19)は、波形を見るとジッタは一切出ていません。このことから、ハードウェアPWMを使用できていることがわかります。
TEK00005.png

本来PWMを出力できないBZPIN2(GPIO20)ですが、こちらも波形に問題ありません。
(なお、daemonの受け付けられる最小の周期が決まっているので、設定した周波数を正確に出せるわけでは無いようです。例:440Hz=400Hz)
TEK00003.png

また、同時にRPi.GPIOを用いて他の端子のLEDを点灯・消灯させましたが、通常通り動作しました。

感想

RPi.GPIOは確かに手軽ですが、リアルタイム性を求める動作には向かないことがわかりました。LEDやDCモータをPWM制御する分には問題なくても、音を鳴らしたりステッピングモータを回したりすると影響が無視できないです。
また、ハードウェアPWMを用いるため、rpi_hardware_pwmを使用しましたが、こちらは設定が少々面倒で、特にPWMを出力する端子を変更するためには再起動が必要でした。ただ、プログラミングはし易いように感じました(周波数やDutyはそのまま入れればOK)。
一方、pigpioは、ハードウェアPWMだけでなくリアルタイム制御に使える機能(今回は試していないですが入力割り込みなど)が多く、設定も楽(デーモンを起動するだけ)でしたが、引数に若干くせがあるように感じました。
また、どちらもRPi.GPIOを同時に用いても問題ない(ピンの重複はNG)ので、リアルタイム制御がいらないデバイスは、過去に書いたプログラムをがんばって書き直す必要がないのが嬉しいです。

3
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?