目次

Gate sensor

 超音波センサーを利用したゲートセンサーを
 PIC12F629で作りました。

 今回利用する超音波センサーは、写真のものを利用します。



 トリガーを与えると、物体までの距離を計測し
 パルスの時間幅で結果を返してきます。

 タイミングチャートでみると、以下。



 TRGを出力して、ECHOを受けるようにします。
 PIC12F629は6ピンだけI/Oに利用できるので
 次のようにピンを割当てします。



 INDICATEで、指定した距離に物体があるのかを
 論理レベルで表示します。指定距離内に物体が
 あればHを、範囲外ならLを出力します。

 音速は340m/sですが、超音波センサーの場合、往復する
 距離で考えなければならないので、170m/sとして処理
 していきます。

 物体間距離を100mm強として扱っていきます。

 1000msで170000mmとなるので、1msでは170mmになります。
 1msよりもパルス幅が大きいときには、範囲外としてLを
 出力することにします。

 100msごとに、計測してパルス幅を求めてINDICATEに
 反映させる仕様にします。

 タイミングチャートで示すと以下。



 タイマー割込みを利用して、計測の開始と終了をフラグ通知します。
 計測開始を通知されたなら、内部カウンタをクリアし、内部クロック
 を利用し、カウンタをインクリメントしていきます。

 計測中に、ECHOパルスのrisingとfallingのエッジを検出して
 そのときのカウンタの値を記憶しておきます。

 risingとfallingのエッジで記憶したカウンタの値の差を求める
 パルス幅を計算したことになるので、時間換算の距離を求める
 ことと等価になります。

 PIC12F629は、タイマー0とタイマー1の2つタイマーをもっています。

 タイマー0を計測開始と終了を通知するために利用し
 タイマー1をパルスのrisingとfallingのエッジを検出
 したときのカウント値を生成する目的で使います。

 ここまで仕様を決めれば、プログラムをゆっくり記述できます。

 計測開始

  計測開始は、タイマー割込みからイベントフラグsflagで通知
  されるとします。

  イベントフラグでの通知が来たなら、次のシーケンスを実行します。
  1. イベントフラグクリア
  2. タイマー1のカウンタクリア
  3. タイマー1のカウントアップ開始
  4. TRGで超音波センサーに計測開始を指示
  5. 計測中フラグをセット
  このシーケンスをコードにします。 if ( sflag == ON ) { /* clear flag */ sflag = OFF ; /* clear timer counter */ TMR1H = 0 ; TMR1L = 0 ; /* start timer1 */ T1CON.TMR1ON = ON ; /* send trigger */ TRG = ON ; for ( i = 0 ; i < 32 ; i++ ) ; TRG = OFF ; /* enable measure */ mflag = ON ; }  計測終了   計測終了は、タイマー割込みからイベントフラグcflagで通知   されるとします。   イベントフラグでの通知が来たなら、次のシーケンスを実行します。
  1. イベントフラグクリア
  2. タイマー1のカウントアップ停止
  3. 計測中フラグをクリア
  4. 時間換算の距離を計算
  5. 距離に応じた論理値を出力
  このシーケンスをコードにします。 if ( cflag == ON ) { /* clear flag */ cflag = OFF ; /* stop timer1 */ T1CON.TMR1ON = OFF ; /* disable measure */ mflag = OFF ; /* calculate difference */ xdif = (xfine - xbegin) >> 1 ; /* judge */ tmp = ON ; if ( xdif > DEFX ) { tmp = OFF ; } /* impress */ IDS = tmp ; }  エッジ検出   エッジ検出は、シフトレジスタを利用します。   シフトレジスタにECHOに接続しているピンの論理値を   入れていき、2進数で01、10になったときエッジ   が入ったとして処理します。   言葉で書くと面倒に見えますが、コードは単純です。 /* shift */ sft <<= 1 ; /* masking */ sft &= 0x03 ; /* observe ECHO */ if ( ECHO == ON ) { sft |= ON ; } /* judge rising edge */ if ( sft == 1 ) { xbegin = 0 ; if ( mflag == ON ) { xbegin = get_tcnt() ; } } /* judge falling edge */ if ( sft == 2 ) { xfine = 0 ; if ( mflag == ON ) { xfine = get_tcnt() ; } }   rising、fallingのエッジを検出したとき、計測中であれば   そのときのカウント値を記憶します。計算は他のブロックが   担当するので、記憶するだけにとどめておきます。  タイマー0とタイマー1の使い方を考えていきます。  タイマー0の使い方   割込みを担当し、内部カウンタをインクリメントしていきます。   100msごとにイベントフラグsflagをセットします。もうひとつの   イベントフラグcflagは、sflagをセットしてから指定カウント値   経過後にセットします。   実際のコードは、以下。 void interrupt(void) { /* generate trigger */ if ( INTCON.T0IF == ON ) { /* clear flag */ INTCON.T0IF = OFF ; /* initialize */ TMR0 = CNTBEGIN ; /* increment */ xcnt++ ; /* judge START */ if ( xcnt == XCNTMAX ) { sflag = ON ; xcnt = 0 ; } /* judge EXIT */ if ( xcnt == XCNTHALF ) { cflag = ON ; } } }   タイマーは、クロックに依存した周期で割込みを発生するので   システムクロックを選択し、プリスケーラとカウンタ値の組合せ   を決めます。   内蔵クロック4MHzをシステムクロックとする。   タイマー0に与えられるクロックは、1/4した1MHzに   なるので、プリスケーラで1/8とする。   125kHzがタイマー0のカウンタに入るので、250分周すると   500kHz(周期は2ms)となり、100ms生成には変数xcntを用意   して対応します。   具体的には、XCNTMAX、XCNTHALFの値を決めてやります。 #define XCNTMAX 50 #define XCNTHALF 40   これらの数値で、計測時間は80msとなります。   80msでは、1360mm(約1.3m)の距離になるため   想定している物体までの距離の10倍になり、問題   なく計測できるとわかります。  タイマー1の使い方   タイマー1は、タイマー0と同様にクロック入力は1MHzですが   プリスケーラで、1:1、1:2、1:4、1:8と分周できます。   プリスケーラを1:2として、500Hzでカウンタをインクリメント   していきます。タイマー1のカウンタは、16ビットなので   最大65535x2msまでに対応できます。   1MHzで計測すると、消費電力が大きくなることを考慮し   500Hzで動かすことにしました。   500Hzで動かしているので、時間換算の距離を算出する   ときに、補正します。  これまでの内容をまとめると、以下。 /* redefine data type */ typedef unsigned char UBYTE ; typedef unsigned int UWORD ; #define OFF 0 #define ON OFF+1 #define MASKFF 0xff #define MASK30 0x30 #define MASK0F 0x0f #define MASK03 0x03 #define XCNTMAX 50 #define XCNTHALF 40 #define CNTBEGIN 6 #define DEFX 588 #define IDS GPIO.B0 #define TRG GPIO.B1 #define ECHO GPIO.B2 volatile UBYTE cflag ; volatile UBYTE sflag ; volatile UBYTE mflag ; volatile UBYTE sft ; volatile UBYTE xcnt ; volatile UWORD xbegin ; volatile UWORD xfine ; volatile UWORD xdif ; /* function prototype */ void init_usr(void); UWORD get_tcnt(void); /* interrupt handler */ void interrupt(void) { /* generate trigger */ if ( INTCON.T0IF == ON ) { /* clear flag */ INTCON.T0IF = OFF ; /* initialize */ TMR0 = CNTBEGIN ; /* increment */ xcnt++ ; /* judge START */ if ( xcnt == XCNTMAX ) { sflag = ON ; xcnt = 0 ; } /* judge EXIT */ if ( xcnt == XCNTHALF ) { cflag = ON ; } } } void main(void) { UBYTE tmp ; UBYTE i ; /* user initialize */ init_usr(); /* endless loop */ while ( ON ) { /* start handling */ if ( sflag == ON ) { /* clear flag */ sflag = OFF ; /* clear timer counter */ TMR1H = 0 ; TMR1L = 0 ; /* start timer1 */ T1CON.TMR1ON = ON ; /* send trigger */ TRG = ON ; for ( i = 0 ; i < 32 ; i++ ) ; TRG = OFF ; /* enable measure */ mflag = ON ; } /* exit handling */ if ( cflag == ON ) { /* clear flag */ cflag = OFF ; /* stop timer1 */ T1CON.TMR1ON = OFF ; /* disable measure */ mflag = OFF ; /* calculate difference */ xdif = (xfine - xbegin) >> 1 ; /* judge */ tmp = ON ; if ( xdif > DEFX ) { tmp = OFF ; } /* impress */ IDS = tmp ; } /* shift handling */ { /* shift */ sft <<= 1 ; /* masking */ sft &= 0x03 ; /* observe ECHO */ if ( ECHO == ON ) { sft |= ON ; } /* judge rising edge */ if ( sft == 1 ) { xbegin = 0 ; if ( mflag == ON ) { xbegin = get_tcnt() ; } } /* judge falling edge */ if ( sft == 2 ) { xfine = 0 ; if ( mflag == ON ) { xfine = get_tcnt() ; } } } } } /* define function body */ void init_usr(void) { /* I/O state */ GPIO = 0x00 ; /* I/O directions */ TRISIO = 0xfc ; /* bit 0,1 as output , others as input */ /* disable compare module */ CMCON = 0x07 ; /* pull-up */ WPU = 0x3c ; /* initialize Timer 0 */ { /* 4MHz/4 = 1MHz -> 1MHz/8 = 125kHz prescaler = 1:8 */ OPTION_REG = 0x02 ; /* 256 - 250 = 6 */ TMR0 = CNTBEGIN ; /* enable timer0 overflow interrupt */ INTCON.T0IE = ON ; } /* initialize Timer 1 */ { /* 4MHz/4 = 1MHz -> 1MHz/2 = 500kHz prescaler = 1:2 */ T1CON = 0x14 ; /* clear counter */ TMR1H = 0 ; TMR1L = 0 ; } /* enable general interrupt */ INTCON.GIE = ON ; /* clear flags */ cflag = OFF ; sflag = OFF ; mflag = OFF ; /* clear */ xcnt = 0 ; sft = 0 ; xbegin = 0 ; xfine = 0 ; } UWORD get_tcnt(void) { UBYTE dh ; UBYTE dl ; UWORD result ; /* get counter values */ dl = TMR1L ; dh = TMR1H ; /* calculate */ result = (dh << 8) | dl ; return result ; }

目次

inserted by FC2 system