スイッチとチャタリング

 スイッチPA0が押された回数を数えるプログラムについて考えてみる。安直に考えると、以下のような感じ。

#include "monitor.h"

#define PA_DDR (*(volatile unsigned char *)0xFFFFD1)
#define PA_DR (*(volatile unsigned char *)0xFFFFD3)

int main()
{
    char sw;
    long int w;
    int cnt = 0;

    PA_DDR = 0x00;  // ポートAの全てのビットを入力に設定
    while (1)
    {
        sw = PA_DR & 0x01;   // bitのPA0のみを切りだす。(他のbitは全て0)
        if (sw == 0)  // bitが0だったら、スイッチPA0が押されたと判断
        {
            cnt++;
            printf("スイッチが押された回数=", cnt);
            for (w=0; w<999; w++);   // printfの表示が大量に出るのを防ぐためのwait
        }
    }
    return 0;
}

              [図 スイッチ(PA0)が押された回数を数える 例1]

 このプログラムも問題なく動作するが、PA0が押し続けられると、その間cntの値がカウントアップし続ける。PA0が押し続けられた場合でも、カウントは一回だけにしたい場合はどうすればよいか。

 一つの方法として、PA0が押された瞬間にカウントするのではなく、PA0が押された状態から離された瞬間にカウントする方法がある。例えば、以下の様なプログラムを動かしてみる。

#include "monitor.h"

#define PA_DDR (*(volatile unsigned char *)0xFFFFD1)
#define PA_DR (*(volatile unsigned char *)0xFFFFD3)

#define PUSHED     1
#define RELEASED   0

int main()
{
    char sw;
    int cnt = 0, flg = RELEASED;

    PA_DDR = 0x00;  // ポートAの全てのビットを入力に設定
    while (1)
    {
        sw = PA_DR & 0x01;   // bitのPA0のみを切りだす。(他のbitは全て0)
        if (sw == 0)   // bitが0だったら、PA0が押されたと判断
        {
            flg = PUSHED;   // PA0が押された事を保持する。
        }
        if ((sw == 1) && (flg == PUSHED))  // PA0が押された後、離された事を検出 
        {
            cnt++;
            printf("cnt=%d\n", cnt);
            flg = RELEASED;  // フラグをリセット
        }
    }
    return 0;
}

        [図 スイッチ(PA0)が押された回数を数える 例2]

 このプログラムを動かしてみると、概ね正常に動作する。しかし、PA0を何度も押して行くうちに、一回しかPA0を押していないのに複数回カウントされてしまったり、押した瞬間にカウントされてしまう事がある。

 この様な現象を、「チャタリング」と呼び、スイッチ入力を行う場合には、対策を施す必要がある。
 


■ チャタリングとはどのようなものか

 スイッチのON/OFFを行う時に、スイッチの接点に小さな火花が散る事がある。この火花が原因で、スイッチがごく短時間(数ミリ秒)に何度もON/OFFを繰り返す(したように見える)現象を、チャタリングと呼ぶ。

I/Oポートにスイッチが接続されていると、以下の図のように、火花が原因で入力値が短時間に1⇔0の変化を繰り返すことになる。 

対策として、チャタリングの発生は、ごく短時間の間しか発生しないので、この間はスイッチの状態変化を無視する方法がある。


 スイッチ操作を検出した直後から、数ミリ秒の間、waitを入れることで、この間にスイッチ操作が行われたとしても、それはチャタリングとみなして無視するようにプログラミングする。(人間の能力では、そんな短時間にスイッチ操作を行うことは出来ないので、この様な対応をしても、問題ない)

#include "monitor.h"

#define PA_DDR (*(volatile unsigned char *)0xFFFFD1)
#define PA_DR (*(volatile unsigned char *)0xFFFFD3)

#define PUSHED     1
#define RELEASED   0


int main()
{
    char sw;
    volatile long int w;
    int cnt = 0, flg = RELEASED;
 

    PA_DDR = 0x00;  // ポートAの全てのビットを入力に設定
    while (1)
    {
        sw = PA_DR & 0x01;   // bitのPA0のみを切りだす。(他のbitは全て0)
        if (sw == 0)   // bitが0だったら、スイッチPA0が押されたと判断
        {
            flg = PUSHED;
           for (w=0; w<9999; w++); // チャタリング対策のwaitを追加
        }
        if ((sw == 1) && (flg == PUSHED))
        {
            cnt++;
            printf("cnt=%d\n", cnt);
            flg = RELEASED;
           for (w=0; w<9999; w++); // チャタリング対策のwaitを追加
        }
    }
    return 0;
}

                                 [図 チャタリング対策を施した例]

最終更新:2014年10月07日 16:04