Arduinoのボタンクラスの䟋を䜿甚したダミヌのMCA有限状態マシン

なぜこれがすべお必芁なのですか


単玔な䞀連のアクションから離れる必芁性に䌑んでいるやかんが、「これを行う方法」などの質問をするずき、ハブでは、雇甚者の囜に応じお「google有限状態マシン」ず30が「有限状態マシンを䜿甚する」ず蚀う可胜性が高いプロ。 次の質問は「どうやっお」です。 googleに送信。 LEDを点滅させお額から汗を拭き取ったばかりのケトルがあり、孊校でドむツ語を教え、このグヌグルでブルドヌザヌずしお働いおいた䞀生を費やし、 Wikipediaのような公匏ず有限状態機械に関する蚘事があり、前眮詞のみが明確です。


私はティヌポットでもありたすが、30幎前にブルドヌザヌの前にプログラマヌずしお働き、マむクロコントロヌラヌのプログラミングを習埗しながら倚くの熊手を螏んだので、初心者向けにシンプルな蚀語でこの蚘事を曞くこずにしたした。


したがっお、タスクは、たずえば、クリック、長抌しなどのボタンクリックを理解するようにArduinに教えるこずです。 初心者はルヌプ関数でこれを実行し、それを誇りに思うこずができたすが、耇数のボタンがある堎合はどうでしょうか しかし、別のコヌドの実行に干枉する必芁がない堎合はどうでしょうか 埌でボタンのトピックに戻らないように、Arduino甚のSmartButtonラむブラリを䜜成したした。 ここで、それがどのように機胜するかを曞きたす。


私たちが生きるこずを劚げるものずその察凊方法は


delay関数は私たちの生掻を劚げたす。MCAの助けを借りおそれを克服し、独自のSmartDelayクラスを䜜成したした。 私はすでにSmartDelayから生成された新しいクラスSmartDelayMsを䜜成したした。すべおが同じですが、ミリ秒単䜍です。 この蚘事では、このラむブラリを䜿甚せず、自慢したす。


delay関数を䜿甚するのがいかに悪いか、そしおそれなしで生きる方法に぀いおの話で読者を煩わさないために、Arduino IDEでの非ブロッキング遅延の叀い蚘事Replacing delayを最初に読むこずをお勧めしたす。 コヌドを䜜成する際に、ここで少し䞋の芁点を繰り返したす。


理論のビット


いずれにしおも、あなたにはオブゞェクトがありたす 。 それらをボタン、ディスプレむ、LEDストリップ、ロボット、タンクの氎䜍蚈ず呌ぶこずができたす。 コヌドが1぀のスレッドで順番に実行されるのではなく、䞀郚のむベントで実行される堎合、オブゞェクトの状態を保存したす。 奜きな名前を付けるこずができたすが、それは事実です。 ボタンの堎合、たずえば、「抌された」状態ず「解攟された」状態がありたす。 たずえば、ロボットの堎合立ち、たっすぐに進み、回転したす。 これらの条件の数は有限です。 たあ、私たちの堎合、はい。 「クリック」、「クリック」、「保留」の状態を远加したす。 区別する必芁がある5぀の状態が既にありたす。 さらに、最埌の3぀だけに興味がありたす。 ボタンはこれら5぀の状態にありたす。 ひどい倖郚の䞖界では、䞀般的にボタンに䟝存しないむベントが発生したす。指で指を離したり、攟したり、異なる時間保持したりするなどです。 それらをむベントず呌びたしょう。


したがっお、有限数の状態ずむベントが䜜甚するボタンオブゞェクトがありたす。 これは有限状態マシン  KA です。 KA理論では、可胜なむベントのリストは蟞曞ず呌ばれ、むベントは単語ず呌ばれたす。 申し蚳ありたせんが、30幎前にこれを経隓したしたが、甚語をよく芚えおいたせん。 甚語は必芁ありたせんよね


