決められた時だけスイッチの判定を行う

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
C素人

決められた時だけスイッチの判定を行う

#1

投稿記事 by C素人 » 7年前

SWが全部で8個あります。
switch文でswの状態に応じた動作のフラグを立てています。
各動作の内容は、フラグごとにif文で定義しています。

次の内容で困っています。
コードを掲載させていただきます。

if(use_r)内の動作について。
①~③の間は、swの状態をモニタしたくありません。
④の動作において、swの状態がある決められた状態になるまで、繰り返し動作をし続け、
その後、switch文に戻る…という動作を行いたいです。


私が記述したコードでは、
①~③の間に、swの状態をモニタしてしまいます。
そのため、③の動作の後(このとき、swの状態は0xFFであったり、switch文で定義されていない状態になっています。)、
④の動作に入れないまま、何も動作しない結果になってしまいます。
(③のあと、swの状態が、switch文で定義されていない状態になっているので、当たり前なのですが…)

コード:

while(1){
    char use_r = 0, use_g = 0, use_b = 0; /* 各関数を使うか */
    char is_valid = 1; /* 定義があるか */
	int count=0;
 
    sw = PORTA & 0xFF;
 
    switch(sw){
        
            case 0x00:
                use_r = 1;
                break;
 
            case 0x01:
                use_b = 1;
				break;
 
            case 0x30:
                use_g = 1;
                break;
 
        default:
            is_valid = 0;   /* 定義が無かった */
            break;
    }
 
    /* 定義がある場合のみ処理する */
    if(is_valid) {
        if(prev_sw != sw) {
            Reset();        //初期化
            delay_ms(1);    //delay
        }
 

        if (use_r) {
			MOT_RIni();		//右初期化
			MOT_LIni();		//左初期化
			delay_ms(1);

			//①機体を停止
			MOT_RStop();		//右停止
			MOT_LStop();		//左停止
			delay_ms(1);

			//②機体を後ろに下げる
			MOT_RBack();		//右後進
			MOT_LBack();		//左後進
			delay_ms(500);

			//③機体を左に曲げる
			MOT_RStop();		//右停止
			MOT_LFeed();		//左前進
			delay_ms(500);

			//①~③の間は、SWの状態をモニタしない

			//④swの状態が、0x00、0x01、0x30のいずれかになるまで機体を前進させる
			MOT_RStop();		//右停止
			MOT_LFeed();		//左前進
			do {
        		sw = PORTA & 0xFF;
        			}while(!((sw==0x00))||(sw==0x01)||(sw==0x30));
		}
    }
    prev_sw = sw;
}

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 決められた時だけスイッチの判定を行う

#2

投稿記事 by みけCAT » 7年前

申し訳ありません。C素人さんのやりたいことがよくわかりませんでした。
C素人 さんが書きました:私が記述したコードでは、
①~③の間に、swの状態をモニタしてしまいます。
本当ですか?
割り込みや提示されたコードから呼ばれている関数でモニタしている可能性は否定できませんが、
提示されたコードの①~③の間にはswの状態をモニタするようなコードは見当たらないように思えます。
C素人 さんが書きました:私そのため、③の動作の後(このとき、swの状態は0xFFであったり、switch文で定義されていない状態になっています。)、
④の動作に入れないまま、何も動作しない結果になってしまいます。
(③のあと、swの状態が、switch文で定義されていない状態になっているので、当たり前なのですが…)
なぜそれが当たり前なのですか?
もし③のあと、swの状態が、switch文で定義されていない状態になっているなら、
このコードでは(たまたま?)④のループの条件が「switch文で定義されていない状態なら繰り返す」になっていますし、
そもそも③と④の間に条件分岐などはないので、③の動作の後④の動作に入れないというのは割り込み、リセット、処理系の不都合などが無い限り考えにくいです。

一見④で①の前のswの状態を使いたいなら、その状態を変数に保存して④で使えばいい気がしますが、
今の④だとそれをやっても結局MOT_RStop()とMOT_LFeed()を呼んだあとのループでswを更新しているので、
この2関数の内容にもよりますが、早ければ数μs~数ms後にはswが更新されてしまい、意味がないでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: 決められた時だけスイッチの判定を行う

#3

投稿記事 by あんどーなつ » 7年前

PORTAのリードを下記の関数に置き換えてみてください。
デフォルトでは、連続5回同じ値になると、その値を返します。

コード:

unsigned char sampling() {
    int cnt = 0;
    int prev = PORTA & 0xff;
    int cur;
    while (cnt < 4) { // 注意:prevの分で1回多くPORTAをリードする
        cur = PORTA & 0xff;
        if (prev == cur) {
            cnt++;
        } else {
            prev = cur;
            cnt = 0;
        }
    }
    return cur;
}
また、char型を使われてるみたいですが、char = signed charになると困るので、
全部unsigned charに直したほうがいいです。

困る例 signed char a = 0xff; if (a == 0xff) -> FALSE

C素人

Re: 決められた時だけスイッチの判定を行う

#4

投稿記事 by C素人 » 7年前

あんど~なつ様

回答ありがとうございます。

もう1度現象を良く確認してみたいと思います。

1つ教えてください。
フラグをcharと定義している件について。

charで定義していると、クリア(0にする)したはずのフラグが、クリアされていないということはあり得ますでしょうか?

あるフラグが1ならば、Aの動き。
0ならば、Bの動きとしていたところ、
クリア(=0)しているはずなのに、Aの動きになっているということがあったので、
あんどーなつ様に指摘していただいた内容が関係しているのかと思い、質問させていただきました。

※クリアしているはず…については、プログラムの不備も含めて検証中です。

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: 決められた時だけスイッチの判定を行う

#5

投稿記事 by あんどーなつ » 7年前

