マイコンは電圧の高い、低いを検知したり、出力したりします。
一般的にスイッチはマイコンへの電圧を機械的にonしたり、offしたりするものです。
スイッチoffでは電源からマイコンへ流れ込むため、マイコンから見たときHighが見えます。
スイッチonでは電源は接地(GND)へ流れるため、マイコンから見たときLowが見えます。
プロモーションボードの回路図ではこんな風に記載されています。
さて、このスイッチ。実は困った問題を抱えています。
機械を操作すると振動が起こりますので、機械の微小震動:チャタリングといった現象が発生します。
チャタリングの回避には
1.抵抗とコンデンサを利用してチャタリングを除去する。
2.ソフトウェアで2度読みして、2回とも同じ値(電圧)ならこれを採用する。
があります。
1.抵抗とコンデンサ
抵抗とコンデンサは時定数τ=CRの関係があります。つまり、電圧変動を時定数をもってチャタリングを平滑化する方式です。こちらの問題は部品点数が多くなり、コストが上昇することと、時定数が大きくなることによって電圧の応答が遅くなってしまうことです。
2.ソフトウェアの2度読み
一方、ソフトウェアでは2度読みを行います。
電圧がふらついている時に読み取りに行くと、1回目はHigh、2回目はLowだとすると、「これはチャタリングだな」と認識し、採用しません。
1回目がHigh、2回目もHigh、もしくは1回目・2回目ともLowであった時にスイッチが離された、もしくはスイッチが押されたと認識します。
こちらの欠点はチャタリング期間中に2度読みしても効果はないので、チャタリングがどのくらいの時間続くのかを把握する必要があります。
しかし、実際には測定することは少なく、数ms~10msくらいの間隔で2度読みをしていると思います。
先ほどの回路図を見ると、プロモーションボードにはコンデンサはついていないので、チャタリングの除去はソフトウェアで行う必要があります。
数msに一度呼ばれる関数を想定してみます。
/********** check sw status **********/ void SwCheck(void) { static unsigned char sw_edge; static unsigned char sw_level; static unsigned char sw_status; sw_edge = sw_level ^ port; sw_level = port; if(sw_edge && sw_level){ /* 立ち上りエッジ検出 */ sw_status = ON; }else{ sw_status = OFF; } }
C言語における「static」は自動変数としないことを明示するものです。一般に関数内で定義された変数は自動変数として扱われるため、関数が終了した時点で参照できなくなりすし、値も保存されません。しかし「static」を指示しておくと値が保存されます。これで過去の状態を比較できるようになります。
「^」は排他的論理和を意味するので、sw_levelとportの組み合わせが「0,1」「1,0」の時のみsw_edgeが1になり、その他は0になります。これで立ち上がり、もしくは立ち下がりを検出します。
例では立ち上がりを検出した時にsw有効としていますが、実際は稼働するシステムに合わせます。