Arduinoのキーストロークを処理します。 クロスOOPとICA。 パート1

数ヶ月前、私はあまり新しくないKTM 250EXCバイクを購入し、スロットルスティックを外し、Motuを空に引っ張り、尻に座り、背中で何かを壊しました。 そのため、少なくとも2か月はバイクに座らないでください。 なぜこれをしているのですか? はい やや疲れたモペットはダッシュボードに欠陥があることが判明し、私は家にいる間に自分で手作りする準備ができました。



画像



レイアウトをすばやく組み立て、数字を実行し、時計を実行し、走行距離計をFRAMに記憶します-美しさですが、この美しさを制御するにはボタンが必要でした。



今日はボタンについて説明し、次にイグニッションセンサーについて、それから整頓自体について説明します。



i2cを介した16x2中国語スクリーンへの描画は簡単で、エンジンの速度とRPMセンサーが外部割り込みを取得し、アナログポートから温度が読み取られ、情報がFRAMに保存され、中国時計もスタックします。 これらはすべて、 SmartDelayのように非同期で回転します。これについては、最近ここで書きました。



はい、ボタン!



他のおもちゃのように、LEDの点滅を遅くするボタンを1つ作るのは簡単でした。 しかし、巨大なキーボードをオートバイのエンデューロのダッシュボードに貼り付けても機能しません。場所はありません。 私は頭を砕き、4つのボタンに制限しなければなりませんでした。

  1. モード
  2. 上へ
  3. ダウン
  4. OK /リセット




これにメニューとコントロールの両方を入力するには、tyk、tyyyk、およびtyyyykを認識する必要があります。 つまり、異なる期間のボタンを押すことです。 私はスイッチから大きながらくたを書きました、そして、もし、私が数ヶ月でそれを読むことができないことに気づいて、そして再びプロを取り上げました。



タスクはSmartDelayライブラリに似ていることが判明しました。





同様のことをご存知の場合は、使用しているものをコメントでお知らせください。



まず、紙に有限状態機械を描きました。 紙なしでは、襲撃では機能しませんでした。



画像



その後、スイッチの代わりに/グーグルでグーグルで検索しました。 前回30年ほど前にICAのトピックに目を向けたとき、私の記憶にある理論を更新する必要がありました。



画像



その結果、抽象SmartButtonクラスを作成しました。 この作成は、内部にMCAを隠し、デジタルポートをリッスンし、クリック、ホールド、およびロングホールド時に空の抽象メソッドをプルします。 このクラスを使用するには、独自のクラスを作成し、必要なメソッドをオーバーライドする必要があります。



#include <SmartButton.h> byte menuMode = 0; //    SmartButton class modeSmartButton: public SmartButton { public: modeSmartButton(int p) : SmartButton(p) {} virtual void onClick(); //    virtual void offClick(); //   ,  . }; //   :    . void modeSmartButton::onClick() { Serial.println("Key pressed."); if (menuMode) { Serial.println("Menu mode off."); } else { Serial.println("Menu mode on."); } menuMode^=1; } //      .   . void modeSmartButton::offClick() { Serial.println("Key depressed."); } //  ,   6  . modeSmartButton btMode(6); void setup() { Serial.begin(9600); Serial.println("Ready"); } void loop() { btMode.run(); //     loop(). }
      
      







コードは少しであり、すべてが多かれ少なかれ明確であることがわかります。 ここにはコールバックがないため、明示的に説明します。 ループ()では、各ボタンに対して1つのrun()呼び出しのみがあり、クラスとボタン自体が定義されています。 Cスタイルのボタンでボタンポップを処理するためのMCAの恐ろしいはしごが干渉することはありません。



コードを見てみましょう。 プロジェクト全体はgithubにあります。



より良いものを発明することなく、時間間隔の設定を外部から利用できるようにしました。 ここでは、それに応じて、クリック、ホールド、ロングホールドなどの遅延が発生するため、このようなクリックをまったく無視する必要があります。 SmartButton.hでは、これらの定数を慎重に定義して、#includeの前に再定義できるようにしました。



 #ifndef SmartButton_debounce #define SmartButton_debounce 10 #endif #ifndef SmartButton_hold #define SmartButton_hold 1000 #endif #ifndef SmartButton_long #define SmartButton_long 5000 #endif #ifndef SmartButton_idle #define SmartButton_idle 10000 #endif
      
      







