入力(Input)

Anonymous Ben Croston

入力(Input)

GPIO入力をプログラムに取り入れる方法には幾つかの方法かある。 最も簡単な方法は、ある自国における入力値を調べることで、『ポーリング』 と呼ばれている。この方法は、まずいタイミングで値を読み込むと、入力をミ スする可能性がある。そこでポーリングはループ(繰り返し)の中で使われるの が一般的であるが、処理に負荷をかけることになる。別な方法は「割り込み」 (エッジの検出)を用いる方法である。ここでエッジとはHIGHからLOWへの状態遷移 (立ち下がりエッジ)と、LOWからHIGHへの状態遷移(立ち上がりエッジ)のことである。

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

何らかの回路に接続されていない入力ピンは「浮いた状態」と呼ばれる。 言い換えれば、そのようなピンから入力される値は、ボタンやスイッチを押す までどの回路にもつながっていなかったので「未定義」であったといえる。 おそらく電源からの干渉(ノイズ)を受けて何回も状態を変化することになる。

この状況を改善するには、プルアップかプルダウン抵抗を用いる。 これにより、入力のデフォルト値が設定される。ハードでプルアップ/プルダウン 抵抗をもたせ、ソフトでそれを使用することは可能である。 ハードでは 10K の抵抗を入力チャネルと 3.3 V ピン(プルアップ抵抗), もしくは 0 Vピン (プルダウン抵抗)をつなぐのが一般的である。 RPi.GPIOモジュールによりBroadcom SOCをこのような設定にさせることが ソフトで可能になっている:

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  # または
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

(ここで channel はBOARD か BCMのどちらかで設定したピン番号付けによるピン番号)

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

ある瞬間での入力値のスナップショットを取ることができる:

if GPIO.input(channel):
    print('入力値はHIGH')
else:
    print('入力値はLOW')

ループの中でポーリングすることで、ボタンが押されるまで待つことも可能:

while GPIO.input(channel) == GPIO.LOW:
    time.sleep(0.01)  # 10 ms 待つことでCPUに他のことをする機会を与える

(ここではボタンを押すことにより入力がLOWからHIGHに状態変化すると仮定)

割り込みとエッジ検出

エッジとは状態がLOWからHIGH(立ち上がりエッジ)、もしくは HIGHからLOW(たち下げエッジ)に変化することである。値よりも入力状態の変化 に関心がある場合が多い。このような状態変化は「イベント」の一つである。

プログラムが忙しく動いていて ボタンが押されるのを見逃すことがないようにするには、 次の二通りの方法がある:

  • 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をほとんど使わないため、他の仕事にCPUをあてられることである。

もしもボタンが押されるのを長く待ちたくない場合、 次のようにタイムアウト(timeout)を設定することができる:

#  立ち上がりエッジを最大 5秒まで待つ (タイムアウトはミリ秒単位)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
    print('タイムアウトした')
else:
    print('次のチャネルでエッジ検出された', channel)

event_detected() 関数

event_detected() 関数もループの中で用いられるが、ポーリングと異なり、 CPUが他の作業で忙しくしていても、入力状態の変化を見落とすことはない。 Pygame (ゲーム用のモジュール) や PyQt (GUIアプリ用モジュール) のように、 メインループがGUIイベントを監視し、イベントに対してタイムリーに反応を起こすような状況のときに役に立つものである。

GPIO.add_event_detect(channel, GPIO.RISING)  #  立ち上がりエッジ検出をチャネルに加える
do_something()
if GPIO.event_detected(channel):
    print('ボタンが押された')

検出できるイベントに GPIO.RISING, GPIO.FALLING、そして GPIO.BOTH が指定できる。

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

RPi.GPIO はコールバック関数用に2番目のスレッドを走らせる。 つまり、メインプログラムと同時にコールバック関数も「同時に」実行された め、エッジに即座に反応して実行できるのである。例:

def my_callback(channel):
    print(これはエッジイベントのコールバック関数です!')
    print('%sチャネルでエッジ検出された'%channel)
    print('これはメインプログラムとは別のスレッドで実行される')

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # チャネルで立ち上がりエッジ検出を追加する
...以下にプログラムが続く...

複数のコールバック関数が必要な場合は:

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)

注意: この場合、コールバック関数は並行的にではなく、順に実行される。 なぜなら、コールバック用にスレッドが一つしか建てられないからである。 そのスレッドで全部のコールバックが実行されるため、定義された順に実行される。

スイッチ・デバウンス

ボタンを押すたびにコールバックが複数回呼び出されることがある。 これは「スイッチバウンス」と呼ばれる現象のせいである。 この対処法として二通りの方法がある:

  • 0.1uF のコンデンサをスイッチに追加する
  • ソフトウェアのデバウンス
  • 両方の組み合わせ

ソフトをもいいてデバウンスするには、コールバック関数を指定する関数に bouncetime=パラメータを追加する。bouncetimeは ミリ秒で指定する。例:

# チャネル上で立ち上がりエッジ検出を追加、検出後200msはスイッチバウンス処理のため、エッジを無視
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)

Related

Wiki: BasicUsage
Wiki: Examples

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks