質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • Python 3.x

    7402questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Raspberry Pi

    898questions

    Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

タクトスイッチ操作時のチャタリングが原因と考えられる誤操作を防止したい

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 441

Sigma1630

score 11

【生じている問題・どうしたいか】
タクトスイッチを押した場合LEDがつき、もう一度押した場合LEDが消える回路を作りました。
LEDが消えているときにスイッチを押した場合は、問題なくLEDがつきます。
しかし、LEDがついているときにスイッチを押した場合、①完全に消える場合、と
②1回消えた後もう一度つく場合、があります。常に①の場合を達成できるコードを書きたいです。

【回路】
イメージ説明

【コード】

import RPi.GPIO as GPIO
from time import sleep

def my_callback(x):
    global ledstate
    if x==24:
        ledstate=not ledstate
        if led state==1:
            GPIO.output(25.GPIO.HIGH)
        else:
            sleep(0.5) #後述
            GPIO.output(25,GPIO.LOW)

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.RISING,callback=my_callback,bouncetime=200) #タクトスイッチを押すとmy_callbackを呼び出す

ledstate=0

while True:
    sleep(0.01)

この回路とコードにより、LEDが消えているときにスイッチを押すとLEDがつき、
LEDがついているときにスイッチを押すと、0.5secスリープした後、LEDが消えます。
しかし上述のように、ついているときに押した場合は、消える場合と、消えた後もう一度つく場合の2通りがでてしまいます。

【試したこと】
my_callback中の
sleep(0.5)
の行を削除すると、問題なく作動します(LEDがついている場合に押しても100%完全に消える)。
おそらくですが、callbackで呼ばれる関数がある程度の時間を伴う場合、今回のようなバグ(おそらくチャタリング)が
生じるのだと思います。

しかし、この行は残したまま解決できるようにしたいのです。なぜなら今後、イベント時に呼び出す関数が
この関数のようにある程度の長さの時間を伴った出力になる場合が想定されるためです。

原理として今回なぜこのようなバグが生じたか、どのようにしたらコードの意図を損なわずに解決できるのか、
ご教示いただけますと助かります。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Sigma1630

    2018/07/27 00:23

    気づきですが、スイッチを押しっぱなしにするとこのようなトラブルは発生しませんでした。しかし、スイッチを離したときになぜかcallbackが呼ばれて点灯してしまいます。

    キャンセル

回答 3

+3

こんにちは。

チャタが原因でそのような現象にはならない筈なので不思議ですね。
チャタがあると1回押した瞬間に複数回押された信号が発生します。
従って、待ち時間がない時は「複数回」が奇数ならLEDが反転しますし、偶数なら反応していないように見えます。

GPIO 24にハード的にはプルダウン抵抗を入れていないようです。ポートの設定でもプルダウンしていなかった場合、スイッチOFFの時GPIO 24は浮いていますので、周辺の電圧の影響を受けやすくなります。
LEDが点灯している時、GPIO 25がHighですからGPIO 24がなかなかLowに落ちないで、この時に限り嫌なタイミングでノイズでチャタっている可能性もあります。

GPIO 24をプルダウンしてみると改善するかも知れません。


