N.Y.Cityのまちかど

RPi.GPIO_Module_BasicUsage

RPi.GPIOモジュールの使い方

2022/05/28翻訳

RPi.GPIOという、Raspberry PiのGPIOをPythonから使うためのライブラリがあります。この資料はSourceForgeに掲載されている公式の使いかたページを抄訳したものです。

RPi.GPIO モジュールの基礎

出典:https://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage/

モジュールのインポート

RPi.GPIOモジュールをインポートするためには以下を実行します。

import RPi.GPIO as GPIO

このような書き方をすることで、以降のスクリプトでは(RPi.GPIO を)GPIOと表記することができます。 モジュールをインポートし、なおかつ正常にインポートできたことをチェックするためには以下のようにします。

try:
    import RPi.GPIO as GPIO
except RuntimeError:
    print("Error importing RPi.GPIO!  This is probably because you need superuser privileges.  You can achieve this by using 'sudo' to run your script")

ピン番号

RPi.GPIOでIOピンを指定する方法は2種類あります。1つめはボードの番号を利用する方法です。P1ヘッダピンのピン番号で表記します。この方法の利点は、Raspberry Piのリビジョン番号に関係なく常に動作することです。コネクタを配線し直したり、プログラムを修正したりする必要がありません。 2つ目の方法はBCM番号を使用する方法です。この方法は低レベル(レイヤ)の方法―Broadcom SOCのチャンネル番号を参照する必要があります。SOCのチャンネル番号がRaspberry Pi回路のどこに接続されているかを常に確認して使う必要があるでしょう。また、Raspberry Piのリビジョン番号が変わる事で、プログラムを修正する必要が出てくる可能性があります。 指定方法は次の通りです(必須)

GPIO.setmode(GPIO.BOARD)
  # or
GPIO.setmode(GPIO.BCM)

どちらのIOピン指定方法を使用しているかを確認するためには(例えば別のPythonモジュールから):

mode = GPIO.getmode()

modeは GPIO.BOARD, GPIO.BCM or None のいずれかになります。

警告

1つのGPIOを2つ以上のスクリプトで使うことができます。その結果、もしもRPi.GPIOがすでにデフォルト(input)から設定変更されていることを検出した場合、スクリプトから設定をするときに警告を受け取ることになります。これを止めるためには

GPIO.setwarnings(False)

チャンネルのセットアップ

全てのチャンネルはinputまたはoutputに設定する必要があります。チャンネルをinputに設定するためには

GPIO.setup(channel, GPIO.IN)

(チャンネルの指定は事前の設定(BOARDまたはBCM)に基づいたチャンネル番号で行います) inputチャンネルに対するより高度な設定はこちらを参照してください チャンネルをoutputに設定するためには

GPIO.setup(channel, GPIO.OUT)

(チャンネルの指定は事前の設定(BOARDまたはBCM)に基づいたチャンネル番号で行います) outputには初期値を設定する事も出来ます

GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)

複数のチャンネルをセットアップする

1回の命令で、2つ以上のチャンネルをセットアップする事が可能です(release 0.5.8以降)。例えば

chan_list = [11,12]    # add as many channels as you want!
                       # you can tuples instead i.e.:
                       #   chan_list = (11,12)
GPIO.setup(chan_list, GPIO.OUT)

入力

GPIOピンの状態を入力するためには

GPIO.input(channel)

(チャンネルの指定は事前の設定(BOARDまたはBCM)に基づいたチャンネル番号で行います) この命令は0 / GPIO.LOW / False または 1 / GPIO.HIGH / Trueを返します。

出力

GPIOピンに値を出力するためには

GPIO.output(channel, state)

(チャンネルの指定は事前の設定(BOARDまたはBCM)に基づいたチャンネル番号で行います) 値には0 / GPIO.LOW / False または 1 / GPIO.HIGH / Trueを指定できます。

複数のチャンネルから出力する

1回の命令で複数のチャンネルから出力することが可能です(release 0.5.8以降)。例えば

chan_list = [11,12]                             # also works with tuples
GPIO.output(chan_list, GPIO.LOW)                # sets all to GPIO.LOW
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW))   # sets first HIGH and second LOW

クリーンアップ

プログラムを終了する時、使用済みのリソースを開放(クリーンアップ)することが望ましいです。RPi.GPIOも同様です。全てのチャンネルをプルアップ・プルダウンなしの入力状態に戻すことで、Raspberry Piのピンが短絡してしまうことを防ぎます。

Note:スクリプトが使用したチャンネルのみがクリーンアップされます

Note: GPIO.cleanup()はピン番号の指定方法もリセットします。

スクリプトの最後でクリーンアップを行うためには

