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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

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

Raspberry Pi

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

Q&A

解決済

3回答

11446閲覧

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

Sigma1630

総合スコア36

Python 3.x

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

Raspberry Pi

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

0グッド

1クリップ

投稿2018/07/26 13:05

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

【回路】
イメージ説明

【コード】

Python

1import RPi.GPIO as GPIO 2from time import sleep 3 4def my_callback(x): 5 global ledstate 6 if x==24: 7 ledstate=not ledstate 8 if led state==1: 9 GPIO.output(25.GPIO.HIGH) 10 else: 11 sleep(0.5) #後述 12 GPIO.output(25,GPIO.LOW) 13 14GPIO.setmode(GPIO.BCM) 15GPIO.setup(25,GPIO.OUT,initial=GPIO.LOW) 16GPIO.setup(24,GPIO.IN,pull_up_down=GPIO.PUD_DOWN) 17GPIO.add_event_detect(24,GPIO.RISING,callback=my_callback,bouncetime=200) #タクトスイッチを押すとmy_callbackを呼び出す 18 19ledstate=0 20 21while True: 22 sleep(0.01) 23

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Sigma1630

2018/07/26 15:23

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

回答3

0

こんにちは。

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

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

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


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

投稿2018/07/26 15:02

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Sigma1630

2018/07/26 15:19

ご回答ありがとうございます。 24のプルダウンは下記の行で内部抵抗を有効化しています GPIO.setup(24,GPIO.IN,pull_up_down=GPIO.PUD_DOWN) ご回答受けて試しに物理的にプルダウン抵抗を挿してみましたが状況変わらずです… 消灯するとき(ledstate==1のときに押下)のみ、なぜかcallbackが2回呼ばれる模様です。このため一回消えてもう一度つくようになってます。 ちなみに物理的に繋いだ場合でも、sleep(0.5)を抜くと問題なく作動します。なんなんでしょう^^;
Chironian

2018/07/26 17:38

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

0

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 は内部で使用している特殊な値(マジックナンバー)のようですよ。

投稿2018/07/28 17:57

編集2018/07/28 18:00
mt08

総合スコア1825

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

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

  1. タイマ割り込みを使用しない

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

  1. タイマ割り込みを使用

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

お好きな方をどうぞ


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

投稿2018/07/26 14:05

編集2018/07/26 14:10
y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Sigma1630

2018/07/26 15:30

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

2018/07/26 22:44

それを指します 基本的なチャタリング除去もマスターしてください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問