LEDのオンオフを切り替える、スイッチ回路とイベント処理とは?
次に、タクトスイッチを回路に追加し、スイッチのオンオフで、LEDの点灯と消灯を切り替えるようにしてみましょう。
スイッチの接続
次の図のように、GPIO23とGNDの間に、スイッチを接続します。タクトスイッチを押せば、GPIO23にGNDがつながるということです。
プルアップ、プルダウン
LEDを点灯させるために、GPIOを出力モードに設定しましたが、スイッチの判定では、GPIOのモードを、入力モードの「プルアップ」という設定にします。
GPIOなどの端子を利用する場合、通常、電源かGNDにつながる回路にします。電源に接続する回路をプルアップ、GNDに接続する回路をプルダウンと呼びます。ESP32では、内部的にプルアップ、プルダウンの接続ができ、ソフトウェアで切り替えることができます。
ここではスイッチをプルアップ回路に接続しますので、スイッチがオフのときは、内部的に電源に接続された状態となり、GPIOがHighレベルになります。スイッチをオンにすると、GNDに接続されて、GPIOはLowレベルになります。
コードは、次のように書き換えます。
public static void Main()
{
using var controller = new GpioController();
// GPIO22番の初期化、設定
var led = controller.OpenPin(22);
led.SetPinMode(PinMode.Output);
led.Write(PinValue.Low);
// GPIO23番の初期化、設定
var sw = controller.OpenPin(23);
sw.SetPinMode(PinMode.InputPullUp); //(1)
// チャタリング防止
sw.DebounceTimeout = TimeSpan.FromMilliseconds(20); //(2)
// イベント処理(3)
sw.ValueChanged += (object _, PinValueChangedEventArgs e) =>
{
Debug.WriteLine("ValueChanged: " + e.ChangeType);
if (e.ChangeType == PinEventTypes.Falling)
{
// GPIOの値の反転
led.Toggle(); //(4)
}
};
Thread.Sleep(Timeout.Infinite);
}
GPIO23番のモードを、プルアップ(PinMode.InputPullUp)にします(1)。
チャタリング
タクトスイッチのような機械式のスイッチは、チャタリングと呼ばれる現象が発生します。チャタリングとは、機械的なスイッチの接点が接触する瞬間に、オンとオフが高速に繰り返される現象です。スイッチの接点は、肉眼ではわからないものの、接触する際に何度もバウンドする動きになります。この間(数十ミリ秒以下)、スイッチはオンとオフを繰り返します。そして最終的には安定した状態になります。
チャタリングでのオンとオフを検知してしまうと、スイッチを一度押しただけで何度も押したような結果となりますので、チャタリングを回避する必要があります。回避方法には、ハードウェアまたはソフトウェアでの方法がありますが、ソフトウェアで対処するほうが手軽です。GpioPinクラスクラスのDebounceTimeoutプロパティを設定すると、値に変化があった後、設定した時間の間は、値が変わっても無視するようになります。つまりチャタリングが発生している間は、値の変化を検知しないということです。ここでは、DebounceTimeoutを20ミリ秒としました。
イベント処理
スイッチのオンオフでGPIOの値が変化するようになりました。では、その変化を検出するにはどうすればよいでしょうか。
値の変化の検出には、いくつかのアプローチが考えられますが、一般的には次のようになるでしょう。
ポーリング処理
GPIOの状態を、一定の間隔でループして参照する方法です。ループのなかで、値に変化があったら対応する処理を行います。ループ内にスリープ処理が必要となるため、変換の検出から、対応する処理を行うまでタイムラグが発生する場合があります。また、ずっと処理が実行されるため、CPUリソースを消費することにもなります。
ハードウェア割り込み処理
ポーリング処理はソフトウェア的な手法ですが、「割り込み」はハードウェア的な手法になります。マイコンでの「割り込み」とは、何らかの信号により、実行しているプログラムを一時中断し、他のプログラムを割り込んで実行させることをいいます。ESP32では、GPIOの値の変化で、ハードウェア的に割り込みを発生させることができます。
.NET nanoFrameworkでは、このハードウェア割り込み処理を、C#で簡単に使えるようになっています。具体的には、GPIOの値が変化した場合、C#のイベント(ValueChangedイベント)が発生するようになっていて、そのイベントハンドラを定義することができるのです。
今回のコードでは、GpioPinクラスのValueChangedイベントに、イベントハンドラとしてラムダ式を定義しています(3)。GPIOの状態が変化したときに、このラムダ式が実行されることになります。
GPIOの状態は、PinValueChangedEventArgsオブジェクトとして渡されますので、そのChangeTypeプロパティで、スイッチが押されたか、離されたかを判断します。PinEventTypes.Falling(値としては2)は、GPIOの値が、HighからLowに変化したときで、PinEventTypes.Rising(おなじく1)は、LowからHighに変化したことを示します。
したがってここでのハンドラの処理は、スイッチが押されて、HighからLowに変化(PinEventTypes.Falling)したときに、LEDのピンの値を反転させる(4)、ということになります。
コードを実行してみましょう。タクトスイッチを押すたびに、LEDが点いたり消えたりするはずです。
またその際、デバッグ出力に、PinValueChangedEventArgsオブジェクトのChangeTypeプロパティが表示されます。
コールバック関数を利用した書き方
じつは、.NET nanoFrameworkでは、ValueChangedイベントを利用する以外に、コールバック関数を利用した書き方もできます。前述のイベント処理のところを、GpioControllerクラスのRegisterCallbackForPinValueChangedEventメソッドに変更します。このメソッドの引数は、ピン番号、PinEventTypes、コールバック関数です。PinEventTypesが指定できますので、特定のPinEventTypesで処理を行う場合は、こちらの書き方のほうがすっきりした形になります。
// コールバック関数の登録
controller.RegisterCallbackForPinValueChangedEvent(
23, // ピン番号
PinEventTypes.Falling, // イベント種類
(object _, PinValueChangedEventArgs _) => // コールバック関数
{
// GPIOの値の反転
led.Toggle();
}
);
最後に
今回は、LEDの点灯とスイッチの処理について解説しました。次回は、ESP32に備わるタッチセンサーを使ったプログラムを作成する予定です。
