目次

カメラ制御

 カメラは、シリアルインタフェースでマイクロコンピュータに接続します。

 カメラから画像データを取得するには、次の機能が必要です。

 3つに分けて、ファームウエアの内容を検討します。


通信確立

 電源を入れた状態で、SYNCコマンドを最大60回送信します。  SYNCコマンド送信のいずれかの回で、ACKを返してきます。  ACKが返信されたならば、通信確立です。  デフォルトでは、通信速度は14400bpsですが、115200bpsや  57600bpsで送信しても、自動で認識してくれます。  今回は、57600bpsで送信します。一度、57600bpsで通信が確立  すると、電源を切るまで、その状態を維持します。  コマンドは6バイトなので、SYNCコマンドを送信する独立した  関数を定義します。 void send_c328(UBYTE x,UWORD p0,UWORD p1) { rs_putchar( 0xAA ); rs_putchar( x ); rs_putchar( (UBYTE)((p0 >> 8) & 0xff) ); rs_putchar( (UBYTE)(p0 & 0xff) ); rs_putchar( (UBYTE)((p1 >> 8) & 0xff) ); rs_putchar( (UBYTE)(p1 & 0xff) ); } void send_sync(void) { send_c328( 0x0D , 0x0000 , 0x0000) ; }  データシートの動作シーケンスは、下図です。  シーケンス図から、ACKコマンドの送信も必要なので、関数定義します。 void send_ack(UBYTE x) { send_c328( 0x0E , (x << 8) | 0x00 , 0x0000) ; }  レスポンスが6バイトずつ返送されるので、返送されてくるデータを  受信割込みで、リングバッファに保存します。  リングバッファ操作は、後で考えるとして、リングバッファに入っている  文字数をget_capacity関数で取得できるものとします。  また、リングバッファから1文字取得するのに、get_ring関数を使えると  しておきます。  60回ループを実行して、そのうちでリングバッファの中に12文字が検出  されたならば、ループをぬける処理とします。    for ( i = 0 ; i < 60 ; i++ ) {    /* send SYNC command */    send_sync();    /* wait */    delay_ms( 10 ) ;    /* judge */    if ( get_capacity() < 12 ) continue ;    /* get data from buffer */    length = get_capacity() ;    for ( i = 0 ; i < length ; i++ ) {    if ( i > 11 ) { result = get_ring() ; }    else { *(dummy+i) = get_ring() ; }    }    /* */    gflag = ON ;    if ( gflag ) break ;    }  正常であれば、12文字がリングバッファに入りますが、何かの  拍子に13文字以上になることも考えられます。  リングバッファに入っている文字数は、get_capacity関数で  把握できるので、ゴミで入っているデータがある場合には、  リングバッファから排出します。  リングバッファに12文字入ったならば、取り出しておきます。  正しいレスポンスになっているかを確認します。 yflag0 = OFF ; yflag1 = OFF ;   if ( *(dummy+1) == 0x0E && *(dummy+2) == 0x0D ) { yflag0 = ON ; }   if ( *(dummy+7) == 0x0D && *(dummy+8) == 0x00 ) { yflag1 = ON ; }  通信確立の前に、リングバッファにゴミは入っている場合もあるので  リングバッファを空にしておきます。   length = get_capacity() ;   if ( length ) { for ( i = 0 ; i < length ; i++ ) { result = get_ring() ; } }  60回のループ中に、レスポンスがあり、正しいレスポンスであれば  OKを出します。   UBYTE establish_c328(void)   {    UWORD i ;    UWORD length ;    UBYTE result,gflag ;    UBYTE yflag0,yflag1 ;    /* default */    gflag = OFF ;    yflag0 = OFF ;    yflag1 = OFF ;    if ( DFLAG ) { rs_puts_txd1((UBYTE *)" _SYNC") ; }    /* flush */    length = get_capacity() ;    if ( length ) { for ( i = 0 ; i < length ; i++ ) { result = get_ring() ; } }    /* looping */    for ( i = 0 ; i < 60 ; i++ ) {    /* send SYNC command */    send_sync();    /* wait */    delay_ms( 10 ) ;    /* judge */    if ( get_capacity() < 12 ) continue ;    /* get data from buffer */    length = get_capacity() ;    for ( i = 0 ; i < length ; i++ ) {    if ( i > 11 ) { result = get_ring() ; }    else { *(dummy+i) = get_ring() ; }    }    /* */    gflag = ON ;    if ( gflag ) break ;    }    /* judge */    if ( *(dummy+1) == 0x0E && *(dummy+2) == 0x0D ) { yflag0 = ON ; }    if ( *(dummy+7) == 0x0D && *(dummy+8) == 0x00 ) { yflag1 = ON ; }    /* send ACK */    result = gflag & yflag0 & yflag1 ;    if ( result ) {    send_ack( 0x0D ) ;    if ( DFLAG ) { rs_puts_txd1((UBYTE *)" _established") ; }    if ( yflag0 ) { rs_puts_txd1((UBYTE *)" _ACK OK! ") ; }    if ( yflag1 ) { rs_puts_txd1((UBYTE *)" _SYNC send ") ; }    }    return result ;   }