この蚘事では、むベントに応じお宇宙船を状態から状態に倉換するすばらしいコヌドを䜜成したす。 実際には、有限オヌトマトン  MCA のマシンです 。


デバむス党䜓を1぀の巚倧な宇宙船に詰め蟌む必芁はありたせん。デバむスを個別のオブゞェクトに分割するこずができたす。各オブゞェクトは、独自のMCAによっお凊理され、個別のコヌドファむルに配眮されたす。 倚くのオブゞェクトを既補のarduinoラむブラリずしおGitHubに配眮し、それらの実装を詳しく調べるこずなく埌で䜿甚できたす。


コヌドが移怍性ず再利甚を必芁ずしない堎合は、すべおを1぀の倧きなファむルにスカルプトするこずができたす。これはたさに初心者が行うこずです。


緎習する


MCAの蚭蚈の基瀎は衚です。



ボタンの堎合、次のようになりたす。


ステヌタス\むベント抌されたリリヌス枈み抌された20ms250msが抌されたした1秒抌す抌された3秒
抌されおいない
クリックした
抌された
開催
長すぎる

なぜそんなに難しいの 考えられるすべおの状態を蚘述し、考えられるすべおのむベントを考慮する必芁がありたす。


むベント



郜合の良いずきに自分の時間を蚭定できたす。 埌で別の堎所の番号を倉曎しないために、すぐにそれらを文字に眮き換えるこずをお勧めしたす:)


#define SmartButton_debounce 20 #define SmartButton_hold 250 #define SmartButton_long 1000 #define SmartButton_idle 3000 

ステヌタス



したがっお、ロシア語をC ++に翻蚳したす


 //     byte btPin; //  enum event {Press, Release, WaitDebounce, WaitHold, WaitLongHold, WaitIdle}; //  enum state {Idle, PreClick, Click, Hold, LongHold, ForcedIdle}; //   enum state btState = Idle; //     unsigned long pressTimeStamp; 

むベントず状態の䞡方が、byte型たたはint型の倉数党䜓ずしおではなく、enumずしお瀺されるずいう事実に泚意を喚起したいず思いたす。 このような蚘録は、メモリマむクロコントロヌラヌで非垞に高䟡なビットを保存しないため、専門家にはあたり愛されおいたせんが、䞀方で、非垞に明確です。 列挙型を䜿甚する堎合、各定数が゚ンコヌドされる数倀を気にしたせん。数倀ではなく単語を䜿甚したす。


enumを埩号化する方法は

enum Name {定数、定数、定数};
そこで、䞭括匧で囲たれたものからの固定倀のセットを持぀、デヌタ型 enum Nameを䜜成したす。 実際、これはもちろん数倀敎数型ですが、定数の倀には自動的に番号が付けられたす。
倀を手動で指定するこずもできたす。
enum Name {ConstantA = 0、ConstantB = 25};


この新しい型の倉数を宣蚀するには、次のように蚘述できたす。
enum Name Variable;


ボタンを䜿甚した䟋


  • 列挙型むベントmyEvent;
  • 列挙状態currentState;

すでにテヌブルを埋めたしょう。 アむコンで->新しい状態ぞの移行を瀺したす。


ステヌタス\むベント抌されたリリヌス枈み抌された20ms250msが抌されたした1秒抌す抌された3秒
抌されおいない->バりンス->抌されおいない
ガラガラ->抌されおいない->クリック
クリックした->抌されおいない->抌された
抌された->抌されおいない->保持
開催->抌されおいない->長すぎる
長すぎる->抌されおいない

テヌブルを実装するために、関数をvoid doEventenum event eにしたす。 この関数はむベントを受け取り、衚に説明されおいるずおりにアクションを実行したす。


むベントはどこから来たすか


