« Linux:SCSIアドレスとデバイス名の関係 | トップページ | Raspberry Pi 4 Model でWebIOPiを動かす »

2020年9月 6日 (日)

Raspberry Pi チャタリング問題

Raspberry Piでモールス練習機(電鍵で打つ方)を作ろうとおもった。案外簡単かと思ったら思わぬところでつまずいている。
思わぬところと言うのは多分正しくなくて、想定していた事が起きてるんだろうけれど、その想定が範囲を超えているってことだろうか。

問題はチャタリング対策。

以下のコードで実験してみた。電鍵が押されているときはLED点灯、電鍵を戻すと消灯。それぞれの時間幅を測定する。24番チャネルの両エッジでCallbackが呼ばれるように設定。Bouncetime=150。ゆっくりだとこれで(ほぼ)問題なく動く。

import RPi.GPIO as GPIO
from time import sleep
import time

def my_callback(channel):
    global ledstate
    global last_time

    ut = time.time()

    if channel==24:
        ledstate = not ledstate
     if ledstate == GPIO.HIGH:
            GPIO.output(25, GPIO.HIGH)
            print("HIGH")
            print(ut-last_time)
            last_time = ut

        else:
            GPIO.output(25, GPIO.LOW)
            print("LOW ")
            print(ut-last_time)
            last_time = ut

GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(24, GPIO.BOTH, callback=my_callback, bouncetime=150)

ledstate = GPIO.LOW
last_time = 0

try:
    while True:
        sleep(0.01)

except KeyboardInterrupt:
    pass

GPIO.cleanup()

しかし、実際に電鍵を叩いてみると短点は案外短く、普通に打っても100ms以下になる。で、Bouncetimeを短くするとチャタリングを拾ってしまう。つまり、電鍵ってのはチャタリングが多発するけれども、それをアナログ信号として扱うからチャタリングが多少発生しても苦にならない使い方をしてきたから問題ないわけで、これをデジタル的に処理しようとするとローパスフィルター的な仕組みを組み込まないと使い物にならないってことかと。

なかなか奥が深い。

あと、RISING・FALLINGのそれぞれのエッジでCallbackが呼ばれるように作ってみたけれども(以下サンプルコード)、これはこれで大きな問題があった。たとえばそれぞれのbouncetimeを200msに設定してあったとしても、RISINGエッジ処理にはbouncetimeはゆうこうだけれどもRISINGエッジ処理中にFALLINGエッジが発生するとRISINGエッジ処理のbouncetimeとは無関係に即時的に処理される。なので交互にのんびり処理するつもりが訳のわからないことになる。

def my_callback_rise(channel):
   global event_time
   ut = time.time()

   if channel==24:
      GPIO.output(25, GPIO.HIGH)
      event_time = ut

def my_callback_fall(channel):
   global event_time
   ut = time.time()

   if channel==23:
      GPIO.output(25, GPIO.LOW)
      event_time = ut - event_time
      print(event_time)


GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback_rise, bouncetime=200)
GPIO.add_event_detect(23, GPIO.FALLING, callback=my_callback_fall, bouncetime=200)

« Linux:SCSIアドレスとデバイス名の関係 | トップページ | Raspberry Pi 4 Model でWebIOPiを動かす »

ラズパイ日記」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

« Linux:SCSIアドレスとデバイス名の関係 | トップページ | Raspberry Pi 4 Model でWebIOPiを動かす »