初期化

 カメラでの撮影から、出力画像のデータフォーマットを指定します。  次の仕様とします。  この仕様を設定するために、関数send_initを定義します。 void send_init(void) { send_c328( 0x01 , 0x0003 , 0x0101 ) ; }  また、スナップショットでデータを取得したいので、関数send_snapshot を定義します。 void send_snapshot(void) { send_c328( 0x05 , 0x0100 , 0x0000 ); }  動作は、下図です。  単純に、コマンドを送信し、レスポンスが正しいかを判定します。  UBYTE init_c328(void) { UWORD i ; UWORD length ; UBYTE result ; UBYTE yflag0 ; UBYTE yflag1 ; UBYTE tmp ; /* set BUSY */ BFLAG = ON ; /* default */ yflag0 = OFF ; /* send SYNC command */ send_init(); /* wait response */ delay_ms(20) ; /* get data from buffer */ length = get_capacity() ; for ( i = 0 ; i < 6 ; i++ ) { *(dummy+i) = get_ring() ; } if ( *(dummy+1) == 0x0E && *(dummy+2) == 0x01 ) { yflag0 = ON ; } /* send snap shot */ send_snapshot(); /* wait response */ delay_ms(20) ; /* get data from buffer */ length = get_capacity() ; for ( i = 6 ; i < 12 ; i++ ) { *(dummy+i) = get_ring() ; } if ( *(dummy+7) == 0x0E && *(dummy+8) == 0x05 ) { yflag1 = ON ; } /* reset BUSY */ BFLAG = OFF ; return yflag1 & yflag0 ; }