特に、StatesNumberとInputsNumberの番号を自動的に取得したため、状態と影響を列挙型として作成しました。



 enum state {Idle = 0, PreClick, Click, Hold, LongHold, ForcedIdle, StatesNumber}; enum input {Release = 0, WaitDebounce, WaitHold, WaitLongHold, WaitIdle, Press, InputsNumber};
      
      







彼は頭を少し折ってこのタイプを描きました。 これは、このクラスのメソッドへのポインターです。 笑わないでください、利点はどういうわけか私を通り過ぎました、私はそれらのマスターではありません。



 typedef void (SmartButton::*FSM)(enum state st, enum input in);
      
      







ここで私はいじくり回さなければなりませんでした。 これは変換表です。 大騒ぎは、メソッドへの参照、コンパイラーが誓わず、リンクがクラスの特定のインスタンスのメソッドであるようにそれらを書く方法にありました。 静的メソッドではなく、左辺関数だけでなく、メソッドであるため、プライベートクラス変数にアクセスできます。



 FSM action[StatesNumber][InputsNumber] = { {NULL, NULL, NULL, NULL, NULL, &SmartButton::ToPreClick}, {&SmartButton::ToIdle, &SmartButton::ToClick, NULL, NULL, NULL, NULL}, {&SmartButton::ToIdle, NULL, &SmartButton::ToHold, NULL, NULL, NULL}, {&SmartButton::ToIdle, NULL, NULL, &SmartButton::ToLongHold, NULL, NULL}, {&SmartButton::ToIdle, NULL, NULL, NULL, &SmartButton::ToForcedIdle, NULL}, {&SmartButton::ToIdle, NULL, NULL, NULL, NULL, NULL} };
      
      







すべてのメソッドはプライベートとして宣言され、生成されたクラスでオーバーライドするためのrun()および空のスタブのみがパブリックのままでした。



 inline virtual void onClick() {}; // On click. inline virtual void onHold() {}; // On hold. inline virtual void onLongHold() {}; // On long hold. inline virtual void onIdle() {}; // On timeout with too long key pressing. inline virtual void offClick() {}; // On depress after click. inline virtual void offHold() {}; // On depress after hold. inline virtual void offLongHold() {}; // On depress after long hold. inline virtual void offIdle() {}; // On depress after too long key pressing.
      
      







回路はこのために構築されているため、pinModeモード(ピン、INPUT_PULLUP)を使用しますが、近い将来、モードを選択する機能を追加する予定です。



run()メソッドは、単に時間間隔を宇宙船の入力アクションに変換します。



 void SmartButton::run() { unsigned long mls = millis(); if (!digitalRead(btPin)) { if (btState == Idle) { DoAction(btState, Press); return; } if (mls - pressTimeStamp > SmartButton_debounce) { DoAction(btState, WaitDebounce); } if (mls - pressTimeStamp > SmartButton_hold) { DoAction(btState, WaitHold); } if (mls - pressTimeStamp > SmartButton_long) { DoAction(btState, WaitLongHold); } if (mls - pressTimeStamp > SmartButton_idle) { DoAction(btState, WaitIdle); } return; } else { if (btState != Idle) { DoAction(btState, Release); return; } } }
      
      







プライベートDoActionメソッド(状態、効果)は、アドレスがある場合、単にテーブルから関数を呼び出します。



 void SmartButton::DoAction(enum state st, enum input in) { if (action[st][in] == NULL) return; (this->*(action[st][in]))(st, in); }
      
      







ほとんどのアクションは十分に単純に見えます。 単純に状態を設定し、抽象メソッドを呼び出します。このメソッドは、生成されたクラスでオーバーライドできます。 これはコールバックの類似物です。



 void SmartButton::ToClick(enum state st, enum input in) { btState = Click; onClick(); //       . }
      
      







Idle状態は、他のさまざまな状態から来たため、fattestハンドラーが判明し、そのようなイベントの抽象メソッドを作成したかったのです。



 void SmartButton::ToIdle(enum state st, enum input in) { btState = Idle; switch (st) { case Click: offClick(); break; case Hold: offHold(); break; case LongHold: offLongHold(); break; case WaitIdle: onIdle(); break; } }
      
      







このようなツールを使用して、表示モードを選択するためのボタン、上下のナビゲーション、および記事の冒頭で説明したオーバーロードされた選択/リセットボタンのクラスを生成する準備ができました。



もっと複雑な別の宇宙船に直面していることは明らかです。 ボタンはほとんどありませんが、多くのアクションがあります。 おもしろい場合は、今説明したライブラリの実際の実用的なアプリケーションの例として次回執筆します。



All Articles