スイッチ入力時のチャタリングを防止する

〔マイコン使用による電子部品の使い方に戻る〕


リレーやスイッチはメカニカル接点です、金属の接点同士を接触させて電気を流す仕掛けです。
こちら「nabeの雑記帳」さんのブログにタクトスイッチの構造が書かれています、見てみましょう。
なのでスイッチを押せば金属接点同士が触れて電流が流れONとなる訳なのですが、
実はこの接触時又は離れた時に振動(バウンド)が起こりONとOFFが数回繰り返される現象が
発生します、この現象をチャタリングと言います。

だからスイッチをマイコンに接続した場合に、スイッチの動作時ONやOFFが数回マイコンに入力されま
す、すると1回のONでなく数回のONが入力されるとプログラムが予期しない動作で誤作動をします。

本来ならスイッチの動作時の波形と改善時の波形をオシロスコープで見てみたい所なのですが、
高価なオシロスコープなどは持っていません。o(TヘTo) カナシカー
当サイトでもたまに利用している秋月電子のLCDオシロキット で表示させて見たのですが無理でした。
こちらの「Tech Village」さんのHP"スイッチではチャタリングが起こる"の項目を見て下さい、
オシロスコープでの波形が掲載されています。

では如何するかぁ.......
(゚Д゚)オー 、マイコンでONとOFF時のカウントを行なえば良いんではないかい?
と言う事でぇ、ここの実験ではONとOFF時の変化が何回発生したかカウントを行います、
あんまり短いONとOFF時間はカウントは出来ないと思いますがやってみましょう。

タクトSW1  今回この2個のタクトスイッチ(タクタイルスイッチ:一般名詞)を試してみました。
 便宜上上のスイッチを[SW1]、下のスイッチを[SW2]と呼びます。
 SW1を押した時のカウント値は"1"で放した時は"1〜3"程のカウント値でした。

タクトSW2  SW2を押した時のカウント値は"1〜3"で放した時は"1"程のカウント値でした。
 SW1と逆のカウント値を示していますね〜(下の実行結果図を参照)
 って事は確実に3回以上マイコンは入力を受け付けるって事になります
 プログラムは確実に誤作動するでしょう

《ハードウエアによるチャタリング除去》

除去回路 左の回路がチャタリングを除去する為の回路です。
RC回路シュミットトリガ(74HC14)の構成です。 *2)

スイッチはプルアップで接続しているので、スイッチを
押した時がLOW、押していない時がHIGHとなります。
がぁシュミットトリガを通しているので反転される事に注意
即ちスイッチを押した時にマイコンはHIGHと認識します。

下記に[SW2]をON/OFFした時の波形を秋月電子の
LCDオシロキットで表示させた時の参考画像を示します。
いや〜ぁ、採れるまで何回リトライした事かぁ。

A点
  A点波形1  A点波形2
  回路図A点での波形で左がスイッチを押した時、右がスイッチを放した時です。
  カウント値でも押した時"1〜3"で、放した時"1"程だった様に、
  押した時にON/OFFが3ms以内に見た目2回程現れています(何とか見る事が出来て良かったぉ〜)
  この3msのチャタリング発生期間(100us-10ms程)はスイッチの種類等により幅が異なります。

B点
  B点波形1  B点波形2
  回路図B点での波形で左がスイッチを押した時、右がスイッチを放した時です。
  RC回路を通るとこの様になだらかなになります、なだらかな度合は
  τ=RxCで決まり、この回路なら時定数は47K x 0.1uF = 4700us(4.7ms)です
  時定数が大きくなれば更にスロープがなだらかになっていきます、が、
  なだらかになればそれだけスイッチを押した時の反応が遅れます。(この回路で約5ms遅れ下図)
  (尚、チャタリング発生期間が長ければ時定数を大きくしましょう)

  時定数とは約4.7ms後にVDDの63%(右図2.9V)まで達する事です、
  左図なら立下りはじめて63%(1.7V)程になるスロープです。
  シュミットトリガもこの辺りが閾値で立ち上がり(2.9V)立下り(1.7V)を行います。

C点
C点波形  シュミットトリガは入力と出力が反転するので
 C点の波形は左の様になります。


カウントプログラムについて

