ページ 11

スイッチの状態監視について

Posted: 2015年11月10日(火) 16:55
by C言語素人
スイッチの状態監視プログラムについて教えてください。

①スイッチ1、スイッチ2のいずれかが押されるまでは、
while (sw==0x03) {}の処理を行う
②スイッチ1、スイッチ2のいずれかが押されたら、
 while(1){}の処理を行う

スイッチの状態を監視していて、スイッチが押されたらすぐに②の処理に移りたいのですが、
下記プログラムでは、5秒間待たなければなりません。(当然ですが…)

delay_ms()の時間を変えられれば良いのですが、諸事情により変更できません。
何か方法はありますでしょうか?

コード:

while (sw==0x03) {
			LATAbits.LATA4=0;	//LED1点灯
			LATAbits.LATA5=0;	//LED2点灯
			sw = PORTA & 0x03;  //スイッチの状態を取得
			delay_ms(5000);		//delay時間調整

				}
while(1)
	{
 	LATAbits.LAT6=0;	//LED3点灯
	}
}


Re: スイッチの状態監視について

Posted: 2015年11月10日(火) 22:18
by みけCAT
C言語素人 さんが書きました:delay_ms()の時間を変えられれば良いのですが、諸事情により変更できません。
何か方法はありますでしょうか?
単純に考えれば、delay_msを実行しないようにすればいいでしょう。
delay_msの前にif(0)を追加します。

コード:

while (sw==0x03) {
			LATAbits.LATA4=0;	//LED1点灯
			LATAbits.LATA5=0;	//LED2点灯
			sw = PORTA & 0x03;  //スイッチの状態を取得
			if(0) delay_ms(5000);		//delay時間調整

				}
while(1)
	{
 	LATAbits.LAT6=0;	//LED3点灯
	}
}

もしも精度があまり重要でなく、かつ利用している環境でタイマー割り込みが利用できるのであれば、
ビジーループで設定した時間が経過するのを待つのがいいかもしれません。

コード:

while (sw==0x03) {
	LATAbits.LATA4=0;	//LED1点灯
	LATAbits.LATA5=0;	//LED2点灯
	run_timer(5000);	//タイマーをセット
	do {
		sw = PORTA & 0x03;  //スイッチの状態を取得
	} while (sw==0x03 && !timer_done)	//時間調整

}
while(1)
	{
 	LATAbits.LAT6=0;	//LED3点灯
	}
}
run_timer(時間)は、呼び出されるとグローバル変数timer_doneの値を0にし、
タイマー割り込みを用いて指定した時間経過したらグローバル変数timer_doneを1にするように設定する関数とします。

Re: スイッチの状態監視について

Posted: 2015年11月11日(水) 08:11
by C言語素人
みけCAT様

回答ありがとうございます。
教えていただきたいことがあります。

①if(0)について
delay_msの前にif(0)を追加するとありますが、このif(0)の解釈の仕方が分かりません。
次の解釈で正しいでしょうか。
sw==0x03の状態が真で、これ以外(つまり、いずれかのスイッチが押された状態)が偽。
→この「偽」を表す式としてif(0)を追加
→つまり、「偽」の状態になれば、delay_msを実行しないで、ループを抜けることができる。


②タイマ割り込みについて
『もしも精度があまり重要でなく、かつ利用している環境でタイマー割り込みが利用できるのであれば、
ビジーループで設定した時間が経過するのを待つのがいいかもしれません。』

私もタイマ割り込みでの解決方法を考えてみました。
まだ正しく動作できていませんが、次の内容で挑戦しています。何かおかしな点がありましたら、指摘して頂けると助かります。


①スイッチ1、スイッチ2のいずれかが押されるまでは、
while (sw==0x03) {}の処理を行う

②スイッチ1、スイッチ2のいずれかが押されたら、
 while(1){}の処理を行う


電源ON後、スイッチは押されていない状態です。
その状態から、スイッチが押されるまでの間、2.5sec周期で[動作0]を実行しています。
この間、タイマ割り込み(0.1msec周期)で常にスイッチの状態を監視し、押されたらただちにwhile(1)文の処理へ移行したいと考えています。


次のコードで試していますが、2.5sec周期の途中でスイッチを押しても、while(1)の処理へ移ってくれません。

コード:

