チャタリングとは?スイッチ入力が誤動作する原因とデバウンス対策を解説

組み込み基礎

マイコンにボタンやスイッチを接続すると、「1回しか押していないのに何度も押されたことになる」という現象が起こることがあります。

その原因のひとつがチャタリングです。

チャタリングは機械式スイッチの接点が物理的に振動することで、短時間に何度もON・OFFを繰り返してしまう現象です。

この記事では、チャタリングが発生する理由から、ソフトウェアによるデバウンス、RC回路を使ったハードウェア対策、GPIO割り込みを使用するときの注意点まで解説します。

チャタリングとは?

チャタリング(Chattering)とは、機械式のスイッチやボタンを押したり離したりした瞬間に、接点が短時間に何度もON・OFFを繰り返す現象です。

人間から見るとボタンを1回押しただけですが、マイコンから見ると次のような入力として検出されることがあります。

HIGH
  ↓
LOW
  ↓
HIGH
  ↓
LOW
  ↓
HIGH
  ↓
LOW

つまり、1回の操作が複数回の入力として認識される可能性があります。

チャタリングの発生波形と理想的なスイッチ入力、デバウンス後の波形、ソフトウェアとRC回路によるチャタリング対策を示した図
チャタリングの発生原理と、ソフトウェア・ハードウェアによるデバウンス対策

上図①のように、実際の機械式スイッチでは入力が短時間に何度もHIGH・LOWを繰り返すことがあります。これを図②・③のような安定した1回の入力として扱えるようにする処理をデバウンスと呼びます。

なぜチャタリングが発生するの?

チャタリングが発生する原因は、機械式スイッチの構造にあります。

スイッチ内部には金属製の接点があり、ボタンを押すと接点同士が接触します。

しかし、接点は理想的に一瞬で静止するわけではありません。接触した瞬間に機械的な振動や跳ね返りが発生し、短時間のうちに接触と非接触を繰り返すことがあります。

この物理的な動きが電気信号として現れるのがチャタリングです。

チャタリングすると何が問題になる?

たとえばボタンを押すたびにカウンタを1増やすプログラムを考えてみましょう。

if (digitalRead(BUTTON_PIN) == LOW) {
  count++;
}

人間は1回しかボタンを押していなくても、チャタリングによって複数回の入力変化が発生すると、意図せずカウンタが複数回増える可能性があります。

同様に、次のような処理でも問題になります。

  • ボタンを押すたびにLEDをON/OFFする
  • メニューを1項目ずつ移動する
  • モードを切り替える
  • 入力回数をカウントする
  • GPIO割り込みを発生させる

デバウンスとは?

チャタリングの影響を取り除き、1回のスイッチ操作を1回の入力として扱えるようにすることをデバウンス(Debounce)と呼びます。

デバウンスには、大きく分けて2つの方法があります。

  • ソフトウェアで対策する
  • ハードウェアで対策する

簡単なボタン入力ではソフトウェアによる対策が使いやすく、用途によっては抵抗やコンデンサなどを使ったハードウェア対策も利用します。

方法1:delayを使った簡単なデバウンス

初心者にも分かりやすい方法が、入力が変化したあと少し待ってからもう一度状態を確認する方法です。

const int BUTTON_PIN = 21;

int lastState = HIGH;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {
  int state = digitalRead(BUTTON_PIN);

  if (state != lastState) {

    delay(50);

    state = digitalRead(BUTTON_PIN);

    if (state != lastState) {

      lastState = state;

      if (state == LOW) {
        Serial.println("ボタンが押されました");
      }
    }
  }
}

入力の変化を検出した直後には処理せず、ここでは50ms待ってから再度入力を確認しています。

チャタリングが収まったあとも同じ状態であれば、本当に入力が変化したと判断します。

ただし、必要な待ち時間はスイッチや回路によって異なります。50msはあくまで説明用の例です。

delayを使う方法の問題点

delay()を使った方法は理解しやすい反面、待っている間は通常のプログラム処理を進めにくくなります。

LEDを点滅させたり、通信処理を行ったり、複数の入力を監視したりするプログラムでは、ボタンのためだけに処理を止めたくない場合があります。

そのような場合は、millis()などを使って経過時間を確認する方法が便利です。

方法2:millisを使ったデバウンス

millis()を利用すると、プログラム全体を停止させずに一定時間が経過したかを確認できます。

const int BUTTON_PIN = 21;

int lastReading = HIGH;
int buttonState = HIGH;

unsigned long lastChangeTime = 0;
const unsigned long debounceTime = 50;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {

  int reading = digitalRead(BUTTON_PIN);

  if (reading != lastReading) {
    lastChangeTime = millis();
  }

  if ((millis() - lastChangeTime) > debounceTime) {

    if (reading != buttonState) {

      buttonState = reading;

      if (buttonState == LOW) {
        Serial.println("ボタンが押されました");
      }
    }
  }

  lastReading = reading;
}

