2.ロータリエンコーダの使用 TOPへ 4.ライトセンサの使用

3.タッチセンサの使用


LEGO MINDSTORMSにはッチセンサ・ライトセンサ・超音波センサ・サウンドセンサ・ロータリーエンコーダが標準で付属しており、nxtOSEK でもこれらのセンサーを使うための関数が用意されています。 ここでは、タッチセンサを使うための関数と、その際のプログラミングのポイントについて解説します。


1.タッチセンサの取り付け

ロボットは前回までのものを引き継ぎます。
使ってタッチセンサをコントローラのサイドの押しやすい位置に、継ぎ手部品だけを使って出来るだけシンプルな形でで取り付けてください。また、タッチセンのコントローラの入力ポート2を適当な長さのケーブルで接続して下さい。


2.タッチセンサを押すとスタートし、もう一度押すと停止

次に示すプログラムは、タッチセンサが押されたらスタートし、少し(200msec)を開けて、もう一度タッチセンサが押されるたら停止するプログラム(touch1.c)です。
touch1.c
#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

DeclareTask(Task1);				/* Task1を宣言 */

#define TOUCH_SENSOR NXT_PORT_S2

void ecrobot_device_initialize(){		/* OSEK起動時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void ecrobot_device_terminate(){		/* OSEK終了時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void user_1ms_isr_type2(void){}

TASK(Task1)
{
	//タッチセンサが押されるまで待って、スタートする
	while(1){
		if(ecrobot_get_touch_sensor(TOUCH_SENSOR)==1) break; 
		systick_wait_ms(4);
	}
	

	//スタート
	nxt_motor_set_speed(NXT_PORT_B,60,1);
	nxt_motor_set_speed(NXT_PORT_C,60,1);

	systick_wait_ms(200); // 200msec待つ
	
	//タッチセンサが押されるまで待つ。(次の while ループを回り続ける。)
	
	while(1){
		if(ecrobot_get_touch_sensor(TOUCH_SENSOR)==1) break;
		systick_wait_ms(4);
	}
	
	//停止(ブレーキモード)
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);

   	display_string("THAT'S ALL !");		/* メッセージを表示する */
	display_update();
	TerminateTask();					/* 処理終了 */
}



ecrobot_get_touch_sensor(TOUCH_SENSOR)
TOUCH_SENSOR のポートに接続されているタッチセンサの値を読み取ります。タッチセンサが押された状態のとき1を、押されていない状態では0を返します。

touch1.c でスタート後の 200msec の wait (ある程度の待ち時間)がないと、スタート待ちのタッチセンサ押下の処理(1つ目の while(1)の処理)に引き続いて、次のタッチセンサ押下を待つ処理(2つ目の while(1) の処理)が実行されてしまい、1回のボタン押下でスタート直後に停止動作が行われて(実際にはスタートせずに)直ぐにプログラムが終了してしまうことになります。


3.連続動作の回避方法

次のプログラムはタッチセンサを押す度に、走行と停止を繰り返すプログラムです。 トグル的に状態(走行と停止)が切り替わるように run_flag というアクションフラグ(単にフラグともいう)を用いています。

touch2.c 
#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

DeclareTask(Task1);				/* Task1を宣言 */

#define TOUCH_SENSOR NXT_PORT_S2
enum {STOP, RUN} run_flag = STOP; //走行状態を示すフラグ

