目次
前
次
カメラ制御
カメラは、シリアルインタフェースでマイクロコンピュータに接続します。
カメラから画像データを取得するには、次の機能が必要です。
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 ;
}
初期化
カメラでの撮影から、出力画像のデータフォーマットを指定します。
次の仕様とします。
- 8ビットモノクロ
- 画像サイズ80x60
- JPEG画像は、80x64
この仕様を設定するために、関数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値化するには、スレショルドが必要です。
画像データ取得の内容を、一部改造してスレショルドを計算します。
今回は、画像の中央と両端のグレイスケール値から計算します。
計算シーケンスは、以下です。
- 画像の中央で8点を選び、グレイスケール値の合計を求める
- 画像の両端で8点を選び、グレイスケール値の合計を求める
- 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 );
}
目次
前
次