void main(void){

 	OSCCON=0b01110000;	//8MHz設定
	TRISA = 0b00000011;	// RA1-0 input
	TRISB = 0b00000011;	// RB1-0 input
	TRISC = 0b00000000;	// RC7-0 output
	ADCON1 = 0x0F;
	PORTC=0x00;
	delay_ms(100);

//0.1msec周期タイマ割り込み設定
	OpenTimer0(TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_2);
	WriteTimer0(65436);
	INTCONbits.TMR0IE = 1;	//タイマ0割り込み許可
	INTCONbits.GIE = 1;		//割り込み許可


//(1)センサが白白状態の間に行う動作(黒検知するまで直進)
 while (sw==0x03) {					//動作0
			LATCbits.LATC0=1;
			LATCbits.LATC1=1;
			LATCbits.LATC2=1;
			LATCbits.LATC3=1;
			delay_ms(5000);		//delay時間固定2.5sec
			LATCbits.LATC0=0;
			LATCbits.LATC1=0;
			LATCbits.LATC2=0;
			LATCbits.LATC3=0;
			delay_ms(5000);		//delay時間固定2.5sec
				}
//(2)少なくとも1つのスイッチが押された後の処理
 while(1){
		T0CONbits.TMR0ON=0;			// Timer0 On/Off Control bit (1:Enable Timer0/0:Stop Timer0)
		sw=PORTA &0x03;
		switch(sw){
			case 0:				//動作1
				LATCbits.LATC0=1;
				LATCbits.LATC1=1;
				LATCbits.LATC2=1;
				LATCbits.LATC3=1;
				break;
			case 1:				//動作2
				LATCbits.LATC0=1;
				LATCbits.LATC1=0;
				LATCbits.LATC2=1;
				LATCbits.LATC3=1;
				break;
			case 2:				//動作3
				LATCbits.LATC0=1;
				LATCbits.LATC1=1;
				LATCbits.LATC2=0;
				LATCbits.LATC3=1;
				break;
			case 3:				//動作3
				LATCbits.LATC0=1;
				LATCbits.LATC1=1;
				LATCbits.LATC2=1;
				LATCbits.LATC3=0;
				break;
			default:break;
        	  }

		}			
}


//遅延関数
/**0.5msec Delay SubFunction at 10MIPS**/
void delay_ms(int time){
	while(time > 0){
		Delay100TCYx(10);
		time--;
	}
}


#pragma interrupt all_isr   //割り込みの宣言
#pragma code isr_vector = 0x08	//	割込みベクタにジャンプ命令をセット
void goto_isr(void)
{
	_asm
		Goto all_isr
	_endasm
}

#pragma code  //割込み処理関数を用意
void all_isr(void)  //割込み処理関数
{
		sw = PORTA & 0x03;  //スイッチの状態を取得
		INTCONbits.TMR0IF = 0; ///タイマ0割り込みフラグのクリア
		WriteTimer0(65436);
}

Re: スイッチの状態監視について

Posted: 2015年11月11日(水) 09:09
by みけCAT
C言語素人 さんが書きました: delay_msの前にif(0)を追加するとありますが、このif(0)の解釈の仕方が分かりません。
次の解釈で正しいでしょうか。
sw==0x03の状態が真で、これ以外(つまり、いずれかのスイッチが押された状態)が偽。
→この「偽」を表す式としてif(0)を追加
→つまり、「偽」の状態になれば、delay_msを実行しないで、ループを抜けることができる。
いいえ。
0はswに依存せず0なので、常にdelay_msを実行しないという意味になります。
C言語素人 さんが書きました:私もタイマ割り込みでの解決方法を考えてみました。
まだ正しく動作できていませんが、次の内容で挑戦しています。何かおかしな点がありましたら、指摘して頂けると助かります。
delay_ms関数でswを全く見ていないので、スイッチを押してもすぐにdelay_ms関数から戻ることは出来ず、while(1)文の処理へ移行することもできません。
delay_ms関数の書き換えが許されるのであれば、
  • swの状態を監視し、0x3で無かったらすぐに戻るようにする。
  • もしもスイッチの入力があった時に割り込みがかけられるのであれば、(ハードウェアでチャタリング/バウンシング対策をした上で)
    delay_msで利用する時間を数える関数をグローバルにし、キー入力の割り込みでその変数を0に書き換えるようにする。
    (この場合ループ内の1個目のdelay_msの呼び出しの直後にもスイッチの判定をしないといけない)
