目次
前
次
センサーデータ生成高速化
カメラから画像データを1ライン入力するごとに、センサーデータを
生成するコードを作成しましたが、加算処理に時間がかかり高速化を
阻んでいました。
CPLD/FPGAで実現したらと考えると、テーブル参照で高速化できる
と思いつきました。
カメラから1画素を送られると、次の処理をしています。
- スレショルドを比較して、1か0を決定
- 1で求めた値を加算
- 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++ ;
}
}
}
目次
前
次