GPIO.cleanup()

全てのチャンネルをクリーンアップしたいわけではない場合、リストまたはタプルで指定したチャンネルだけを個別にクリーンアップすることも可能です。

GPIO.cleanup(channel)
GPIO.cleanup( (channel1, channel2) )
GPIO.cleanup( [channel1, channel2] )

Raspberry Pi情報とRPi.GPIOのバージョン

Raspberry Piの情報を参照するためには

GPIO.RPI_INFO

Raspberry Piのrevisionを参照するためには

GPIO.RPI_INFO['P1_REVISION']
GPIO.RPI_REVISION    (deprecated)

RPi.GPIOのバージョンを参照するためには

GPIO.VERSION

GPIOに入力

出典:https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/

プログラムでGPIOから入力を受け取る方法はいくつかあります。1つ目に紹介するのはもっとも単純な方法で、その瞬間のGPOIの値を取り込む方法です。この方法は「ポーリング」として知られていますが、不適切なタイミングで値を読み込んでしまう可能性を含んでいます。ポーリングはループの中で実行され、プロセッサを占有してしまう可能性があります。GPIOの入力を受け取る別の方法は「割込み(エッジ検出)」を使用する方法です。エッジには信号がHからLに下がる瞬間(立ち下がり)と、信号がLからHに上がる瞬間(立ち上がり)の2つがあります。

プルアップ/プルダウン抵抗

もしも入力ピンに何も接続していない状態であれば、そのポートは「フロート状態」と言います。それは言い換えれば、スイッチを押していない間は何も接続されない状態になってしまうため、ポートから読みだした値が「不定」になる事を意味します。ノイズの影響を受け、値が大きく変化してしまう可能性があります。

これを回避するためには、プルアップ抵抗またはプルダウン抵抗を使用します。この方法で入力のデフォルト値を定めることができます。プルアップ/プルダウン抵抗はハードウェアでもソフトウェアでも用意することができます。ハードウェアで用意する場合、10kΩの抵抗を入力ポートと3.3V(プルアップの時)または0V(プルダウンの時)に接続する方法が一般的です。RPi.GPIOモジュールはこれをソフトウェアで用意するようにBroadcom SOCを設定することが可能です。

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  # or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

(チャンネルの指定は事前の設定(BOARDまたはBCM)に基づいたチャンネル番号で行います)

入力のテスト(ポーリング)

その瞬間における入力の値を取得することができます。

if GPIO.input(channel):
    print('Input was HIGH')
else:
    print('Input was LOW')

ボタンが押されている間ループを使って待機する方法

while GPIO.input(channel) == GPIO.LOW:
    time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things

(これは、ボタンを押すと入力がLからHに変わることを前提としています。)

割込みとエッジ検出

エッジとは、電気信号がLからH(立ち上がり)またはHからL(立ち下がり)に変換する瞬間を意味します。多くの場合、入力された値そのものよりも、状態の「変化」の方に関心があります。この状態変化がイベントです。 プログラムが他の処理で忙しい状態であってもボタン操作を見逃さないようにするためには、2つの方法があります。

  • wait_for_edge()関数
  • event_detected()関数
  • エッジを検出した時に実行するスレッド化されたコールバック関数
WAIT_FOR_EDGE() 関数

wait_for_edge()関数は、エッジを検出するまでプログラムの実行を止めます。言い換えれば「ボタンが押されるのを待つ」処理であり、次のように書くことができます。

GPIO.wait_for_edge(channel, GPIO.RISING)

GPIO.RISING、GPIO.FALLING、GPIO.BOTHのエッジを検出できることに注意して下さい。この方法の利点はCPUの使用量がごくわずかで、他のタスクに処理能力を回せることです。

特定の時間だけ待機する場合には、タイムアウトの設定を行うことができます。

# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
    print('Timeout occurred')
else:
    print('Edge detected on channel', channel)
EVENT_DETECTED()関数

event_detected() 関数は他の処理と共にループの中で使用するように作られていますが、ポーリングと違ってCPUが他の処理で忙しくても、状態の変化を見逃すことがありません。これはPygameやPyQtといった、GUIイベントをListenして応答するようなメインループがある場合に有効です。

GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
do_something()
if GPIO.event_detected(channel):
    print('Button pressed')

GPIO.RISING、GPIO.FALLING、GPIO.BOTHのエッジを検出できることに注意して下さい。

スレッド化されたコールバック

RPi.GPIOはコールバックのために2つ目のスレッドを実行します。これはコールバック関数がエッジに反応してメインのプログラムと同時に動けることを意味します。例えば

