目次

センサーデータ生成高速化

 カメラから画像データを1ライン入力するごとに、センサーデータを
 生成するコードを作成しましたが、加算処理に時間がかかり高速化を
 阻んでいました。

 CPLD/FPGAで実現したらと考えると、テーブル参照で高速化できる
 と思いつきました。

 カメラから1画素を送られると、次の処理をしています。
  1. スレショルドを比較して、1か0を決定
  2. 1で求めた値を加算
  3. 16回加算後、変数に保存
 この方法で、16ビットx8ブロックを計算しておきます。  1ライン入力すると、8ブロックの平均を求めます。  平均を求めるために、加算しています。  16回加算するのは、16ビット中の1の個数を求めたいだけなので、  この処理をテーブル参照で実行します。  16ビットを中の1の個数を求めるために、ROMを使う方法がありますが  パターンが65536通りあります。力づくで、やれないことはないですが  小さいマイコンには、パターンは入りません。  そこで、別の方法を考えます。  16ビットを4ビットが4つ並んでいると見ると、4ビット中に1が  いくつあるかをテーブルにしておけば、テーブルを4回参照して  16ビットの中に1がいくつあるかを求められます。  4ビット中の1の個数を保存しておくには、16パターンしかないので  配列に、1の個数を入れると、容量の小さいマイコンに充分入ります。  計算でテーブルを作成するためのコードは、以下となります。 UBYTE b1_cnt[16] ; void make_code(void) { UBYTE i ; UBYTE cnt ; for ( i = 0 ; i < 16 ; i++ ) { cnt = 0 ; if ( i & 1 ) { cnt++ ; } if ( i & 2 ) { cnt++ ; } if ( i & 4 ) { cnt++ ; } if ( i & 8 ) { cnt++ ; } *(b1_cnt+i) = cnt ; } }  実際にコードを記述して、ビット中の1の個数を表示すると  以下となります。 0000 -> 0 0001 -> 1 0010 -> 1 0011 -> 2 0100 -> 1 0101 -> 2 0110 -> 2 0111 -> 3 1000 -> 1 1001 -> 2 1010 -> 2 1011 -> 3 1100 -> 2 1101 -> 3 1110 -> 3 1111 -> 4  このテーブルは、4ビット中の1の個数を格納しているので  8ビット中の1の個数を求めるには、次の関数を用意します。 UBYTE get_1_cnt(UBYTE x) { UBYTE bh; UBYTE bl; UBYTE result ; /* separate */ bl = x & 15 ; x >>= 4 ; bh = x & 15 ; /* sum */ result = 0 ; result += b1_cnt[bh] ; result += b1_cnt[bl] ; return result ; }  8ビット中の1の個数を求められると、16ビット中の1の個数  を求めるには、それを利用します。 UBYTE get_1_count(UWORD x) { UBYTE bh; UBYTE bl; UBYTE result ; /* separate */ bl = x & 255 ; x >>= 8 ; bh = x & 255 ; /* sum */ result = 0 ; result += get_1_cnt(bl) ; result += get_1_cnt(bh) ; return result ; }  関数get_1_countを利用すると、16ビットごとに16画素の代表値を  計算できます。これで8ブロックの画素の代表値を計算します。  1ラインの画素を入力したならば、平均を求め、さらに8ブロック  の代表値が1か0に決めます。  8ブロックの代表値を8ビットにまとめると、センサーデータになります。  128ピクセルx128ラインのデータから、センサーデータを求める関数は  次のようになります。 void get_sensor(void) { UWORD result; UBYTE index ; UWORD res ; UBYTE i ; UBYTE sensor ; UBYTE avr ; UBYTE dh ; UBYTE dl ; /* send start */ send_start(); /* READ data and show*/ img_cnt = 0 ; index = 0 ; i = 0 ; res = 0 ; avr = 0 ; dh = 0 ; dl = 0 ; while ( img_cnt < MAX_SIZE ) { /* impress XCK:H */ set_xck(ON); /* impress XCK:L */ set_xck(OFF) ; /* judge */ if ( PINC & 2 ) { /* start conversion */ start_adc() ; /* wait */ wai_adc() ; /* get data */ dl = ADCL ; dh = ADCH ; result = (dh << 8) + dl ; /* shift */ result >>= 2 ; /* judge */ res <<= 1 ; if ( result > threshold ) { res |= 1 ; } /* generate block code */ if ( (img_cnt % 16) == 15 ) { /* store */ *(rdat+i) = get_1_count(res) ;     /* clear */ res = 0 ;     /* sum */     avr += *(rdat+i) ;     /* update pointer */ i++ ; i %= 8 ; } /* generate sensor data */ if ( (img_cnt % 128) == 127 ) { /* get average */ avr >>= 3 ; /* compare */ sensor = 0 ; if ( *(rdat+0) > avr ) { sensor |= 0x80 ; } if ( *(rdat+1) > avr ) { sensor |= 0x40 ; } if ( *(rdat+2) > avr ) { sensor |= 0x20 ; } if ( *(rdat+3) > avr ) { sensor |= 0x10 ; } if ( *(rdat+4) > avr ) { sensor |= 0x08 ; } if ( *(rdat+5) > avr ) { sensor |= 0x04 ; } if ( *(rdat+6) > avr ) { sensor |= 0x02 ; } if ( *(rdat+7) > avr ) { sensor |= 0x01 ; } /* store */ *(sdat+index) = sensor ; /* increment */ index++ ; /* clear average */ avr = 0 ; } /* increment */ img_cnt++ ; } } }  16画素を取得するごとに、データ処理をしているので多少高速になりました。  1ラインの処理後、センサーデータを生成する時間は、短くなります。  8回分の加算処理が不要になった分、高速でした。  ソフトウエアでの高速化は目を見張るほどにはなりませんが  ハードウエアでは、10%程度処理時間は短くなりました。  ソースコードを眺めていると、冗長な部分が見えてきました。  閾値で2値化し、さらに16ビットごとにまとめて、1の個数を  求めた後で、1の個数を加算しています。  加算処理は、計算時間を消費するので、1の個数の最大、最小  を求め、相加平均を計算する方式に変更します。 void get_sensor(void) { UWORD result ; UBYTE index ; UWORD res ; UBYTE i ; UBYTE sensor ; UBYTE avr ; UBYTE dh ; UBYTE dl ; UBYTE max; UBYTE min; /* send start */ send_start(); /* READ data and show*/ img_cnt = 0 ; index = 0 ; i = 0 ; res = 0 ; avr = 0 ; max = 0 ; min = 16 ; dl = 0 ; dl = 0 ; while ( img_cnt < MAX_SIZE ) { /* impress XCK:H */ set_xck(ON); /* impress XCK:L */ set_xck(OFF) ; /* judge */ if ( PINC & 2 ) { /* start conversion */ start_adc(); /* wait */ wai_adc() ; /* get data */ dl = ADCL ; dh = ADCH ; result = (dh << 8) + dl ; /* shift */ result >>= 2 ; /* judge */ res <<= 1 ; if ( result > threshold ) { res |= 1 ; } /* generate line code */ if ( (img_cnt % 16) == 15 ) { *(rdat+i) = get_1_count(res) ; /* judge */ if ( max < *(rdat+i) ) { max = *(rdat+i); } if ( min > *(rdat+i) ) { min = *(rdat+i); } /* clear */ res = 0 ; /* update pointer */ i++ ; i %= 8 ; } /* generate sensor data */ if ( (img_cnt % 128) == 127 ) { /* calculate threshold */ avr = ((max+min) >> 2) ; if ( avr < min ) { avr = min ; } /* compare */ sensor = 0 ; if ( *(rdat+0) > avr ) { sensor |= 0x80 ; } if ( *(rdat+1) > avr ) { sensor |= 0x40 ; } if ( *(rdat+2) > avr ) { sensor |= 0x20 ; } if ( *(rdat+3) > avr ) { sensor |= 0x10 ; } if ( *(rdat+4) > avr ) { sensor |= 0x08 ; } if ( *(rdat+5) > avr ) { sensor |= 0x04 ; } if ( *(rdat+6) > avr ) { sensor |= 0x02 ; } if ( *(rdat+7) > avr ) { sensor |= 0x01 ; } /* store */ *(sdat+index) = sensor ; /* increment */ index++ ; /* initialize max and min */ max = 0 ; min = 16 ; } /* increment */ img_cnt++ ; } } }  更なる高速化を考えてみます。  XCKをL→Hとしたときは、何も操作をしていません。  XCKをH→Lにしたときに、一気に仕事をする仕様です。  オシロスコープでXCKを見ると、DUTY比が1:1ではなく  アンバランスです。XCKをL→Hとしたとき、センサー  データを生成すれば効率がよくなりそうです。  XCKをL→Hとしたとき、センサーデータを生成し  XCKをH→Lにしたとき、2値化と16ビット中の1の個数  を求めるようにします。  センサーデータを生成するときは、XCKをH→Lのステージで  トリガーフラグを立てておき、そのフラグによりデータ生成  するか否かを判断します。  この方法では、127ライン目のセンサーデータを生成できない  のですが、実際に利用していないので、問題はないです。 void get_sensor(void) { UWORD result ; UBYTE index ; UWORD res ; UBYTE i ; UBYTE sensor ; UBYTE avr ; UBYTE dh ; UBYTE dl ; UBYTE max; UBYTE min; UBYTE max_x; UBYTE min_x; UBYTE exeflag ; /* send start */ send_start(); /* READ data and show*/ img_cnt = 0 ; index = 0 ; i = 0 ; res = 0 ; avr = 0 ; max = 0 ; min = 16 ; max_x = 0 ; min_x = 16 ; dl = 0 ; dl = 0 ; exeflag = OFF ; while ( img_cnt < MAX_SIZE ) { /* impress XCK:H */ set_xck(ON); /* generate sensor data */ if ( exeflag == ON ) { /* clear trigger */ exeflag = OFF ; /* calculate threshold */ avr = ((max_x+min_x) >> 2) ; if ( avr < min_x ) { avr = min_x ; } /* compare */ sensor = 0 ; if ( *(rdatx+0) > avr ) { sensor |= 0x80 ; } if ( *(rdatx+1) > avr ) { sensor |= 0x40 ; } if ( *(rdatx+2) > avr ) { sensor |= 0x20 ; } if ( *(rdatx+3) > avr ) { sensor |= 0x10 ; } if ( *(rdatx+4) > avr ) { sensor |= 0x08 ; } if ( *(rdatx+5) > avr ) { sensor |= 0x04 ; } if ( *(rdatx+6) > avr ) { sensor |= 0x02 ; } if ( *(rdatx+7) > avr ) { sensor |= 0x01 ; } /* store */ *(sdat+index) = sensor ; /* increment */ index++ ; } /* impress XCK:L */ set_xck(OFF) ; /* judge */ if ( PINC & 2 ) { /* start conversion */ start_adc(); /* wait */ wai_adc() ; /* get data */ dl = ADCL ; dh = ADCH ; result = (dh << 8) + dl ; /* shift */ result >>= 2 ; /* judge */ res <<= 1 ; if ( result > threshold ) { res |= 1 ; } /* generate line code */ if ( (img_cnt % 16) == 15 ) { *(rdat+i) = get_1_count(res) ; /* judge */ if ( max < *(rdat+i) ) { max = *(rdat+i); } if ( min > *(rdat+i) ) { min = *(rdat+i); } /* clear */ res = 0 ; /* update pointer */ i++ ; i %= 8 ; /* transfer */ if ( i == 0 ) { /* transfer */ max_x = max ; min_x = min ; *(rdatx+0) = *(rdat+0) ; *(rdatx+1) = *(rdat+1) ; *(rdatx+2) = *(rdat+2) ; *(rdatx+3) = *(rdat+3) ; *(rdatx+4) = *(rdat+4) ; *(rdatx+5) = *(rdat+5) ; *(rdatx+6) = *(rdat+6) ; *(rdatx+7) = *(rdat+7) ; /* initialize max and min */ max = 0 ; min = 16 ; } } /* trigger */ if ( (img_cnt % 128) == 127 ) { exeflag = ON ; } /* increment */ img_cnt++ ; } } }
目次

inserted by FC2 system