実験風景  左は[SW2]A点のカウントを数えている時の実験風景です。

 カウントを行うプログラムはArduinoで行っています、
 下記がそのスケッチです。

 回路的には[SW1]をデジタルピン7番
 [SW2]をデジタルピン8番に配線しています。
 [SW2]にRC回路とシュミットトリガ回路が
 配線されていますのでこの[SW2]をカウントします。

 スケッチで[SW2]をdigitalRead( )せずに、PINB入力ポート
 レジスターを直接見ています、この方が早く[SW2]の状態を
 知る事が出来る為です。
 デジタル8番はPINBの0番ポート(PB0)に割付けられている
 詳しい内容は、「Arduino 日本語リファレンス」の
 ”ポート操作”を参照下さい。

 自分の使いたいスイッチが有ればここの実験の様に行い、
 RC回路の時定数を変更しカウント値が"1"になる様に
 調整を行なえば良いでしょう。
 この方法だとオシロなくても何とかなるのでは?

---------------------------------------------------------------------
#define SW1_PIN 7
#define SW2_PIN 8   // このスイッチのカウントを測る

void setup()
{
     Serial.begin(9600) ;
     pinMode(SW1_PIN, INPUT) ;
     pinMode(SW2_PIN, INPUT) ;
}
void loop()
{
     int pre_pin , now_pin ;
     long i , count ;
     
     // スイッチ1が押されたら処理する
     if (digitalRead(SW1_PIN) == LOW) {
          delay(2000) ;                 // これはSW1のチャタリング防止の為
          pre_pin = (PINB & 0x1) ;      // 現在のSW2の状態を読込み前回値とする
          count   = 0 ;
          Serial.print("Press the switch -> ") ;
          // 2000000回ループする間のSW2の変化回数をカウントする
          for (i=0 ; i < 2000000 ; i++) {
               now_pin = (PINB & 0x1) ; // 今回値SW2の状態を読込む
               if (pre_pin != now_pin) {// 前回値と今回値が異なったならカウントする
                    pre_pin = now_pin ; // 今回値を前回値とする
                    count++ ;
               }
          }
          Serial.println(count) ;       // カウント数を表示する
     }
}
---------------------------------------------------------------------
[SW2]が押された時のカウント値(A点)を見る操作

実行結果1  左図は[SW2]を押したり放したりした時の実行結果です。

 @ Arduino IDE のシリアルモニターを起動させます。

 A [SW1]を押します、2秒後に"Press the switch ->"が表示
   されます。

 B 表示されたらすぐに[SW2]を押します、但し、押しっぱなしに
   して置く。

 C "Press the switch ->"の横にカウント値が表示される。

 D [SW2]を放します。(以降はA〜Dを繰り返せば良いです)


実行結果2  上図の結果で0がカウントされているのは、[SW2]の場合、どうも
 押しても接点が接触していない場合が発生するみたいです。


 ちなみに左図の結果は[SW1]を動作させた時の内容です。
 ( [SW1]の方がチャタリングが少ないかチャタリング発生期間が
 短いのでしょう)


[SW2]を放した時のカウント値(A点)を見る操作

 @ Arduino IDE のシリアルモニターを起動させます。

 A [SW2]を押します、但し、押しっぱなしにして置く。

 B [SW1]を押します、2秒後に"Press the switch ->"が表示されます。

 C 表示されたらすぐに[SW2]を放します。

 D "Press the switch ->"の横にカウント値が表示される。(以降はA〜Dを繰り返せば良いです)



実行結3
 左の結果はC点でのチャタリング防止回路を通した後でのカウント結果です。

 [SW2]を押しても放してもカウント値は”1”でした。

 ”2”の値は"Press the switch ->"が表示された後にスイッチを押して
 直ぐに放した場合ですので2がカウントされている分けです。

 ここでまだ押した時や放した時に、カウント値が”1”にならない様であれば
 RC回路の時定数を長く調整して下さい、Cの定数を増やせば良いでしょう。
 尚、Rの定数を変更する場合は47KΩ以下で選定しましょう。


※ 他にRSフリップフロップ回路による除去も有るのですが、プッシュ型スイッチでは
   使用出来ない為に(トグルスイッチの様な2接点の物はOK)ここでは説明を省きます。

《Arduinoソフトウエアによるチャタリング除去》