この方法では、入力が変化してから一定時間同じ状態が続いた場合に、正式な入力状態として採用します。

delay()でプログラムを停止しないため、ほかの処理と並行してボタン入力を監視しやすくなります。

ハードウェアでチャタリングを抑える

チャタリングはソフトウェアだけでなく、回路側で対策することもできます。

代表的な方法のひとつが、抵抗とコンデンサを組み合わせたRC回路です。

コンデンサを利用して急激な電圧変化をなだらかにすることで、接点の細かなON・OFFがそのままGPIOへ伝わりにくくなります。

ただしRC回路を使用すると信号の立ち上がり・立ち下がりも緩やかになります。使用するGPIOの入力仕様やしきい値、必要な応答速度などを考えて抵抗値と容量を決める必要があります。

用途によっては、RC回路に加えてシュミットトリガ入力や専用のデバウンス回路を利用する方法もあります。

プルアップ抵抗とチャタリングは別の問題

ここで混同しやすいのが、プルアップ・プルダウンとチャタリングの違いです。

プルアップ・プルダウン抵抗は、スイッチが接続されていない状態でGPIOがフローティングすることを防ぐために使用します。

一方、チャタリングは機械式スイッチの接点が物理的に振動することで発生します。

主な問題代表的な対策
フローティング入力電圧が決まらないプルアップ・プルダウン
チャタリング接点が短時間にON/OFFするデバウンス処理

プルアップ・プルダウンについては「プルアップ・プルダウン抵抗とは?必要な理由と仕組みを初心者向けに解説」で詳しく解説しています。

割り込みではチャタリングに特に注意

GPIO割り込みで機械式ボタンを検出するときは、チャタリングに特に注意が必要です。

たとえばボタンを押したときのFALLINGを割り込み条件にしている場合、チャタリングによって信号が何度もHIGH・LOWを繰り返すと、1回の操作で複数回の割り込みが発生する可能性があります。

そのため、「割り込みを使えばボタンを正確に1回だけ検出できる」というわけではありません。

割り込みを使う場合でも、必要に応じてデバウンス処理を行います。

割り込みについては「割り込み処理とは?ポーリングとの違いと仕組みを初心者向けに解説」で詳しく解説しています。

KUMITATE-C3でチャタリングを確認してみる

チャタリングは、実際にボタン入力の変化を観察すると理解しやすい現象です。

KUMITATE-C3にはESP32-C3が搭載されており、GPIO入力や内部プルアップを使ったボタン入力を学習できます。

たとえばGPIO21に接続されたボタンの状態変化を検出して、シリアルモニターへ表示してみます。

const int BUTTON_PIN = 21;

int lastState = HIGH;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop() {

  int state = digitalRead(BUTTON_PIN);

  if (state != lastState) {

    Serial.println("入力が変化しました");

    lastState = state;
  }
}

デバウンスを行わない状態で入力変化を記録すると、ボタンによっては1回の操作で複数の変化が検出されることがあります。

その後、先ほどのデバウンス処理を追加して結果を比較すると、チャタリング対策の効果を確認できます。

チャタリング対策は何msにすればいい?

デバウンス時間に「必ず○msにすればよい」という共通の値はありません。

必要な時間は、使用するスイッチの構造や特性、回路、必要な応答速度によって異なります。

学習用のプログラムでは数十ms程度から試すことがありますが、実際の製品では使用するスイッチの仕様や実測した波形を確認して決めることが重要です。

ロジックアナライザやオシロスコープを使用すると、実際にどのくらいの時間チャタリングが発生しているのかを確認できます。

まとめ

チャタリングは、機械式スイッチの接点が物理的に振動し、短時間に何度もON・OFFを繰り返す現象です。

  • ボタンを1回押しても複数回の入力として検出されることがある
  • チャタリング対策をデバウンスと呼ぶ
  • 簡単な方法として一定時間待って再確認する方法がある
  • millis()を使えば処理を止めずにデバウンスできる
  • RC回路などを使ってハードウェア側で対策することもできる
  • プルアップ抵抗だけではチャタリング対策にはならない
  • GPIO割り込みを使う場合もチャタリング対策が必要になることがある
  • 適切なデバウンス時間はスイッチや用途によって異なる

チャタリングを理解すると、ボタン入力が「たまに誤動作する」原因を回路とソフトウェアの両面から考えられるようになります。

KUMITATE

読むだけでなく、
実際に動かして学びませんか?

クミタテは、組み込み開発や電子工作を 実践しながら学べる学習プラットフォームです。 ESP32を使ったプログラミングから、電子回路、センサー、 通信、基板設計まで、自分のペースで学習できます。

  • 無料で学べる実践的な教材を掲載
  • すぐに試せるサンプルプログラム付き
  • Googleアカウントですぐに登録可能
クミタテに無料登録する
組み込み基礎
スポンサーリンク