という方法が考えられます。
もしくは、このようにキー入力があったら即戻る遅延関数を別に作ってもいいでしょう。
オフトピック
C言語素人 さんが書きました:

コード:

OSCCON=0b01110000;	//8MHz設定
delay_ms(5000);		//delay時間固定2.5sec
/**0.5msec Delay SubFunction at 10MIPS**/
8MHzで10MIPS出るということは命令を並列で実行する高度なパイプライン的な装置を積んでいるか、
もしくはPLLで4倍くらいの周波数で走っているのかな?
もしくは単にコメントにデタラメが書かれているか。

【追記】
Delay100TCYx関数の効果がここで説明されているものであると仮定すると遅延時間はあっていそうだし、単にコメントが間違っているか、自分の「10MIPS」の解釈が間違っているかな?
MCC18 ライブラリ <delays.h>

Re: スイッチの状態監視について

Posted: 2015年11月11日(水) 11:43
by C言語素人
みけCAT様

回答ありがとうございます。
いただいた回答を参考にさせていただき、次のプログラムを記述してみました。

『swの状態を監視し、0x03で無かったらすぐに戻るようにする』

getsens関数を用意し、センサの状態を監視しています。
デバッグしていますが、スイッチが押されていない状態(sw==0x03)から、いずれかのスイッチを押しても、while(1)に移りません。

何か根本的に間違えているのでしょうか。

コード:

void main(void){
 
    OSCCON=0b01110000;  //8MHz設定
    TRISA = 0b00000011; // RA1-0 input
    TRISB = 0b00000011; // RB1-0 input
    TRISC = 0b00000000; // RC7-0 output
    ADCON1 = 0x0F;
    PORTC=0x00;
    delay_ms(100);
 
 
//(1)スイッチが白白状態の間に行う動作
 while (getsens() == 0x03) {                 //動作0
            LATCbits.LATC0=1;
            LATCbits.LATC1=1;
            LATCbits.LATC2=1;
            LATCbits.LATC3=1;
            delay_ms(5000);     //delay時間固定2.5sec
            LATCbits.LATC0=0;
            LATCbits.LATC1=0;
            LATCbits.LATC2=0;
            LATCbits.LATC3=0;
            delay_ms(5000);     //delay時間固定2.5sec
                }
//(2)少なくとも1つのスイッチが押された後の処理
 while(1){
        sw=PORTA &0x03;
        switch(sw){
            case 0:             //動作1
                LATCbits.LATC0=1;
                LATCbits.LATC1=1;
                LATCbits.LATC2=1;
                LATCbits.LATC3=1;
                break;
            case 1:             //動作2
                LATCbits.LATC0=1;
                LATCbits.LATC1=0;
                LATCbits.LATC2=1;
                LATCbits.LATC3=1;
                break;
            case 2:             //動作3
                LATCbits.LATC0=1;
                LATCbits.LATC1=1;
                LATCbits.LATC2=0;
                LATCbits.LATC3=1;
                break;
            case 3:             //動作3
                LATCbits.LATC0=1;
                LATCbits.LATC1=1;
                LATCbits.LATC2=1;
                LATCbits.LATC3=0;
                break;
            default:break;
              }
 
        }           
}
 
 
//遅延関数
/**0.5msec Delay SubFunction at 10MIPS**/
void delay_ms(int time){
    while(time > 0){
        Delay100TCYx(10);
        time--;
    }
}


/*=========================================================================
								getsens()
==========================================================================*/
int	getsens(void)
{
	int	sens, now_sens;

	if((now_sens = sw) == 0x03) return 0x03;

	delay_ms(20);

	now_sens = sw;

	while((sens = sw) != 0x03){
		if(sens > now_sens) now_sens = sens;
	}

	return now_sens;
}


Re: スイッチの状態監視について

Posted: 2015年11月11日(水) 12:46
by みけCAT
C言語素人 さんが書きました:何か根本的に間違えているのでしょうか。
  • 問題になっているdelay_ms関数に全く手が加えられていない
  • やりたそうなことに対して無駄に複雑そうでしかも意味がなさそうなgetsens関数が導入されている
    (なんで最大値?動作2より動作3を優先したい?それにしては肝心の後半部分で使われていないし…)
申し訳ないですが、論外ですね。