void ecrobot_device_initialize(){		/* OSEK起動時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void ecrobot_device_terminate(){		/* OSEK終了時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void user_1ms_isr_type2(void){}

TASK(Task1)
{

	while(1){

		if(ecrobot_get_touch_sensor(TOUCH_SENSOR) == 1){

			if(run_flag == STOP){
				run_flag = RUN;	//停止中なら走行中へ
			}else{
				run_flag = STOP;	//走行中なら停止中へ
			}

		}

		if(run_flag==RUN){
			nxt_motor_set_speed(NXT_PORT_B,60,1);
			nxt_motor_set_speed(NXT_PORT_C,60,1);
		}else{
			nxt_motor_set_speed(NXT_PORT_B,0,1);
			nxt_motor_set_speed(NXT_PORT_C,0,1);
		}

		systick_wait_ms(4);

	}

}


しかし、実際にプログラムを動作させると、タッチセンサを押しても走行と停止が切り替わらないことが相当な頻度で発生します。 その理由は、タッチセンサを押して直ぐに手を離したつもりでも、実際にはある程度の時間押されている状態(ecrobot_get_touch()の戻り値が1となる状態)が続くため、run_flag の値が2回以上変わることがあるからです。このプログラム例だと押下状態が 4msec 以上続けば、その間に複数回の走行モード(run_falgの値)が切り替わることになります。

この問題を回避するために、タッチセンサ押下の判定の部分を以下のように変更したとします。

		if(ecrobot_get_touch_sensor(TOUCH_SENSOR) == 1){
			if(run_flag == STOP){
				run_flag = RUN;	//停止中なら走行中へ
			}else{
				run_flag = STOP;	//走行中なら停止中へ
			}			
			systick_wait_ms(200); // 200ms の wait
		}

このプログラムでは0.2秒(200ミリ秒)以上押し続けない限り、run_flag の値は1度切り替わるだけです。 しかし、この方法では、waitを入れた分だけ、次の処理(この場合は走行動作の切り替え)が待たされることになり、 即応性を求められる処理をさせる場合に問題となります。 また、waitをしている間のタッチセンサの動作を検知することができなくなります。

走行状態のフラグと別にタッチセンサの状態を示すフラグ(次のプログラムでは ts_flag)を使うことにより、この問題を解決できます。

touch3.c
#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

DeclareTask(Task1);				/* Task1を宣言 */

#define TOUCH_SENSOR NXT_PORT_S2
enum {STOP, RUN} run_flag = STOP; //走行状態を示すフラグ
enum {OFF, ON} ts_flag = OFF; //タッチセンサの状態を示すフラグ

void ecrobot_device_initialize(){		/* OSEK起動時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void ecrobot_device_terminate(){		/* OSEK終了時の処理(モータ停止)*/
	nxt_motor_set_speed(NXT_PORT_B,0,1);
	nxt_motor_set_speed(NXT_PORT_C,0,1);
}

void user_1ms_isr_type2(void){}

TASK(Task1)
{

	while(1){

		//タッチセンサが押されていなければ TSフラグをOFFに戻す
		if(ecrobot_get_touch_sensor(TOUCH_SENSOR) == 0 && ts_flag == ON ) ts_flag=OFF; 

		//タッチセンサが押されていて、TSフラグがOFFのとき以下の処理を行う
		if(ecrobot_get_touch_sensor(TOUCH_SENSOR) == 1 && ts_flag == OFF){ 
			ts_flag = ON; 		//TSフラグをONに変更
			if(run_flag == STOP){
				run_flag = RUN;	//停止中なら走行中へ
			}else{
				run_flag = STOP; //走行中なら停止中へ
			}
		}

		if(run_flag==RUN){
			nxt_motor_set_speed(NXT_PORT_B,60,1);
			nxt_motor_set_speed(NXT_PORT_C,60,1);
		}else{
			nxt_motor_set_speed(NXT_PORT_B,0,1);
			nxt_motor_set_speed(NXT_PORT_C,0,1);
		}
		systick_wait_ms(4);

	}
}


なお、スイッチを使用する場合、一般的にはチャタリング(接触部のバウンドによるによるON/OFFの繰り返し)を考慮する必要がありますが、enrobot_get_touch_sensor()によるタッチセンサ値の読み取りでは、チャタリングは除去されているためプログラム内で考慮する必要はありません。チャタリングについては、例えば マルツオンラインの解説を参照して下さい。

4.課題

  1. サンプルプログラム touch2.c, touch3.c のタスク関数の動作を確認せよ。

  2. 「2.ロータリエンコーダの使用」の課題で作成した正方形走行するプログラムを改変して、次のように動作するプログラムを作成せよ。
    • タッチセンサが押されたらスタートする。(touch1.c を参考にする)
    • 直進動作中にタッチセンサが押される度に、停止、走行を繰り返す。(touch3.c を参考にする)
      ただし、この停止、走行時にはロータリエンコーダデータをリセットしない。
    • 回転動作中の動作は変更する必要はない。



2.ロータリエンコーダの使用 TOPへ 4.ライトセンサの使用