- 締切済み
H8マイコンでC言語で、パルスモータを制御
パルスモータを使用した、自動車のパワーウィンドウシミュレートプログラムを作成したいのですが、if文の書き方がわかりません。 1.プログラム開始時、窓が閉まっている状態を想定。2.完全に閉じた状態から左回りに5回転したときの状態を完全に開いた状態とする。3.完全に開いた状態から右回りに5回転したときに、窓が完全に閉じた状態となる。 キー入力は、A :左回り全開:窓が完全に閉じた状態(5回転目)になるまで左回転し、停止。B :左回り部分開:キー押下時に左回転。キーを離す、窓が完全に閉じた状態になったら停止。C :右回り部分閉:キー押下時に右回転。キーを離す、窓が完全に開いた状態になったら停止。D :右回り全閉 :窓が完全に開いた状態(5回転目)になるまで右回転し、停止。 ヒントを教えてください。お願いします。 #include <stdio.h> #include <key.h> /* 外部ポート A1,B1,C1 */ #define EPA1 (*((unsigned char *)0x60000)) #define EPB1 (*((unsigned char *)0x60001)) #define EPC1 (*((unsigned char *)0x60002)) #define EPCW1 (*((unsigned char *)0x60003)) void wait(long); void counterclockwise (void); void clockwise (void); unsigned char Keydat, count, cf, state; int cwpuls,ccwpuls; void main(){ //開始時は、窓が閉じている状態 state = 0; //回転数を初期化 count = 0; /* uPD71055(82c55) 初期化 */ init_key(); Keydat = 0; cf = 0; while (1) { if((Keydat = get_key_code())==KEY_A){ //左回り全開 clockwise(); //全開(5回転目)になったら停止 EPC1 = 0; /* 一度電流OFF */ wait(90000);/* 時間まち */ } else if (Keydat == KEY_B){ //左回り部分開 clockwise(); //全開(5回転目)になったら停止 EPC1 = 0; /* 一度電流OFF */ wait(90000);/* 時間まち */ } else if (Keydat == KEY_C){ //右回り部分閉 counterclockwise(); //全閉(5回転目)になったら停止 EPC1 = 0; wait(90000); } else if (Keydat == KEY_D){ //左回り全閉 counterclockwise(); //全閉(5回転目)になったら停止 EPC1 = 0; wait(90000); } } } void wait(long time) { long i; for (i = 0; i < time; i++) ; } void counterclockwise (void){ for (cwpuls = 0; cwpuls < 96; cwpuls++){/* CW方向96パルス回す*/ cf++; if (cf == 4) { cf = 0; } /* 励磁相カウンタ+1*/ switch (cf) { case 0:EPC1 = 0x50; /* 励磁パターン出力 */ break; case 1:EPC1 = 0x60; break; case 2:EPC1 = 0xa0; break; case 3:EPC1 = 0x90; break; } wait(1000); // モーター速度 } } void clockwise (void){ for (ccwpuls = 0; ccwpuls < 96;ccwpuls++){/* CCW方向96パルス回す*/ cf--; if (cf == 0xff) { cf = 3; } /* 励磁相カウンター -1 */ switch(cf) { case 0:EPC1 = 0x50; /* 励磁パターン出力 */ break; case 1:EPC1 = 0x60; break; case 2:EPC1 = 0xa0; break; case 3:EPC1 = 0x90; break; } wait(1000); //200~500が限界 } }
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- Interest
- ベストアンサー率31% (207/659)
度々登場、失礼します。今度は細かい話です。 ご質問のタイトルには「H8マイコンで」とありますが、ソースコードを見る限りではH8に関係ありそうなコードが見当たりません。ヘッダファイルにすらH8の影が無いのに、動くのでしょうか? ステッピングモータを回す以前に、LED1個を狙った周期で点滅させるプログラム、書けてますか?動かせてますか?<組み込み系のHello world見たいなものですね それから、突然 #include <key.h> というものが出てきているのですが、どこのCコンパイラを使っているのですか? ポートの定義ですが、 #define EPA1 (*((unsigned char *)0x60000)) これ、 #define EPA1 (*((volatile unsigned char *)0x60000)) としましょう。volatile は、コンパイラに最適化禁止を指示します。 これがないと、例えば EPA1 = 0x00; EPA1 = 0x01; EPA1 = 0x00: という処理を行わせようと思ったのに、コンパイラが最適化してしまって、 EPA1 = 0x00; に省略されてしまうおそれがあります。 同様に、ここで出てきたwait関数もコンパイラの最適化によって動作が変わるおそれ大。 void wait(long time) { long i; for (i = 0; i < time; i++) ; } これが、何もしない無駄ループと見做されて省略されるからです。 それに、この関数、時間の単位は何ですか? ちゃんとやるならH8が持っているタイマー(例えばウォッチドッグタイマをインターバルモードで使用するとか)の割り込みで正確に時間を作りましょう。 続いて大切なステッピングモータ(=パルスモータ)の駆動方法。普通は励磁パターンをテーブルにしておき、タイマ割り込み毎にポインタの移動を移動させて励磁パターンを変化させます。ステッピングモータを扱う上で忘れちゃいけないのがスローアップ・スローダウン。これをやらないと簡単に脱調します。加速テーブルを使ったH8でのステッピングモータ制御プログラムのサンプルが http://www.robotics.ee.shibaura-it.ac.jp/micromouse/ に載っていますので参考にしてみてください。
- Interest
- ベストアンサー率31% (207/659)
ANo.3 投稿後に早速間違い発見してしまいました。ごめんなさい。 【改定】 PFUNC stateTransitionTable[ STATE_NUM ][ EVENT_NUM ] = { // EV_AUTO_OPEN EV_MANUAL_OPEN EV_MANUAL_CLOSE EV_AUTO_CLOSE { nothing, nothing, manualClose, autoClose }, // ST_FULL_OPEND { autoOpen, manualOpen, manualClose, autoClose } // ST_HALF_OPEND { autoOpen, manualOpen, nothing, nothing } // ST_CLOSED }; 修正もテーブルだけですむから簡単! という例だと思ってください(汗)
- Interest
- ベストアンサー率31% (207/659)
ANo.1 = Interest です。ナイスフォローありがとうございます >DT200様 switch-case を使った例をANo.2で示していただきましたが、状態の数、イベントの数が増えるとメンテナンスが非常に煩雑になります。そこで、関数へのポインタを活用すると、メンテナンスがずっと楽になります。(ちょっとハイレベルなやり方です。) たとえば、ANo.1の状態遷移表を関数へのポインタ(のテーブル)で実装すると、 enum STATE { ST_FULL_OPEND, ST_HALF_OPEND, ST_CLOSED, STATE_NUM }; enum EVENT { EV_AUTO_OPEN, EV_MANUAL_OPEN, EV_MANUAL_CLOSE, EV_AUTO_CLOSE, EVENT_NUM }; typedef void ( * PFUNC ) ( STATE *state ); void nothing(STATE *state){ return; } void autoOpen(STATE *state) { (窓を全部開ける処理); state = ST_FULL_OPEND; } void manualOpen(STATE *state) { (窓をちょっとだけ開ける処理); if(窓が全部開いたら) state = ST_FULL_OPEND; else state = ST_HALF_OPEND; } void manualClose(STATE *state) { (窓をちょっとだけ閉める処理); if(窓が全部閉まったら) state = ST_CLOSED; else state = ST_HALF_OPEND; } void autoClose(STATE *state) { (窓を全部閉める処理); state = ST_CLOSED; } PFUNC stateTransitionTable[ STATE_NUM ][ EVENT_NUM ] = { // EV_AUTO_OPEN EV_MANUAL_OPEN EV_MANUAL_CLOSE EV_AUTO_CLOSE { nothing, manualOpen, manualClose, autoClose }, // ST_FULL_OPEND { autoOpen, manualOpen, manualClose, autoClose } // ST_HALF_OPEND { autoOpen, manualOpen, manualClose, nothing } // ST_CLOSED }; STATE state = ST_CLOSED; EVENT evetn; for(;;){ event = getKeyCode(); stateTransitionTable[ state ][ event ]( &state ); } ANo.2同様、動作は未確認。処理構造を示すための擬似コードだと思ってください。また、この例ではイベント名とアクション名を同じにしてしまいましたが、一般論では別になると思ってください。 ステートマシンの要素は、 「状態、イベント、アクション、遷移後の状態」 です。 まず、enum を使って状態とイベントを定義しつつ、状態とイベントの数がそれぞれいくつになるかコンパイラに数えさせます。 つづいて、アクションを実行する関数へのポインタの型を作ります。状態遷移後にどの状態を取るかはアクション依存なので、状態(state)を関数内で参照、変更できるように引数としてポインタをもらってくることにします。 アクションを実行する関数は、適当に(笑) 関数の終わりで状態を変更してあげます。 状態遷移テーブルは、見てわかるとおり素直に作ります。そのほうが、メンテナンスが楽ですし、わかりやすいからバグが乗りにくい。 実際に状態とイベントからアクションを選択実行するところは恐ろしくシンプルです。switch-case を使うと分岐の数だけCPUの処理ステップが必要ですが、関数ポインタのテーブルなら1ステップで実行する関数が決まります。 ほかにも、 volatile つけろ! とか、 無限ループで待ち時間を作ったらコンパイラに無視されるよ!とか いろいろ突っ込みどころがありますが、それはおいおい指摘していきます。
- DT200
- ベストアンサー率38% (63/164)
ANo1さんの続き。 ちなみに反時計回り(左回り)をCCW、時計回り(右回り)がCWです。 clockwise()とcounterclockwise()が96パルス一度に出力するのではなく、 引数に設定されたパルス数を出力するようにする。として、一つの例。 条件: 脱調はないものとします。 #define PLUSE_LIMIT 96 /* 96パルスで5回転とする */ pluse_num = 0; state = CLOSED; event = 0; for(;;){ event = get_key_code(); switch( state ){ case FULL_OPEND: if( event == AUTO_CLOSE ){ CCW( PLUSE_LIMIT ); state = FULL_CLOSED; pluse_num = 0; } else if( event == MANUAL_CLOSE ){ CCW( 1 ); pluse_num -= 1; if( pluse_num == 0 ){ state = FULL_CLOSED; } else{ state = HALF_OPEND; } break; case HALF_OPEND: if( event == AUTO_OPEN ){ CCW( PLUSE_LIMIT - pluse_num ); state = FULL_OPEND; pluse_num = PLUSE_LIMIT; } else if( event == MANUAL_OPEN ){ CCW( 1 ); pluse_num += 1; if( pluse_num < PLUSE_LIMIT ){ state = HALF_OPEND; } else{ state = FULL_OPEND; } } else if( event == MANUAL_CLOSE ){ CW( 1 ); pluse_num -= 1; if( pluse_num == 0 ){ state = FULL_CLOSED; } else{ state = HALF_OPEND; } } else if( event == AUTO_CLOSE ){ CW( pluse_num ); state = FULL_CLOSED; pluse_num = 0; } break; case CLOSED: if( event == AUTO_OEPN ){ CW( PLUSE_LIMIT ); state = FULL_OPEND; pluse_num = PLUSE_LIMIT; } else if( event == HALF_OPEND ){ CW( 1 ); pluse_num += 1; if( pluse_num < PLUSE_LIMIT ){ state = HALF_OPEND; } else{ state = FULL_OPEND; } } } } ------------------------------------------------------------- 当然、動作の検証を行なっていません。よって、動作保証はできませんし、 細かいところがだめかも知れません。 雰囲気だけでも。 実際は、FULL_OPENDとFULL_CLOSEDの直前にセンサを設けると思います。 その場合、その制御が加わります。
- Interest
- ベストアンサー率31% (207/659)
こんにちは。 ダメなところが多すぎて一度には直しきれませんが、一番大きいところから攻めて行きましょう。まずはプログラム全体の考え方のベースとなる状態遷移について考えます。 とりあえず、「状態遷移表」「ステートチャート」「ステートマシン」などをweb上で検索して、何の話をしているのか調べてみてください。その上で、上記の問題を次のように定義しなおします。 初期状態は CLOSED とする。 (event) AUTO_OEPN, MANUAL_OPEN, MANUAL_CLOSE, AUTO_CLOSE --------------------------------------------------------------- (state) FULL_OPEND 何もしない, 何もしない, ※2, CLOSED HALF_OPEND -> FULL_OPEND, ※1, ※2, CLOSED CLOSED -> FULL_OPEND, ※1, 何もしない, 何もしない ※1 全部開いたらFULL_OPEN, それまではHALF_OPEN ※2 全部閉まったらCLOSED, それまではHALF_OPEN AUTO_OPEN = Key D MANUAL_OPEN = Key C MANUAL_CLOSE = Key B AUTO_CLOSE = Key A と定義します。そして、この状態遷移表を実装します。 実装方法は swich-case で行う方法、関数ポインタのテーブルで行う方法などがありますが、初心者にはswich-case のほうが分かりやすいでしょう。 例: switch(state){ case STATE_A: if(event1) hogehoge; else if(event2) fugafuga; break; ・・・ }