目次
前
次
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のカウンタクリア
- タイマー1のカウントアップ開始
- TRGで超音波センサーに計測開始を指示
- 計測中フラグをセット
このシーケンスをコードにします。
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のカウントアップ停止
- 計測中フラグをクリア
- 時間換算の距離を計算
- 距離に応じた論理値を出力
このシーケンスをコードにします。
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 ;
}
目次
前
次