HOME > 技術談話 > PIC® microcontroller > スイッチの入力
はじめに チャタリング ハードウェアによるチャタリング除去 ソフトウェアによるチャタリング除去
回路図 動作 フローチャート プログラム補足
ユーザインタフェースには欠かせないスイッチを入力するプログラムを作成します。 極端に言えば入力ポートからスイッチの状態を読み出せば済むのですが…。 幾つかの注意事項があるのでスイッチ入力に関して本項でまとめて説明します。 |
▲ |
メカニカルなスイッチをオフからオン、オンからオフした際にスイッチの接点が短時間でオン、オフを繰り返す現象がチャタリングです。人が1秒間に10回スイッチ操作を繰り返す。この場合約100ms周期でスイッチ操作を行うことになりますが、チャタリングの場合は更に短い周期でスイッチ操作を操作したかの様に見えます。 試しに実験用の回路を作成し、スイッチ操作に伴う信号の状態をオシロスコープで測定してみました。 |
この波形はスイッチをオフした瞬間をトリガとし、記録結果を部分拡大しています。 このケースではスイッチオフが確定する1ms手前でチャタリングが発生しています。 見た目はノイズの様で発生時期、信号幅は不定です。 |
メカニカルなスイッチだからと言って必ずチャタリングが発生する…そうでもありません。どちらかと言うとオン、オフの一方だけが発生しやすい。全く出ない。オン、オフの双方で発生しやすい等様々です。 現物のスイッチの挙動を調べたらチャタリングが発生しない。この判断は誤りです。もしかしたら調査した1点だけが優れ物かもしれません。また、今は大丈夫だけど経年変化でチャタリングが発生しやすくなる場合もあります。基本は部品を選定した人にチャタリングがどのように発生するのかを聞いてみるのが一番です。 いずれにせよ、メカニカルスイッチに限らず、スイッチを利用する場合はチャタリングが発生するものだ。というという意識でプログラムを作成すれば誤動作を抑止することができます。 |
▲ |
スイッチの変化の過程をハードウェアフィルタにより綺麗な信号に生成することができます。 たとえば、こんな感じの回路(ここはさらりと…)。 |
左側のスイッチの部分は同じです。 中央はローパスフィルターと呼ばれる回路で抵抗とコンデンサで構成されます。呼び名の通り高い周波数を遮断し、低い周波数の信号だけを通過させます。 右側はシュミットトリガーと呼ばれる回路です。たいていは74HC14等のIC(集積回路)を利用します。 |
ローパスフィルターはどちらかというと信号を「鈍らせる」イメージです。短い幅の信号は吸収され、長い幅の信号は「じわじわ」と出力側に伝達します。この「じわじわ」という信号の変化が曲者で信号の変化が急峻ではありません(徐々に電圧が上がったり下がったりするイメージ)。 「じわじわ」と変化する状態は電圧レベルからするとオンでもなく、オフでもない中途半端な状態。変化の過程が数十~数百usレベルで一時的に生じます。受ける側は中途半端な状態をどのように判断するか迷うわけで…。 その様な信号を扱う場合にシュミットトリガーを利用します。シュミットトリガーは「じわじわ」と変化する信号を急峻な変化を持つ信号に変換します。 変換に当たっては入力電圧で判断が行われます。xxV以上の電圧であれば出力はH、yyV以下の電圧であれば出力はL。この時、xx = yy ではなく、xx > yy となる様な電圧を利用します。入力する電圧が少し位ふらついても出力に影響を与えないようヒステリシスを持たせています。 ざっと説明しましたが、ハードウェアでチャタリング除去を行う場合には複数の部品が必要です。また、ローパスフィルターで使用する抵抗とコンデンサの定数選定も重要で、定数を誤るとスイッチの状態を正しく読み取れないばかりか、部品を交換しなくてはなりません。 なお、外部割り込み要因としてスイッチを利用する場合、マイコンにリセットを与える場合等はハードウェアによるチャタリング除去は必要です。時と場合により本方式が優先的に採用される場合があります。 |
▲ |
特別なハードウェアを用いずにチャタリング除去を行います。この場合の道具(?)は時間です。 前述のオシロスコープの測定結果を見るとわかりやすいのですが、スイッチの変化に伴うチャタリングは継続して発生することは無く、状態確定の前で発生します。今回のケースでは一発の大きな信号が1ms手前に現れましたが、たいていの場合、数十msの範囲に収まります。 チャタリングが発生する環境下では信号が変化してから数msの情報はあてにできません。そこで信号の状態を定期的に取得し、複数回同じ状態が得られたならば真とする。このルールでチャタリング除去を行います。 例えば、1ms周期で信号を取得し、3回一致で真とするチャタリング除去は以下の様になります。 |
処理タイミングと一致検出回数の決定は難しいかもしれません。処理タイミングが短ければ処理全体に占めるスイッチ処理の割合が増加しますし、一致検出回数が多すぎるとスイッチの反応が鈍くなります。このあたりは現物合わせになりそうですが、基本はハードウェア担当者に問い合わせる。でしょうか。 実験用でスイッチを使用する際、私がよく使用する組み合わせは10ms周期3回一致です。普通の人(ゲーマーでは無い)がスイッチを連打しても処理が追従し、かつチャタリング除去が成立します。このあたりは処理タイミングの生成方法等で変わってきますので、各自で流儀を作るのも良いと思います。 |
▲ |
前回の回路図が利用されます(クリックすると新しいウィンドウが開きます)。 今回のプログラムではソフトウェアによるチャタリング除去を行うので必要な部品は少ないです。PICマイコン内蔵のプルアップ(物理的な抵抗素子では無い)を利用するのでスイッチ入力に関してはスイッチしか実装されていません。 |
▲ |
スイッチのオンエッジを検出し、その数をLEDに2進数で反映します。 部品配置に依存するのですが、左側のスイッチ(SW0)がUp、右側のスイッチ(SW1)がDownです。 LEDの表示は2進数で左側(LED0)がLSBになります。演算の都合ですが、7の次は0、0の前は7になります。 理想動作はスイッチをオンする度に表示内容が1つづつ変化するはずですが、実際はどうなるでしょう? 012 … LEDx ●●● … 0 ●●● … 1 ●●● … 2 ●●● … 3 ●●● … 4 ●●● … 5 ●●● … 6 ●●● … 7 ●:点灯、●:消灯 |
▲ |
基本的な流れはこれまでに紹介したLED制御プログラムと同様です。今回は定周期でスイッチの状態を取得し、チャタリング除去を行った結果を利用してLEDを制御します。 ソフトウェアでチャタリング除去を行う際に必要な情報は、 ・診断中のスイッチの状態 ・確定済みのスイッチの状態 ・一致検出回数 です。スイッチの数、一致検出回数を少なくすればこれらの情報を1バイトに集約することもできます。今回は総て1バイトの領域で定義しているので、同時に診断可能なスイッチは8個、一致検出回数は255回まで対応することができます。 冒頭でスイッチの状態を取得し、チャタリング除去のための環境を整えます。今回はスイッチのオンを検出する為に最初の状態を前回状態として保持しています。 タイマはこれまでと同様、タイマ0を利用し、タイムアウト時間は512usとします。時間としては短いのですが、このあたりは他の定期的に行う処理、例えば1ms周期でも10ms周期でも大丈夫です。 スイッチの現在の状態を取得したのち、チャタリング除去を行います。スイッチの現在の状態とチャタリング除去後の確定状態は同じ変数に入れているので、後者の処理をコメントアウトすることでチャタリング除去を行わない場合の実験を行うことができます。 スイッチのオンエッジの検出は論理演算で行うと高速に処理することができます。演算式は、「前回状態 xor 今回状態 and 今回状態」です。演算結果で「1」を得られたビットは今回オンになった箇所に相当します。 今回オンされた箇所に基づき、LED表示用のデータを更新します。左側のスイッチ(SW0)で+1、右側のスイッチ(SW1)で-1します。 LED表示用のデータは下位3ビットがそのままLED表示用のデータとして使用されます。 |
チャタリング除去用の情報を初期設定します。 装置起動時のスイッチの状態は安定していると判断されるので、現在のスイッチ状態をそのまま確定状態としています。 なお、初期状態の情報をチャタリング除去してから実運用を始める方法でも問題はありません。 |
スイッチの状態は入出力ポートを読み出すだけで完了します。 ハードウェアの構成によりスイッチオンで「L」になるケースが多いです。ソフトウェアで処理する際に混乱する場合があるので、私の場合はスイッチオンで「H」になる様に論理を反転しています。また、情報として意味を持たないビットは0でクリアします。 |
ソフトウェアによるチャタリング除去処理の本体です。 与えられたスイッチの状態が前回と同じか否かを判定し、同じであるならば一致回数を更新して規定回数以上、同じ状態が継続すればスイッチの状態を確定します。確定するまでの間は過去に確定した状態を上位関数に返すようにしています。 一致回数の更新に制限を設けるのは任意です。少なくとも規定回数以上経過した時にスイッチの状態を確定する構成であれば「現在安定待ち」の判定は不要です。更新がオーバーフローしても問題ありません。 なお、チャタリング除去の過程でスイッチ変化をイベントとして返すのであれば規定回数以上の判定はスイッチ変化後の1回である必要があり、その場合は更新前に判定することは意味があります。 変数を参照してスッキリするのは…必要以上に回数を更新しないことを勧めます。このあたりはプログラマの趣味によるところが多いと思いますが、トリッキーな使い方をする場合は、その副作用を十二分に検証する必要があります。 与えられたスイッチの状態が前回と異なる場合は新たな検出を開始する為に検出回数をリセットします。ここで0をセットしてもよいのですが、前述の判定方法により1が好ましい場合があります。お互いが整合していないと結果的に一致検出回数が多くなる不具合が発生するので注意してください。 |
▲ |
(1)プログラム本体
サンプルプログラムを公開します … Test104_SW001.zip HI-TECH C v9.70 で作成されたプログラムを v9.81 以降のコンパイラで再コンパイルする際の注意点 → 開発環境による修正 2011/03/09 プログラムには特別な記述はありません。コメントを見るだけでも大まかに理解できると思いますが、気になりそうな箇所だけをピックアップして補足説明します。 |
(2)プログラムの修正
プログラムを修正することで以下の実験を行うことができます。 ・チャタリング除去回数の指定 … 現在5回一致で判定、main.c:92行目を修正 ・チャタリング除去の実行有無 … 現在チャタリング除去有効、main.c:187行目を修正 チャタリング除去回数を増やした場合どうなるでしょうか? 今回のサンプルプログラムではチャタリング除去回数に最大255を指定できます。定周期の時間が512usなので、結果としてスイッチの状態確定を最大約130msまで延長することができます。 サンプルプログラムではオンエッジを検出しているので、オン、オフ、オンの3つの状態を検出するまでに最短約260ms要します。実際に試してみるとスイッチを素早く連続押下しても反応が無く、ゆっくりスイッチを操作するとLEDが反応する結果が得られます。 チャタリング除去を無効にした場合はどうなるでしょうか? 入力ポートの状態がそのまま反映されるので、チャタリングを見つけた途端(上手くヒットするのが難しいですが…)、LEDの表示が連続して更新されます。 |
(2)スイッチの一括操作
今回のサンプルプログラムでは一度に2種のスイッチのチャタリング除去(最大8個同時まで可能)を行っていますが、実運用では通用しない場合が多々あります。 装置の構成に依りますが、1つの装置に使用されるスイッチは設置場所により異なる形状、型番を使用することが多く、それに伴いチャタリングの発生状況も異なります。また、スイッチの同時押下操作が含まれる事もあります。このような場合はスイッチ毎にチャタリング除去を行った方が良い結果を得られる場合が多いです。 今回のサンプルプログラムでは唯一のチャタリング除去用の情報を直接参照していますが、スイッチ毎にチャタリング除去用の処理を分けるか、同情報をアドレスで渡す等の工夫が必要です。 |
(3)ショートピンやDIPスイッチの扱い
装置の動作条件を設定する為にショートピンやDIPスイッチを利用することがあります。通常であれば装置電源がオフの状態で設定しますから運用中に切り替わることがありません。この仕様であればスイッチの状態を読み取る際にチャタリング除去は不要です。 非常にまれなケースですが、これらのスイッチを運用中に操作する仕様が提示された場合は注意が必要です。ただでさえ切り替えが面倒なスイッチですから、スイッチ切り替えに多くのチャタリングが伴います。このような場合は一致検出回数を時間換算で1秒程度確保した方が良い結果が得られます。操作しづらいスイッチを扱うのですからチャタリング除去に時間を要しても苦情は少ないです。 |
(4)ショーティングとノンショーティング
構造的に1つのスイッチで複数のオンを得ることのできるスイッチではスイッチの振る舞いに注意します。 |
以下はノンショーティングの例です。ABからBCに切り替わる際に双方オフの状態が生じます。 |
以下はショーティングの例です。ABからBCに切り替わる際に双方オンの状態が生じます。 |
スイッチの入力~チャタリング除去、チャタリング除去~スイッチ処理のどちらでも状態を認識することができますが、少なくとも上図を例にとれば4通りの状態(Aオン、Cオン、ACオン、総てオフ)がありえると言うことを前提に処理を作成する必要があります。 |
(5)オンエッジ、オフエッジの論理演算による検出
これは論理演算の応用なのでさらりと。 スイッチの状態はオン、オフの2通りです。オンを 1、オフを 0 とし、結果を 1 で得る場合の真理値表は、 |
オン検出 | オフ検出 | ||||||
前回 | 今回 | 結果 | 前回 | 今回 | 結果 | ||
0 | 0 | 0 | 0 | 0 | 0 | ||
0 | 1 | 1 | 0 | 1 | 0 | ||
1 | 0 | 0 | 1 | 0 | 1 | ||
1 | 1 | 0 | 1 | 1 | 0 |
となります。 これを論理演算で算出する場合は、 ・オンエッジ検出 … (前回 xor 今回) and 今回 ・オフエッジ検出 … (前回 xor 今回) and 前回 で得ることができます。 変数のビット幅にもよりますが、バイト型変数であれば一度に8つのスイッチの状態を演算することができます。 |
▲ |