C素人 さんが書きました: charで定義していると、クリア(0にする)したはずのフラグが、クリアされていないということはあり得ますでしょうか?
gccで a &= 0x7f や a = a & 0x7f などやってみましたが、クリアがされないということはなかったです。ただ、PCとマイコンのコンパイラでは挙動が違うことがあるので何とも言えないです。

C素人

Re: 決められた時だけスイッチの判定を行う

#6

投稿記事 by C素人 » 7年前

あんどーなつ様

あんどーなつ様に指摘していただいてから、現象をもう1度よく確認しました。
まず、以前掲載したコードに一部不備がありましたので、新たにコードを添付して現象を説明します。
if (use_r)内の動作について。
①~③の動作の後、swの状態が0xFFの状態なら、④の動作を行うと記述していますが、
①~③の動作の後、0xFFにならない場合があり、その場合はif (use_r)の処理を抜けて、ループの頭に戻ってしまうため、
ループの頭で、use_rが初期化され、再びswitch文によるswの状態判定になります。
このとき、swの状態が定義されていない状態だった場合、そのまま動作しない状態に陥ってしまっていました。
(現象を見て確認した私の理解です。間違えていたらすみません。)

while(sw==0xFF)の条件設定がだめだと思っていますが、どのように条件設定すべきか分かりません。

コード:

while(1){
    char use_r = 0, use_g = 0, use_b = 0; /* 各関数を使うか */
    char is_valid = 1; /* 定義があるか */
	int count=0;
 
    sw = PORTA & 0xFF;
 
    switch(sw){
        
            case 0x00:
                use_r = 1;
                break;
 
            case 0x01:
                use_b = 1;
				break;
 
            case 0x30:
                use_g = 1;
                break;
 
        default:
            is_valid = 0;   /* 定義が無かった */
            break;
    }
 
    /* 定義がある場合のみ処理する */
    if(is_valid) {
        if(prev_sw != sw) {
            Reset();        //初期化
            delay_ms(1);    //delay
        }
 

        if (use_r) {
			MOT_RIni();		//右初期化
			MOT_LIni();		//左初期化
			delay_ms(1);

			//①機体を停止
			MOT_RStop();		//右停止
			MOT_LStop();		//左停止
			delay_ms(1);

			//②機体を後ろに下げる
			MOT_RBack();		//右後進
			MOT_LBack();		//左後進
			delay_ms(500);

			//③機体を左に曲げる
			MOT_RStop();		//右停止
			MOT_LFeed();		//左前進
			delay_ms(500);

			//①~③の動作の後、swの状態が0xFFの状態なら、④の動作を行う
			//このとき、swの状態が0xFFになっていないので、ループの頭に戻ってしまい、ループの頭でuse_r=0としているので、
			//再び、switch文によるswの状態判定になってしまうので、①~③の後、swの状態が0xF0とかだと、動作しない状態に陥ってしまっていた。
			//
while(sw==0xFF){
			//④swの状態が、swの状態が0x00、0x01、0x30のいずれかになるまで機体を動かす(右折、前進の繰り返し)
			//動作時間はタイマ割り込みを使用しているので、0x00,0x01,0x30のいずれかになれば、すぐにこのループから抜けてくれる(はず)
			MOT_RStop();		//右停止
			MOT_LFeed();		//左前進
			timer=0;
			WriteTimer0(xxxxx); //0.5sec
			do {
        		sw = PORTA & 0xFF;
        			}while(!((sw==0x00))||(sw==0x01)||(sw==0x30)!timer);
			MOT_RFeed();		//右前進
			MOT_LFeed();		//左前進
			timer=0;
			WriteTimer0(xxxxx); //0.5sec
			do {
        		sw = PORTA & 0xFF;
        			}while(!((sw==0x00))||(sw==0x01)||(sw==0x30)!timer);
		}
			//⑤0x00,0x01,0x30の状態になったら、機体を停止させて、ループ処理に戻り、switch文によるswの状態に従い動作
			MOT_RStop();		//右停止
			MOT_LStop();		//左停止
		}
    }
 
    prev_sw = sw;
}

あんどーなつ
記事: 171
登録日時: 7年前
連絡を取る:

Re: 決められた時だけスイッチの判定を行う

#7

投稿記事 by あんどーなつ » 7年前

LEDが3つ付いているロボットですか? 飛行機だとプログラムが間違っているとすぐ壊れそうなので、初期段階ではエミュレータを使わないと厳しいですよね。
C素人 さんが書きました: ①~③の動作の後、0xFFにならない場合があり、その場合はif (use_r)の処理を抜けて、ループの頭に戻ってしまうため、
ループの頭で、use_rが初期化され、再びswitch文によるswの状態判定になります。
このとき、swの状態が定義されていない状態だった場合、そのまま動作しない状態に陥ってしまっていました。
別にそんなことはないと思います。システム全体を見て判断しているわけではないですが、0x00, 0x01, 0x03のどれかに入れれば再度動いてくれると思います。

機体は、デバッガをあてることができますか?ROMライトしかできないのであれば、3つのLEDを使って内部状態を判断するしかないと思います。
組み込みの場合、CPUが暴走することもありうるので、1秒ごとにLEDが点滅する仕組みを入れておくといいです。ウォッチドッグを設定しておくのもいいでしょう。

本番プログラムとデバッグで、LEDの動作を変えたいときは、LED点灯をモジュール化します。もうすでにされているかもしれませんが、されていない場合は、

コード:

int debugmode = 0;
void LEDR_set() {
  if (!debugmode) {
    // LED R を点灯
  }
}
void debugsetR() {
  if (debugmode) {
    // LED R を点灯
  }
}

int main() {
  debugmode = 1; // デバッグをするとき
  ..
  return 0;
}
のような感じに書くと、コードを汚さなくて済むと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る