def my_callback(channel):
    print('This is a edge event callback function!')
    print('Edge detected on channel %s'%channel)
    print('This is run in a different thread to your main program')

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # add rising edge detection on a channel
...the rest of your program...

更なるコールバック関数が必要であれば

def my_callback_one(channel):
    print('Callback one')

def my_callback_two(channel):
    print('Callback two')

GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)

このケースの場合、コールバック関数は同時ではなく逐次的(シーケンシャル)に動くことに注意して下さい。なぜならばコールバックのために使われるスレッドは1つだけだからです。全てのコールバックが指定された順序で実行されます。

スイッチのデバウンス*2

1回ボタンを押しただけで、何度もコールバックが呼ばれることに気づくかもしれません。これは「スイッチバウンス*3」と呼ばれる現象が理由です。これに対処するためには2つの方法があります。

  • スイッチの両端に0.1μFのコンデンサを追加する
  • ソフトウェアによる対処
  • 上記2つを併用する

ソフトウェアによる対処を行う場合、コールバック関数にbouncetime=パラメータを追加することで対応できます。バウンス時間はミリ秒単位で指定します。例えば

# add rising edge detection on a channel, ignoring further edges for 200ms for switch bounce handling
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)

または

GPIO.add_event_callback(channel, my_callback, bouncetime=200)

イベント検出を削除する

何らかの理由で、もうイベントを検出して欲しくないという場合は、次の方法で止めることができます。

GPIO.remove_event_detect(channel)

GPIOから出力

出典:https://sourceforge.net/p/raspberry-gpio-python/wiki/Outputs/

RPI.GPIOの初期設定

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

出力をHIGHにするためには

GPIO.output(12, GPIO.HIGH)
 # or
GPIO.output(12, 1)
 # or
GPIO.output(12, True)

出力をLOWにするためには

GPIO.output(12, GPIO.LOW)
 # or
GPIO.output(12, 0)
 # or
GPIO.output(12, False)

複数のチャンネルから同時に出力するためには

chan_list = (11,12)
GPIO.output(chan_list, GPIO.LOW) # all LOW
GPIO.output(chan_list, (GPIO.HIGH,GPIO.LOW))  # first LOW, second HIGH

プログラムの最後にクリーンアップするためには

GPIO.cleanup()

Note:outputによって現在出力されている値をinput()で読み取ることができます。例えば出力をトグルさせる(切り替える)例は

GPIO.output(12, not GPIO.input(12))

RPi.GPIOでPWMを使う

出典:https://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/

PWMインスタンスを生成する

p = GPIO.PWM(channel, frequency)

PWMを開始する

p.start(dc)   # where dc is the duty cycle (0.0 <= dc <= 100.0)

周波数を変更する

p.ChangeFrequency(freq)   # where freq is the new frequency in Hz

デューティー比を変更する

p.ChangeDutyCycle(dc)  # where 0.0 <= dc <= 100.0

PWMを停止する

p.stop()

インスタンス変数pがスコープ外に出るとPWMも停止することに注意して下さい。

2秒ごとにLEDを点滅させる例

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

p = GPIO.PWM(12, 0.5)
p.start(1)
input('Press return to stop:')   # use raw_input for Python 2
p.stop()
GPIO.cleanup()

LEDを明るくしたり暗くしたりする例

import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

p = GPIO.PWM(12, 50)  # channel=12 frequency=50Hz
p.start(0)
try:
    while 1:
        for dc in range(0, 101, 5):
            p.ChangeDutyCycle(dc)
            time.sleep(0.1)
        for dc in range(100, -1, -5):
            p.ChangeDutyCycle(dc)
            time.sleep(0.1)
except KeyboardInterrupt:
    pass
p.stop()
GPIO.cleanup()

GPIO_FUNCTION(CHANNEL)

出典:https://sourceforge.net/p/raspberry-gpio-python/wiki/Checking%20function%20of%20GPIO%20channels/

GPIOピンの機能を取得する例

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
func = GPIO.gpio_function(pin)

結果としてGPIO.IN, GPIO.OUT, GPIO.SPI, GPIO.I2C, GPIO.HARD_PWM, GPIO.SERIAL, GPIO.UNKNOWNのいずれかを返します。

*1訳者注:日本語では「チャタリングシェーバ」とか「チャタリング除去」と呼ぶことが一般的です。

*2訳者注:日本語では「チャタリングシェーバ」とか「チャタリング除去」と呼ぶことが一般的です。

*3訳者注:日本語では「チャタリング」と呼ばれることが一般的です。


現在ご覧のページの最終更新日時は2022/06/21 20:17:49です。

Copyright (C) N.Y.City ALL Rights Reserved.

Email: info[at]nycity.main.jp