ソフトウエアで防止する場合は、基本的にスイッチを有る間隔で読み込み、
2回以上同じ状態が続いたならその状態(ON又はOFF)とする様にプログラムを作成します。
間隔=ArduinoのMsTimer2機能により10ms間隔でスイッチの状態を見ています。
(尚、MsTimer2機能についての話はこちらの頁を見て下さい)

ソフトウエアによるチャタリング除去イメージ図 左図がイメージ図です、
例えばP2-P4点で3回見た場合スイッチの状態が
3回共異なるので、スイッチの状態はLOWのままと
します。
P3-P5点で見た場合は3回共同じなのでHIGHとします。


下のスケッチによるスイッチの配線が、上記のカウントスケッチと接続端子番号が異なります、
注意しましょう。(本当は合わせた方が良かったのですがぁ.....)

このスケッチでは[SW1]をデジタル2番端子に配線で、[SW2]をデジタル7番端子に繋いでいます、
(もちろんスイッチはプルアップ配線ですよ、念の為に、Arduino内蔵プルアップでも可)
ちなみに、RC回路とシュミットトリガ回路の配線は要りませんよ。

まずは、↓ここからArduino用のサンプルスケッチファイルをダウンロードして下さい。
Chattering1.lzh

ダウンロードファイルを解凍すると[Chattering]フォルダー内に下記ファイルが入っています。
Chattering.ino・・・・・・・・ スイッチのチャタリング除去メインのサンプルスケッチ
ScanSW.ino・・・・・・・・・・ スイッチのチャタリング除去読込みを行う関数用ライブラリ
ScanSW.h・・・・・・・・・・・・ スイッチのチャタリング除去読込みを行うライブラリのヘッダファイル

 @ 解凍ファイルの保存先は、ArduinoIDEを起動させ、メニューバーの[ファイル]→[環境設定]を
   クリックして、環境設定画面の"スケッチブックの保存場所"のフォルダーに上記解凍した
   ファイルのフォルダー(Chattering)ごと移動させます。

 A ArduinoIDEメニューバーの[ファイル]→[スケッチブック]→[Chattering]をクリックします、
   サンプルスケッチが読み込まれて表示されるでしょう。

 B コンパイルとArduino書き込みを行って下さい。

 C 正常終了したらスイッチを押して見て下さい、Arduino内蔵の13番LEDが点灯するはずです。

Chattering.ino

 [SW1][SW2]の何方を押してもLEDが点灯するだけですが、これによりスイッチを押した時の
 反応速度が判ると思います。

ScanSW.h

 今回のスケッチでは10ms毎にスイッチを読み込んでいます、
 で、3回読み込んで状態が同じならOKとしています、この3回を変更したい場合は、
 #define SW_COUNTER 3
 と宣言しているのでこの部分を変更して下さい。
 但しあんまり数値を増やせばスイッチの反応速度は遅くなりますよ。

ScanSW.ino

 本当はライブラリ化したかったのですが、その方がArduinoらしいので......
 でも、ライブラリのクラス内に MsTimer2::set(ms, ScanSW_Interrupt) ; を記述したけど、
 割り込み処理用のメンバー関数(ScanSW_Interrupt)のポインターが上手く渡せなくてエラーが出たの
 であきらめました。で、ライブラリファイルを一緒にコンパイルする方式になったのでした。
 なので、自分のスケッチが有れば、ScanSW.ino/ScanSW.hをその同じフォルダーに入れて利用しましょう。

 関数の説明をします。

 SW_Init(ms,inf)
  スイッチのチャタリング除去読込みをする為の初期化を行う処理

  ms : 割り込み関数を呼び出すタイミングの時間(ms)を指定する
      ここの指定でスイッチを見に行くタイミングを変えられます、今回は10msです。
      スイッチのチャタリング発生期間が長い場合はここの時間を長めにして見て下さい。
  inf : スイッチを接続している端子を指定する(但しDIGITAL2〜7までに繋ぐ事)
      このスケッチではDIGITAL2〜7までのMAX6個までしか対応していません。
      指定方法は、DIGITAL2と7に接続しているなら"B10000100"と記述します。
      ここで指定したピンはpinMode( )で入力にする必要は有りません、SW_Init( )で行っています。

  例) SW_Init(10,B10000100) ;

 SW_read(value)
  スイッチの状態を読み込む処理
  DIGITAL2に接続したスイッチを見る場合は、SW_read(2) とします。

  例) if (SW_read(2) == LOW) digitalWrite(13,HIGH) ;
     else             digitalWrite(13,LOW) ;

 ScanSW_Interrupt( )
  この関数は定期的にMsTimer2から起動され、スイッチの状態を調べる割込みの処理を行います。