画像データ取得

 画像データ取得のシーケンスは、次のようになっています。  画像取得のコマンドを送信し、レスポンスでACKを受け、さらに  画像サイズを受け取ります。  その後、画像データを受信し、データがなくなった時点でACKを出します。  画像取得コマンドが必要なので、関数send_get_imgとして定義します。 void send_get_img(void) { send_c328( 0x04 , 0x0100 , 0x0000 ); }  画像データフォーマットは、Snapshotとします。  (コマンドの3バイト目を0x01と指定)  画像データを受取りつつ、2値化します。  さらに、2値化したデータからセンサーデータを生成します。  ACK、Dataで指定される内容を確認し、画像サイズを取得して  から、画像データ受信とセンサーデータ生成に続けます。 UBYTE get_c328(void) { UWORD i ; UWORD j ; ULONG length ; UBYTE k ; UBYTE resx ; UBYTE result ; UBYTE yflag0 ; UBYTE yflag1 ; /* set status */ SBUSY = ON ; /* sweap */ length = get_capacity(); if ( length ) { for ( i = 0 ; i < (UWORD)length ; i++ ) { result = get_ring() ;} } /* send get picture command */ send_get_img(); /* wait */ while ( ON ) { result = get_capacity() ; if ( result >= 12 ) break ; delay_ms(1); } /* copy */ for ( i = 0 ; i < 12 ; i++ ) { *(dummy+i) = get_ring() ; } /* judge */ yflag0 = OFF ; yflag1 = OFF ; if ( *(dummy+1) == 0x0E && *(dummy+2) == 0x04 ) { yflag0 = ON ; } if ( *(dummy+7) == 0x0A && *(dummy+8) == 0x01 ) { yflag1 = ON ; } /* calculate size */ length = *(dummy+11) ; length <<= 8 ; length += *(dummy+10) ; length <<= 8 ; length += *(dummy+9 ) ; if ( length > 32767 ) { /* flush */ length = get_capacity(); for ( i = 0 ; i < length ; i++ ) { result = get_ring(); } return OFF ; } /* get image data */ k = 0 ; resx = 0 ; for ( i = 0 ; i < (UWORD)length ; i++ ) { /* wait receive graphic data */ while ( get_capacity() <= 0 ) ; /* get 1 pixel */ result = get_ring(); /* judge */ if ( result > threshold ) { resx++ ; } /* block summuation */ j = i % 10 ; if ( j == 9 ) { /* store summuation */ *(gdat+k) = resx ; /* clear block summuation */ resx = 0 ; /* pointer increment */ k++ ; } } /* send ACK */ send_ack( 0x0A ); /* generate sensor data */ SCRANK = OFF ; SLANERIGHT = OFF ; SLANELEFT = OFF ; for ( i = 0 ; i < LINE_MAX ; i++ ) { /* index */ j = (i < 3) ; /* summuation */ resx = 0 ; resx += *(gdat+j+0) ; resx += *(gdat+j+1) ; resx += *(gdat+j+2) ; resx += *(gdat+j+3) ; resx += *(gdat+j+4) ; resx += *(gdat+j+5) ; resx += *(gdat+j+6) ; resx += *(gdat+j+7) ; /* average */ resx >>= 3 ; /* convert */ result = 0 ; if ( *(gdat+j+0) > result ) { result |= 0x80 ; } if ( *(gdat+j+1) > result ) { result |= 0x40 ; } if ( *(gdat+j+2) > result ) { result |= 0x20 ; } if ( *(gdat+j+3) > result ) { result |= 0x10 ; } if ( *(gdat+j+4) > result ) { result |= 0x08 ; } if ( *(gdat+j+5) > result ) { result |= 0x04 ; } if ( *(gdat+j+6) > result ) { result |= 0x02 ; } if ( *(gdat+j+7) > result ) { result |= 0x01 ; } /* reverse store */ *(senx+59-i) = result ; /* judge CRANK */ if ( result == 0xff ) { SCRANK = ON ; } if ( result == 0xf0 || result == 0xf8 ) { SLANELEFT = ON ; } if ( result == 0x0f || result == 0x1f ) { SLANERIGHT = ON ; } } /* reset status */ SBUSY = OFF ; return ON ; }

