TNJ-017:スイッチ読み出しでのチャタリング防止の3種類のアプローチ
2016年1月6日公開
はじめに
「スイッチのチャタリングはアナログ的振る舞いか?デジタル的振る舞いか?」ということで、アナログ・チックだろうという考えのもと技術ノートの話題としてみます(「メカ的だろう!」と言われると進めなくなりますので…ご容赦を…)。
さてこの技術ノートでは、スイッチのチャタリング対策(「チャタ取り」とも呼ばれる)について、電子回路の超初級ネタではありますが、デジタル回路、マイコンによるソフトウェア、そしてCR回路によるものと、3種類を綴ってみたいと思います。
チャタリングのようすとは?
まずは最初に、チャタリングの発生しているようすをオシロスコープで観測してみましたので、これを図1にご紹介します。こんなふうにバタバタと変化します。チャタリングは英語で「Chattering」と書きますが、この動詞である「Chatter」は「ぺちゃくちゃしゃべる。〈鳥が〉けたたましく鳴く。〈サルが〉キャッキャッと鳴く。〈歯・機械などが〉ガチガチ[ガタガタ]音を立てる」という意味です(weblio辞書より)。そういえばいろんなところでChatterを聞くなあ…(笑)。

図1. スイッチのチャタリングが発生しているようす
(横軸は100us/DIV)
先鋒はRTL(デジタル回路)
余談ですが、エンジニア駆け出し4年目位のときに7kゲートのゲートアレーを設計しました。ここで外部からの入力信号のストローブ設計を間違えて、バグを出してしまいました…(汗)。外部からの入力信号が非同期で、それの処理を忘れたというところです。チャタリングと似たような原因でありました。ESチェックで分かったのでよかったのですが、ゲートアレー自体は作り直しでした。中はほぼ完ぺきでしたが、がっくりでした。外部とのI/Fは(非同期ゆえ)難しいです(汗)…。
当時はFPGAでプロトタイプを設計し(ICはXC2000!)、回路図(紙)渡しで作りました。テスト・ベクタは業者さんに1か月入り込んで、そこのエンジニアの方と一緒にワーク・ステーションの前で作り込みました。その会社の偉い方がやってきて、私を社外の人と思わず、私の肩に手をやり「あれ?誰だれ君はどした?」と聞いてきたりした楽しい思い出です(笑)。

