InputPortのグリッチフィルター(たぶんチャタリング軽減フィルター)をオンにしても、チャタリングが起こるので、対策してみた。押しっぱなしにされたときのイベントも実装してある。
動作概要
想定しているボタンはタクトスイッチのようなモーメンタリ(自動復帰型)スイッチ。
「ボタンを押した」とみなす時間をPressTime[ms]で設定する。50[ms]でだいたいうまく動作したので、既定値にしてある。これ以下だとチャタリングが起こることがある。
ボタンを一回押すと、Pressイベントが発生する。
さらにRepeatTimeLag[ms]ボタンを押したままにするとRepeatInterval[ms]ごとに PressRepeatイベントが起こる。
押しっぱなしで早送りなどの機能をつけたいときに役に立つと思う。
使い方
基本的にはピンを指定してインスタンス化し、イベントを設定しておくだけ。
ただ、裏でチェック用のループは動かしていないので、メインループかどこかでCheckメソッドを呼ぶ必要がある。
1 2 3 4 5 6 7 8 |
ソースコード
なお、実行には時間のユーティリティクラス TimeUtil(こちら)が必要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | /// <summary> /// ボタンを接続する入力ポートです。 /// </summary> public class ButtonPort : InputPort { /// <summary> /// グリッチフィルター有、プルアップ抵抗有でボタン用入力ポートを初期化します。 /// </summary> /// <param name="portId"></param> public ButtonPort(Cpu.Pin portId) : this(portId, true, ResistorMode.PullUp) { } /// <summary> /// グリッチフィルター、プルアップ抵抗を指定してボタン用入力ポートを初期化します。 /// </summary> /// <param name="portId"></param> /// <param name="glitchFilter"></param> /// <param name="registorMode"></param> public ButtonPort(Cpu.Pin portId, bool glitchFilter, ResistorMode registorMode) : base(portId, glitchFilter, registorMode) { PressTime = 50; RepeatTimeLag = 800; RepeatInterval = 500; Reverse = true; } /// <summary> /// ボタンが押されたとして判定する時間 [ms](初期値:50[ms]) /// </summary> public int PressTime { get; set; } /// <summary> /// ボタンが押しっぱなしにされたとき、最初の PressRepeat イベントが起こるまでの時間 [ms](初期値:800[ms]) /// </summary> public int RepeatTimeLag { get; set; } /// <summary> /// ボタンが押しっぱなしにされたとき、Press イベントを発生させる間隔 [ms](初期値:500[ms]) /// </summary> public int RepeatInterval { get; set; } /// <summary> /// 論理を反転するかどうか(初期値:true) /// </summary> public bool Reverse { get; set; } private bool _PrevState = false; private long _PressStartTick = 0; private long _RepeatCounter = 0; private bool _IsPressInvoked = false; public event NativeEventHandler Press; public event NativeEventHandler PressRepeat; /// <summary> /// メインループから呼び出します。 /// </summary> public void Check() { bool currentState = Reverse ^ this.Read(); // 逆論理の場合はひっくり返す if (currentState && _PrevState) { if (_PressStartTick == 0) { _PressStartTick = TimeUtil.TickMS; } if (!_IsPressInvoked) { if (TimeUtil.GetElapsedMS(_PressStartTick) >= PressTime) { // Press イベント if (Press != null) Press((uint)TimeUtil.TickMS, 0, DateTime.Now); _IsPressInvoked = true; } } else { if (TimeUtil.GetElapsedMS(_PressStartTick) >= RepeatTimeLag && TimeUtil.GetElapsedMS(_RepeatCounter) >= RepeatInterval) { // PressRepeat イベント if (PressRepeat != null) PressRepeat((uint)TimeUtil.TickMS, 0, DateTime.Now); _RepeatCounter = TimeUtil.TickMS; } } } else { _PrevState = false; _PressStartTick = 0; _RepeatCounter = 0; _IsPressInvoked = false; } _PrevState = currentState; } } |