スレショルド計算

 画像データを2値化するには、スレショルドが必要です。  画像データ取得の内容を、一部改造してスレショルドを計算します。  今回は、画像の中央と両端のグレイスケール値から計算します。  計算シーケンスは、以下です。
  1. 画像の中央で8点を選び、グレイスケール値の合計を求める
  2. 画像の両端で8点を選び、グレイスケール値の合計を求める
  3. 1、2の相加平均をスレショルドにする
 画像の中央は、ライン番号14、29、44、59の4ラインをえらび  39ピクセル目と49ピクセル目のグレイスケール値を利用します。  画像の両端は、ライン番号14、29、44、59の4ラインをえらび  0ピクセル目と79ピクセル目のグレイスケール値を利用します。  画像の中央の指定ピクセルかを判定する関数is_middleは、以下です。 UBYTE is_middle(UWORD x) { UBYTE result ; result = OFF ; if ( x == 1159 ) { result = ON ; } if ( x == 1160 ) { result = ON ; } if ( x == 2359 ) { result = ON ; } if ( x == 2360 ) { result = ON ; } if ( x == 3559 ) { result = ON ; } if ( x == 3540 ) { result = ON ; } if ( x == 4759 ) { result = ON ; } if ( x == 4760 ) { result = ON ; } return result ; }  画像の両端の指定ピクセルかを判定する関数is_edgeは、以下です。 UBYTE is_edge(UWORD x) { UBYTE result ; result = OFF ; if ( x == 1120 ) { result = ON ; } if ( x == 1179 ) { result = ON ; } if ( x == 2320 ) { result = ON ; } if ( x == 2399 ) { result = ON ; } if ( x == 3520 ) { result = ON ; } if ( x == 3599 ) { result = ON ; } if ( x == 4720 ) { result = ON ; } if ( x == 4799 ) { result = ON ; } return result ; }  2つの関数を使いピクセル位置を求めて、スレショルドを計算します。 UBYTE generate_threshold(void) { UWORD i ; ULONG length ; UBYTE resx ; UBYTE resy ; UBYTE result ; UBYTE yflag0 ; UBYTE yflag1 ; /* set status */ SBUSY = ON ; /* sweap */ length = get_capacity(); if ( length ) { for ( i = 0 ; i < (UWORD)length ; i++ ) { result = get_ring() ;} } /* send get picture command */ send_get_img(); /* wait */ while ( ON ) { result = get_capacity() ; if ( result >= 12 ) break ; delay_ms(1); } /* copy */ for ( i = 0 ; i < 12 ; i++ ) { *(dummy+i) = get_ring() ; } /* judge */ yflag0 = OFF ; yflag1 = OFF ; if ( *(dummy+1) == 0x0E && *(dummy+2) == 0x04 ) { yflag0 = ON ; } if ( *(dummy+7) == 0x0A && *(dummy+8) == 0x01 ) { yflag1 = ON ; } /* calculate size */ length = *(dummy+11) ; length <<= 8 ; length += *(dummy+10) ; length <<= 8 ; length += *(dummy+9 ) ; if ( length > 32767 ) { /* flush */ length = get_capacity(); for ( i = 0 ; i < length ; i++ ) { result = get_ring(); } return OFF ; } /* get image data */ resx = 0 ; resy = 0 ; for ( i = 0 ; i < (UWORD)length ; i++ ) { /* wait receive graphic data */ while ( get_capacity() <= 0 ) ; /* get 1 pixel */ result = get_ring(); /* judge */ if ( is_middle(i) ) { resx += result ; } if ( is_edge(i) ) { resy += result ; } /* calculate threshold and store it */ if ( i == 4799 ) { resx >>= 3 ; resy >>= 3 ; threshold = ((resx+resy) >> 1) ; } } /* send ACK */ send_ack( 0x0A ); /* reset status */ SBUSY = OFF ; return ON ; }

リングバッファ処理

 通信中のデータを取りこぼさないよう、リングバッファを使います。  512バイトのバッファを確保し、リード、ライトのポインタと  格納しているデータ数を示す変数を使います。 #define RINGP_SIZE 512 typedef struct { UBYTE data[RINGP_SIZE]; UWORD capacity; UWORD rdp; UWORD wrp; } RINGP ; volatile RINGP rings;  リングバッファに1文字格納する関数を、put_ringとします。 void put_ring(UBYTE x) { /* store data */ sring.dat[sring.wp] = x ; /* update pointer */ sring.wp += 1 ; if ( sring.wp == RINGP_SIZE ) { sring.wp = 0 ; } /* capacity increment */ sring.cap += 1 ; }  リングバッファから1文字取得する関数を、get_ringとします。 UBYTE get_ring(void) { UBYTE result ; /* load data */ result = sring.dat[sring.rp] ; /* update pointer */ sring.rp += 1 ; if ( sring.rp == RINGP_SIZE ) { sring.rp = 0 ; } /* capacity decrement */ sring.cap -= 1 ; return result ; }  リングバッファに格納されているデータ数を返す関数を、get_capacityとします。 UBYTE get_capacity(void) { return rings.capacity ; }  リングバッファの初期化を、関数init_ringで定義します。 void init_ring(void) { /* clear capacity */ rings.capacity = 0 ; /* initialize read pointer */ rings.rdp = 0 ; /* initialize write pointer */ rings.wrp = 0 ; }

受信割込み

 カメラから送信される文字は、受信割込みで取得します。  受信割込みが発生すると、1文字入力し、関数put_ringで  リングバッファに格納していきます。  フレーミングエラー、オーバーランエラーがないときに  データをリングバッファに保存します。  擬似コーディングで、次のように記述します。 volatile UBYTE ch ; if ( フレーミングエラーなし ) { /* get 1 charactoer */ ch = UDR0 ; /* store */ put_ring( ch ); }
目次

inserted by FC2 system