図2. スイッチのチャタリング防止を
FPGA上でRTLでやってみた
それでは本題に…
閑話休題ということで…。さて、図2の上側の7セグメントLEDの表示を変えるべく(というより内部動作パラメータをスイッチで操作すべく)、下側のスイッチで設定するためのRTL(VHDL)を書きました。
図3にこのRTLを示します。チャタリング対策のアルゴリズム(アプローチ)も多岐の方法論があろうかと思いますが、一例としてご覧ください。TNJ-016でも書きましたが、Verilog が書けないので、VHDLです。一応これで動いています。
応用される場合は、必要に応じてカウンタにtoggle enableを追加してstrobeを入れるなど、カウンタのビット数が無用に伸びないようにしてください(この回路のクロックは低速になっているので、カウンタは5ビットで済んでいます)。マイコンのソフトより論理回路のほうがタイミング(時間)予測が簡単だから楽ですね(マイコンで割り込み処理すれば別ですが)。
architecture Behavioral of SwChat is
signal LastSwState : std_logic;
signal CurrentSwState : std_logic;
signal DurationCounter : std_logic_vector(4
downto 0);
begin
process(CLK, RESET) begin
if (RESET = '1') then
LastSwState <= '0';
CurrentSwState <= '0';
elsif (CLK'event and CLK = '1') then
LastSwState <= CurrentSwState;
CurrentSwState <= SwitchIn;
end if;
end process;
process(CLK, RESET) begin
if (RESET = '1') then
DurationCounter <= "00000";
elsif (CLK'event and CLK = '1') then
if ((LastSwState xor CurrentSwState) = '1')
then
DurationCounter <= "00000";
elsif (DurationCounter /= "11111") then
DurationCounter <= DurationCounter + 1;
end if;
end if;
end process;
process(CLK, RESET) begin
if (RESET = '1') then
RiseEdge <= '0';
FallEdge <= '0';
elsif (CLK'event and CLK = '1') then
if (DurationCounter = "11110") then
if (LastSwState = '1') then
RiseEdge <= '1';
FallEdge <= '0';
else
RiseEdge <= '0';
FallEdge <= '1';
end if;
else
RiseEdge <= '0';
FallEdge <= '0';
end if;
end if;
end process;
end Behavioral;
図3. VHDLで書いたチャタリング対策回路のRTL
簡単に動作説明
LastSwStateとCurrentSwStateは1クロックごとに読んだ、入力ポートの状態履歴です。これを赤字で示した部分のようにxorすると、同じ状態(チャタっていない)であれば結果はfalse (0)になり、異なっている状態(チャタっている)であれば結果はtrue (1)になります。
チャタっている状態を検出したらカウンタ(DurationCounter)をクリアし、継続しているのであればカウントを継続します。このカウンタは最大値で停止します。
その最大値ひとつ前のカウント値になるときにLastSwStateが0であるか1であるかにより、スイッチが押された状態が検出されたか、スイッチから手を離した状態が検出されたかを判断し、それによりRiseEdge, FallEdgeをアサートします。なお本質論とすれば、スイッチの状態とRiseEdge, FallEdgeのどちらがアサートされるかについては、スイッチ回路の設計に依存しますが…。
メタステーブル(準安定)はデジタル回路でのアナログ的ふるまいだ!
やれやれ出来たぞ、と思っていたところ、「この前段に気分的にメタ・ステーブル殺しを入れたくなりますね」というコメントをいただきました。
もともとFPGA/RTLからのスタートでしたので「技術ノートにするにも、デジタル・ネタだなあ」と後悔ぎみでありましたが、「メタ殺し」という、デジタル回路に生じるアナログ的現象である「メタ・ステーブル」についてのコメントでありました。こういう進展するとは思いもせず、ただただ嬉しく思いました。
メタステーブル(準安定)はセットアップ・ホールドが満足できないために生じる、中間レベルが「ある時間」維持される現象です。調べてみるとwikipediaには記述が無いようです。自分も図3のソースでは(これをミーリ・マシン/ムーア・マシンと言っていいのか判りませんが)入力をワン・ライン(SwitchIn)のみとしており、複数のF/Fで準安定による誤動作が生じないように
としてF/Fでクロッキングしてあります。このSwitchInが外部からのポートです。CurrentSwStateでメタ・ステーブル(準安定)が発生することがありますが、システム・クロックも低速ゆえ、時間が経ってメタ・ステーブルが消滅する可能性が高いことから、1段いれておけば大丈夫だろう、という考え方で「メタ殺し」設計してあります。CurrentSwState <= SwitchIn;
中堅はC言語(マイコンでのソフトウェア)
余談ですが先日、立川市のお客様のところにお邪魔しました。車両での移動でしたが、立川飛行場跡地前を通りすぎました。
私は大学生だったころ、それは昭和60年の夏、この「タチヒ(立飛)地区」にあった会社にソフトウェア・エンジニア(Z80のアセンブラ)としてバイトに来ていたのでした。たしかADコンバータの値をZ80マイコンで取り込み、それをPC9801とI/Fし(RS232Cだったか?今は記憶が無いのですが8251などのSCIは使わなかったはず…。PC98 BUSだったかも?)、さらにそれをN88 BASICで画面表示させ、HP-GLでプロッタにプロットするというものでした。当然デバッガなども無く、いきなりオブジェクトをEPROMに焼いて確認という開発スタイルでした。
それは大学4年生として最後の夏休みの1.5か月程度のバイトでした。昼休み時間には青い空の下で、若手社員さんから仕事の大変さについて教わっていたものでした…。
今回そのお客様訪問後に、このことを思い出し、ネットでサーチしてみると(会社名さえ忘れかけていました)、今は違うところで会社を営業されていることを見つけ、私の設計したソフトが応用されている装置も「Web歴史展示館」上に展示されているものを見つけることができました(感動の涙)。
それではここでも本題に…
またまた閑話休題ということで…。図 4はマイコンを利用した回路基板です。これらの設定スイッチが正しく動くようにC言語でチャタリング防止機能を書きました。これも一応これで問題なく動いています。
ソースコードを図5に示します。こちらもチャタリング対策のアプローチとしても、多岐の方法論があろうかと思いますが、一例としてご覧ください(汗)。

図4. こんなマイコン回路基板のスイッチのチャタリング
防止をC言語でやってみた
// 5 switches from PE2 to PE6
swithchstate = (PINE & 0x7c);
// wait for starting switch
if (switchcount < 1000) {
if (swithchstate == 0x7c) {
// switch not pressed
switchcount = 0;
lastswithchstate = swithchstate;
}
else if (swithchstate != lastswithchstate) {
switchcount = 0;
lastswithchstate = swithchstate;
}
else {
// same key is being pressed
switchcount++;
}
}
// Perform requested operation
if (switchcount == 1000) {
※ ここで「スイッチが規定状態に達した」として、目的の
動作をさせる処理を追加 ※
// wait for ending of switch press
switchcount = 0;
while (switchcount < 1000) {
if ((PINE & 0x7c) != 0x7c) {
// some of switches are still pressed
switchcount = 0;
}
else {
switchcount++;
}
}
// return
switchcount = 0;
図5. C言語で書いたチャタリング対策ソース
簡単に動作説明
良く見てみると、この図5のCコードも図3のRTLコードと考え方はほとんど同じです。この場合は5つのスイッチがPort EのB2~B6に接続されており、全てのスイッチはプルアップされています。スイッチが押されると各ビットが0になります。スイッチが押されていない状態であれば、
swithchstate == 0x7c
がtrueとなり、図3のRTLではDurationCounterとなっているswitchcount(intです)がゼロになります。チャタリングが起きればこれがゼロに戻りますので、チャタリングによる誤動作を防止できます。
どこかのスイッチを押しているとswitchcountがメイン・ループ1周ごとにカウントアップしていきます。switchcountは(10進で)1000になるまでカウントアップし、1000になったときに目的のスイッチ動作を行います。
これは簡易な動作用のソースですが、「複数のスイッチが同時に押されたときにはどう動くのだ?」というソフトウェア設計レビュー・ミーティング的な突っ込みにもelse ifのあとで対応しています、いや、いるつもりです(笑)。
大将はやっぱりCR回路(アナログ回路)
図6も複数の設定スイッチがあるデジタル回路基板です。このデジタル回路基板はFPGAもマイコンもない基板で、HCMOSでアクロバティックなシーケンサ回路を実現している(という自己満足?的な)基板です。HCMOSが多数見えるところかと思います…。
ということでデジタル信号処理やソフトウェア的なチャタリング対策が施せませんので、ここでは図7のような回路にして、CR回路とシュミット・トリガ・インバータ74HC14を用いたかたちで実現してみました。
チャタリング取りはCR回路により実現します。時定数は後でも詳しく説明しますが、約0.1secです。この時定数で波形が大きく鈍りますので、それを安定に検出するためにシュミット・トリガ・インバータ74HC14を用いています。
74HC16xのカウンタは同期回路の神髄が詰まったもの
この回路でスイッチを押すと、74HC16xのカウンタを使った自己満足的なシーケンサ回路が動作し、デジタル信号波形のタイミングが変化していきます。波形をオシロで観測しながらスイッチを押していくと、波形のタイミングがきちんとずれていくようすを確認することができました。
74HC16xとシーケンサと聞いてピーンと来たという方は、「いぶし銀のデジタル回路設計者」の方と拝察いたします。74HC16xは、同期シーケンサの基礎技術がスマートに、煮詰まったかたちで詰め込まれ、応用されているHCMOS ICなのであります。動作を解説するだけでも同期回路の神髄に触れることもできると思いますし(半日説明できるかも)、いろいろなシーケンス回路も実現できます。
不適切だったことは後から気が付く!
「やれやれ出来たぞ」というところでしたが、基板が完成して数か月してから気が付きました。使用したチャタリング防止用コンデンサは1uFということで容量が大きめでありますが、電源が入ってスイッチがオフである「チャージ状態」では、コンデンサ(図7ではC15/C16)は5Vになっています。これで電源スイッチを切ると74HC14の電源電圧が低下し、ICの入力端子より「チャージ状態」のC15/C16の電圧が高くなってしまいます。ここからIC内部のダイオードを通して入力端子に電流が流れてしまい、ICが劣化するとか、最悪ラッチアップが生じてしまう危険性があります。
ということで、本来であればこのC15/C16と74HC14の入力端子間には1kΩ程度で電流制限抵抗をつけておくべきでありました…(汗)。この基板は枚数も大量に作るものではなかったので、このままにしておきましたが…。

図6. 複数の設定スイッチのある回路基板の
チャタリング防止をCR回路でやってみた

図7. 図6の基板のCR回路によるチャタリング防止
(気づくのが遅かったがC15/C16と74HC14の間には
ラッチアップ防止の抵抗を直列に入れるべきであった!)
回路の動作をオシロスコープで一応確認してみる
図7の回路では100kΩ(R2/R4)と1uF(C15/C16)が支配的な時定数要因になっています。スイッチがオンしてコンデンサから電流が流れ出る(放電)ときは、時定数は100kΩ×1uFになります。スイッチが開放されてコンデンサに電流が充電するときは、時定数は(100kΩ + 4.7kΩ)×1uFになりますが、ほぼ放電時の時定数と同じと考えることができます。
図8にスイッチが押されたときの74HC14の入力端子(コンデンサの放電波形)と同出力端子(シュミット・トリガでヒステリシスを持ったかたちでLからHになる)の波形のようすを示します。
また図9にスイッチが開放されたときの74HC14の入力端子(コンデンサの再充電波形)と同出力端子(シュミット・トリガでヒステリシスを持ったかたちでHからLになる)の波形のようすを示します。このときは時定数としては(100kΩ + 4.7kΩ)×1ufということで、先に示したとおりですが、4.7%の違いなのでほぼ判別することはできません。

図8. 図6の基板でスイッチを押したときのCR回路の
放電のようすと74HC14出力(時定数は100kΩ×1uFになる。横軸は50ms/DIV)

図9. 図6の基板でスイッチを開放したときのCR回路の
充電のようすと74HC14出力(時定数は104.7kΩ×1uFに
なるが4.7%の違いなのでほぼ判別できない。横軸は50ms/DIV)
著者について
デジタル回路(FPGAやASIC)からアナログ、高周波回路まで多...
この記事に関して
資料
{{modalTitle}}
{{modalDescription}}
{{dropdownTitle}}
- {{defaultSelectedText}} {{#each projectNames}}
- {{name}} {{/each}} {{#if newProjectText}}
-
{{newProjectText}}
{{/if}}
{{newProjectTitle}}
{{projectNameErrorText}}