《PICソフトウエアによるチャタリング除去》

サンプルプログラムは16F1827での記述で、プログラムはArduinoの場合と基本同じ処理です。
配線的には[SW1]をRB7端子に接続し、[SW2]をRB6端子に繋ぎ、LEDをRA2に配線しています。
(スイッチは外部にてプルアップ接続を行って下さい)
今回はポートBにスイッチを繋いでいますが、他のポートを利用する場合は、"ScanSW.h"ファイルの
#define SW_PIN PORTB // スイッチの接続をするポートの指定を行う
を変更して下さい。
PORTBを使うとPIC内蔵のプルアップを利用する事が出来るでしょう。
尚、利用する端子は"ANSEL"と"TRIS"レジスターでデジタルの入力で有ると確実に指定してください。

まずは、↓ここからPIC用のサンプルプログラムファイルをダウンロードして下さい。
Chattering2.lzh
Chattering2.lzh MPLAB X/XC8(V1.32)に対応 *1)

プログラムソースをダウンロードしたら、MPLAB X(v2.15)にてプロジェクトを作成します。
以下のファイルをプロジェクトディレクトリにコピーしてプロジェクトに取込んで下さい。
次にコンパイルPIC書き込みを実行して下さい。 *1)
MPLAB(R) XC8 C Compiler Version 1.32コンパイラを使用しています。

ダウンロードファイルを解凍すると下記の様なファイル構成です。
Chattering.c・・・・・・・ 16F1827用スイッチのチャタリング除去メインのサンプルスケッチ *1)
ScanSW.c・・・・・・・・・・ スイッチのチャタリング除去読込みを行う関数用ライブラリ
ScanSW.h・・・・・・・・・・ スイッチのチャタリング除去読込みを行うライブラリのヘッダファイル

※ "ScanSW.c"はタイマー2の機能が有れば他のPICでも利用出来ると思います。
※ コンパイル等を行うと、”COFF file が読めなくてデバッグは出来ませんよ”見たいな警告画面が
  出ますが、プログラムは正常に動作します、気になる人は自力で解決して下さいすみません m(_ _)m

Arduinoと異なる部分の関数のみ説明します。

 SW_Init(ms,inf)
  スイッチのチャタリング除去読込みをする為の初期化を行う処理
  CPUクロック8MHzで初期化されています。

  ms : 割り込み関数を呼び出すタイミングの時間を指定する
      ここの指定でスイッチを見に行くタイミングを変えられます、 5024us x ms(1〜16倍)。
      今回は ms=2 なので、5024us x 2 = 約10ms毎に割り込みが発生する。
      スイッチのチャタリング発生期間が長い場合はここの時間を長めにして見て下さい。
  inf : スイッチを接続している端子の位置(0-7)を指定する
      このプログラムでは0〜7bit位置までのMAX8個までしか対応していません。
      指定方法は、RB6/7に接続しているなら"0b11000000"と記述します。
      接続ポートの指定は"SW_PIN"で行います。

 ScanSW_Interrupt( )
  タイマー2から定期的に起動され、スイッチの状態を調べる処理です。
  なので、メインプログラム(例"Chattering.c")の割込み処理で必ず呼びます。
  例)
     void interrupt InterFunction( void )
     {
          // 定期的にスイッチの状態を読込みする処理
          ScanSW_Interrupt() ;
     }

《その他》

ハードウエアによる除去でも、ソフトウエアによる防止でもスイッチを動作させてから数10ms後に
マイコンが認識する事に注意して下さい。
高橋名人のように16連射を行わなければ大丈夫でしょう。
高速動作をさせたい場合は、RSフリップフロップ回路を使い2接点型の押しボタンスイッチを
利用すれば良いかもね。



リンク切れ見直し(*2) 2017/01/11
MPLAB X用に記事変更(*1) 2015/10/30


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2017 Shigehiro Kimura All Rights Reserved.