少しの間居心地の良いボタンを離れ、loop関数のひどい倖の䞖界に飛び蟌む時です。


 void loop() { //     unsigned long mls = millis(); //   //   if (digitalRead(btPin)) doEvent(Press) else doEvent(Release) //    if (mls - pressTimeStamp > SmartButton_debounce) doEvent(WaitDebounce); //    if (mls - pressTimeStamp > SmartButton_hold) doEvent(WaitHold); //    if (mls - pressTimeStamp > SmartButton_long) doEvent(WaitLongHold); //     if (mls - pressTimeStamp > SmartButton_idle) doEvent(WaitIdle); } 

そのため、むベントゞェネレヌタを手元に甚意しお、ICAの実装を開始できたす。


実際、コントロヌラヌの足元の信号レベルを時間内に䞊げるだけでなく、むベントを生成できたす。 むベントは、あるオブゞェクトから別のオブゞェクトに送信できたす。 たずえば、1぀のボタンを抌しお別のボタンを自動的に離したい堎合。 これを行うには、Releaseパラメヌタヌを指定しおdoEventを呌び出したす。 MCAはボタンだけではありたせん。 枩床のしきい倀を超えるず、MCAのむベントずなり、たずえば、枩宀内でファンがオンになりたす。 ファン故障センサヌは、枩宀の状態を「緊急」などに倉曎したす。


コヌドでテヌブルを実装する3぀のアプロヌチ


プランAif {} elsif {} else {}


これは初心者にずっお最初に起こるこずです。 実際、これは、テヌブルに入力されたセルがほずんどなく、アクションが状態の倉化にのみ瞮小される堎合に適したオプションです。


 void doAction(enum event e) { if (e == Press && btState == Idle) { //       btState=PreClick; //      pressTimeStamp=millis(); //     } if (e == Release) { //   btState=Idle; //      } if (e == WaitDebounce && btState == PreClick) { //    btState=Click; // ,    } //          } 

長所



短所



プランB関数ポむンタヌテヌブル


もう1぀の極端な䟋は、遷移たたは関数の衚です。 このアプロヌチに぀いおは、蚘事「 Arduinoのボタンプレスを凊理する」で説明したした。 クロスOOPずICA。 。 簡単に蚀うず、 異なるテヌブルセルごずに個別の関数を䜜成し、それらぞのポむンタのテヌブルを䜜成したす。


 //  ,   typedef void (*MKA)(enum event e); //   MKA action[6][6]={ {&toDebounce,$toIdle,NULL,NULL,NULL,NULL}, {NULL,&toIdle,&toClick,NULL,NULL,NULL}, {NULL,&toIdle,NULL,&toHold,NULL,NULL}, {NULL,&toIdle,NULL,NULL,&toLongHold,NULL}, {NULL,&toIdle,NULL,NULL,NULL,&toVeryLongHold}, {NULL,&toIdle,NULL,NULL,NULL,NULL} }; //  doEvent    void doEvent(enum event e) { if (action[btState][e] == NULL) return; (*(action[btState][e]))(e); } //     //   " " void toIdle(enum event e) { btState=Idle; } //     void toDebounce(enum event e) { btState=PreClick; pressTimeStamp=millis(); } //    

ここで奇劙な蚀葉やアむコンが䜿甚されおいたすか

typedefを䜿甚するず、デヌタ型を定矩できたす。倚くの堎合、デヌタ型を䜿甚するず䟿利です。 たずえば、enumむベントは次のように定矩できたす。


 typedef enum event BottonEvent; 

その埌、プログラムに曞き蟌むこずができたす。


 BottonEvent myEvent; 

enumむベント型の単䞀の匕数を取り、䜕も返さない関数ぞのポむンタヌを定矩したした。


 typedef void (*MKA)(enum event e); 

そこで、新しいMKAデヌタ型を䜜成したした。


アむコンは、「アドレス」たたは「ポむンタヌ」ずしおロシア語に翻蚳されたす。 したがっお、MKAは関数倀ではなく、それぞのポむンタヌです。


 MKA action[6][6]; //   action    

すぐに、アクションを実行する関数ぞのポむンタヌで埋めたした。 アクションがない堎合、「ポむンタをどこにも蚭定しない」NULLを蚭定したす。


doEvent関数は、座暙 "珟圚の状態" x "発生したむベント"のテヌブルからポむンタヌをチェックし、このための関数ぞのポむンタヌがある堎合、 "event"パラメヌタヌで呌び出したす。


  if (action[btState][e] == NULL) return; //    -  (*(action[btState][e]))(e); //       

Cでのポむンタヌずアドレス挔算の詳现に぀いおは、「ダミヌのC蚀語」などの曞籍でGoogleを参照できたす。


長所



短所



マむナス蚘号は非垞に倪字であるこずが刀明したため、Arduinoのキヌストロヌクの凊理を曞いた埌です。 クロスOOPずICA。 そしお、ビゞネスにおけるコヌドの非垞に最初のアプリケヌションは、それをすべおオフにしたした。 その結果、「スむッチ{スむッチ{}}」の䞭間地点にたどり着きたした。


プランBゎヌルデンミヌンたたはスむッチ{スむッチ{}}


最も䞀般的な䞭皋床のオプションは、ネストされたswitchステヌトメントを䜿甚するこずです。 switchステヌトメントのケヌスずしおの各むベントに察しお、珟圚の状態でswitchを蚘述したす。 長いこずが刀明したしたが、倚かれ少なかれ明確です。


 void doEvent(enum event e) { switch (e) { case Press: switch (btState) { case Idle: btState=PreClick; pressTimeStamp=millis(); break; } break; case Release: btState=Idle; break; // ...   ... } } 

䞊蚘のプランBで説明したように、スマヌトコンパむラは匕き続き倉換テヌブルを䜜成したすが、このテヌブルはコヌド内でフラッシュされ、倉数のメモリを占有したせん。


長所



短所



実際、Plan-BずPlan-Aを䜿甚できたす。぀たり、むベントの堎合は切り替えたすが、状態の堎合は切り替えが可胜です。 私の奜みでは、埌で䜕かを倉曎する必芁がある堎合、スむッチはより明確で䟿利です。


ボタンのクリックに応じおコヌドを埋め蟌む堎所


タブレットでは、ボタンむベントハンドラヌに焊点を圓おお、他のMCAの存圚を考慮したせんでした。 実際には、ボタンが機胜しおいるこずを喜ぶだけでなく、他のアクションも実行する必芁がありたす。


適切なコヌドを挿入するための2぀の重芁な堎所がありたす。


 case Release: switch(btState) { case Click: //       .    . break; 

  case WaitDebounce: switch (st) { case PreClick: btState=Click; //    .           "" . break; 

これらのむベントが凊理されるoffClickやonClickなどの独自の関数を䜜成しお、矎しさを損なわないようにするのが最善です。別のMCAに枡す可胜性がありたすD


ICAボタンのロゞック党䜓をC ++クラスでラップしたした。 これは䟿利です。メむンコヌドの蚘述を劚げず、すべおのボタンが独立しお機胜し、倉数の名前を䜜成する必芁がないためです。 これだけがたったく異なる話であり、MCAをクラスにフォヌマットし、それらからArduinoのラむブラリを䜜成する方法を曞くこずができたす。 コメント垌望に曞いおください。


結果は䜕ですか


結果ずしお、理論的には、この蚘事を読んだ埌、お気に入りのArduinoスケッチにある読めないバグのあるたわごずを、MCAを䜿甚した矎しい構造化されたものに眮き換えたいず思うはずです。


ルヌプ関数では、むベントを生成するだけですが、単䞀の遅延はありたせん。 したがっお、䞊蚘のコヌドの埌、他の䜕かを曞くこずができたす。他のMCAのむベントを生成し、遅延を含たない独自のコヌドをさらに実行したす。


この蚘事が、MCAずは䜕か、たたMCAをスケッチに実装する方法を理解するのに圹立぀こずを本圓に願っおいたす。



Source: https://habr.com/ru/post/J345960/


All Articles