因みに、タクト・スイッチはかなり優秀でほとんどチャタがありません。昔観察した時は1uSecもなかった記憶があります。Pythonは遅いですから、その遅さの御蔭でチャタが取れている可能性がありそうです。
そして、浮いている端子がLowに落ちる時間は結構長くて数秒かかるようなこともありました。(これは電源OFFした時の話ですので、ちょっと違いますが。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 00:19

    ご回答ありがとうございます。

    24のプルダウンは下記の行で内部抵抗を有効化しています
    GPIO.setup(24,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

    ご回答受けて試しに物理的にプルダウン抵抗を挿してみましたが状況変わらずです…

    消灯するとき(ledstate==1のときに押下)のみ、なぜかcallbackが2回呼ばれる模様です。このため一回消えてもう一度つくようになってます。

    ちなみに物理的に繋いだ場合でも、sleep(0.5)を抜くと問題なく作動します。なんなんでしょう^^;

    キャンセル

  • 2018/07/27 02:38

    不思議ですね。
    もし、その0.5秒待ちの間に、何らかの要因で再度GPIO 24が立ち上がったら何が起こるでしょうか?
    タクト・スイッチから手を離す時にチャタっている場合、ありえるかも知れません。
    これは結構過酷な使い方(コールバック関数に再入する)になるのでPythonがとちくるったりすることはないでしょうか?
    bouncetimeを例えば600mSecくらいにしたらどうなるでしょう? そのような設定をすれば再入することはなくなる筈です。

    キャンセル

checkベストアンサー

+2

チャタリング除去の方法を書いておきます

  1. タイマ割り込みを使用しない
    一度スイッチの状態を読み、約5msのウェイトをおいて再度読む、そして前回読んだ値と同一であれば、その値を返す。同一でなければ、再度5msのウェイト後読んで前回と同一かをチェック、と、同一になるまで繰り返す

  2. タイマ割り込みを使用
    タイマ割り込みで、約5msごとにスイッチの状態を読み、前回と同一なら、状態フラグをその値にセット、とする。 そして、メインルーチンからは状態フラグを読んでスイッチ状態とする。

お好きな方をどうぞ


スイッチを直接繋ぐ場合は、ポート割り込みを使うべきではありません。チャタリングの影響を必ず受けます

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 00:30

    ご回答ありがとうございます。

    割り込みとは
    GPIO.add_event_detect
    を指しますでしょうか?

    ご指摘のように、確かに頑張ればコード側でもっと確実にイベント検知をできそうな気もします^^; ただ、できれば、電圧の変化をイベントとする基本的なやり方もマスターしたいと考えております。

    キャンセル

  • 2018/07/27 07:44

    それを指します

    基本的なチャタリング除去もマスターしてください

    キャンセル

+2

Sigma1630さん、

スイッチのOn/Off ~ On/Offする間隔を十分にとった場合の動作はどうでしょうか?
また、0.5秒のスリープ中に、スイッチが押された場合は、どのように動作するのを期待していますか?

RPi.GPIOでは、gpioの変化検出のためにpthread_createで、poll_thread()というスレッドを作って、その中で、イベントが発生してbouncetimeの条件が揃うと、callback関数を呼んでいます。

callback関数実行終了後に次のイベント待ちになるので、callback内で時間がかかるような処理(Sleepなど)があると、次のイベントの取りこぼしが発生したと思います。

なので、RPi.GPIOでのイベントCallbackでは、Waitさせるような処理はしないで、イベント取得・フラグの設定くらいにして、LEDの点灯やら、0.5秒待って消灯させるというような処理は、While True: のループ内で行なうのがいいかもしれません。

気づきですが、スイッチを押しっぱなしにするとこのようなトラブルは発生しませんでした。しかし、スイッチを離したときになぜかcallbackが呼ばれて点灯してしまいます。

add_event_detect()で、GPIO.RISINGを指定しているので、立ち上がりエッジ(= GPIOがLOWからHIGHに変化 = スイッチを離すとき)のイベントでcallbackが呼ばれます。
スイッチを押したタイミングでイベントを起こしたい場合は、GPIO.FALLING、押したとき・離したときの両方でイベントを取りたいときは、GPIO.BOTHを指定します。

bouncetime=200なので、前回のイベント発生(callback呼び出し)から、200msec以内のイベント発生は無視されます。
1秒間に5回スイッチを押すような速さでやってしまうと、そのイベントは無視されますね。

ちなみに、bouncetime=-666 は内部で使用している特殊な値(マジックナンバー)のようですよ。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • Python 3.x

    7402questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Raspberry Pi

    898questions

    Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。