目次
前
次
GameBoyCamera処理再考
MCR_VCマシンに利用しているモータは、思いの他
パワーがあるので、GameBoyCameraを載せることに
しました。
R-2RのD/A変換器を利用すれば、A/D変換器を
逐次比較で実現できます。
SENXにGBCの変換出力Voutを接続しておけば
逐次比較でA/D変換できます。
GameBoyCamera(GBC)からのデータは、クロックXCKに
同期してVoutから出力されます。READが'H'になって
いれば、データを変換しFPGA内部メモリに保存。
このシーケンスを採用すれば、FPGA外部にSRAMを
用意することなく、画像データを保存できます。
タイミングチャートでみると、次のようになります。
READが'H'かつXCKの立下りでトリガーを作り
逐次比較してVoutの値を入力。
トリガーが生成されたとして、シフトレジスタに
1画素分のデータを取り込むVHDLコードは、以下。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity taa is
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- clock
XCK : out std_logic_vector(1 downto 0);
-- ENABLE
XREAD : in std_logic ;
-- trigger
GTRG : in std_logic ;
-- data
VAD : in std_logic ;
-- data valid
DENA : out std_logic ;
-- result
SFT : out std_logic_vector(7 downto 0) ;
-- output
OST : out std_logic_vector(7 downto 0) --;
);
end taa ;
architecture behavioral of taa is
-- clock
signal iXCNT : integer range 0 to 3 ;
signal iXCK : std_logic_vector(1 downto 0) ;
-- state machine
signal iSCNT : integer range 0 to 8 ;
signal iSTATE : std_logic_vector(2 downto 0) ;
signal iGTRG : std_logic ;
signal iSFT : std_logic_vector(7 downto 0) ;
signal iOST : std_logic_vector(7 downto 0) ;
begin
-- output
OST <= iOST ;
XCK <= iXCK ;
SFT <= iSFT ;
-- input
iGTRG <= (not GTRG) and XREAD ;
-- clock divider
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iXCNT <= 0 ;
elsif rising_edge( CLOCK ) then
iXCNT <= iXCNT + 1 ;
end if ;
end process ;
iXCK <= "01" when ( iXCNT = 1 ) else
"11" when ( iXCNT = 2 ) else
"10" when ( iXCNT = 3 ) else
"00" ;
-- state machine
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "000" ;
iSFT <= X"00" ;
iSCNT <= 0 ;
elsif rising_edge( CLOCK ) then
case conv_integer(iSTATE) is
-- trigger
when 0 => if ( iGTRG = '1' ) then
iSTATE <= "001" ;
iSFT <= X"00" ;
iSCNT <= 0 ;
else
iSTATE <= "000" ;
end if ;
-- judge
when 1 => if ( iSCNT = 8 ) then
iSTATE <= "110" ;
else
iSTATE <= "011" ;
end if ;
-- get data
when 3 => iSTATE <= "111" ;
iSFT <= iSFT(6 downto 0) & VAD ;
-- impress
when 7 => iSTATE <= "001" ;
iSCNT <= iSCNT + 1 ;
-- copy
when 6 => iSTATE <= "100" ;
-- return first state
when 4 => iSTATE <= "000" ;
when others =>
iSTATE <= "000" ;
end case ;
end if ;
end process ;
iOST <= X"7F" when ( iSCNT = 1 ) else
X"3F" when ( iSCNT = 2 ) else
X"1F" when ( iSCNT = 3 ) else
X"0F" when ( iSCNT = 4 ) else
X"07" when ( iSCNT = 5 ) else
X"03" when ( iSCNT = 6 ) else
X"01" when ( iSCNT = 7 ) else
X"FF" ;
DENA <= '1' when ( iSTATE = "110" ) else '0' ;
end behavioral;
UCFの内容は、以下。
# system
NET "CLOCK" LOC = "P5" ;
NET "nRESET" LOC = "P39" ;
# GBC clock
NET "XCK<0>" LOC = "P1" ;
NET "XCK<1>" LOC = "P2" ;
# sensor data
NET "SFT<0>" LOC = "P11" ;
NET "SFT<1>" LOC = "P12" ;
NET "SFT<2>" LOC = "P13" ;
NET "SFT<3>" LOC = "P14" ;
NET "SFT<4>" LOC = "P18" ;
NET "SFT<5>" LOC = "P19" ;
NET "SFT<6>" LOC = "P20" ;
NET "SFT<7>" LOC = "P22" ;
#
NET "OST<0>" LOC = "P24" ;
NET "OST<1>" LOC = "P25" ;
NET "OST<2>" LOC = "P26" ;
NET "OST<3>" LOC = "P27" ;
NET "OST<4>" LOC = "P28" ;
NET "OST<5>" LOC = "P29" ;
NET "OST<6>" LOC = "P33" ;
NET "OST<7>" LOC = "P34" ;
# XCK
NET "DENA" LOC = "P40" ;
NET "XREAD" LOC = "P42" ;
NET "VAD" LOC = "P43" ;
NET "GTRG" LOC = "P44" ;
XilinxのXC9536に入れてみると、29マクロセルを消費
していました。
FPGA内部のメモリにデータ保存するには、アドレスを
ゼロクリアするのと、インクリメントが必要。
変換開始は、制御信号STARTを利用するので、この信号で
FPGA内部のメモリアドレスをゼロクリアします。
アドレスインクリメントは、シフトレジスタへのデータ
保存終了を通知するフラグがあるので、このフラグを
参照してデータ格納後、アドレスを+1します。
メモリへのデータ保存の担当するシーケンサ(ステート
マシン)とメモリアドレスを操作するシーケンサを別途
用意します。
メモリアドレス操作シーケンサ
データメモリはFPGA内部にあるので、動作はシステム
クロックを利用して、他の回路ブロックからの指令を
トリガーで受け取って対応。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iFSADR <= X"0000" ;
iSTATE <= "00" ;
elsif rising_edge( CLOCK ) then
case conv_integer(iSTATE) is
-- trigger
when 0 => if ( iRSTA = '1' ) then
iSTATE <= "01" ;
elsif ( iINCA = '1' ) then
iSTATE <= "11" ;
else
iSTATE <= "00" ;
end if ;
-- clear address
when 1 => iSTATE <= "10" ;
iFSADR <= X"0000" ;
-- increase address
when 3 => iSTATE <= "10" ;
iFSADR <= iFSADR + '1' ;
-- return first state
when 2 => iSTATE <= "00" ;
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
データ保存処理シーケンサ
データ保存は、シフトレジスタ処理回路から出力されるDENA
をトリガーとしてつかます。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
iDREG <= X"00" ;
elsif rising_edge( CLOCK ) then
case conv_integer(iSTATE) is
-- trigger
when 0 => if ( iDENA = '1' ) then
iSTATE <= "01" ;
iDREG <= X"00" ;
else
iSTATE <= "00" ;
end if ;
-- copy data
when 1 => iSTATE <= "11" ;
iDREG <= DOUT ;
-- store data
when 3 => iSTATE <= "10" ;
-- return first state
when 2 => iSTATE <= "00" ;
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
-- increase address
iINCA <= '1' when ( iSTATE = "10" ) else '0' ;
XCKは、最大500kHzですがシフトレジスタを動かす
シーケンサのクロックは、この20倍以上とします。
10MHzあれば、充分処理できます。
メモリをFPGA内部に用意しますが、これは256バイト
程度で充分です。GBCは1ライン=128ワードですが
その128ワードの中で真ん中の64ワード程度が走行
に必要な路面情報になっています。この見方で考えると
1ワード2バイトとして64x2バイト=128バイト
で充分となります。
GBCを利用するには、リセットと初期化が必要です。
リセットと初期化は、動作開始の一度だけで充分なので
外部プロセッサを利用します。
ポンチ絵で示すと、以下。
外部プロセッサにPICを利用したのは、手持ちで¥100
程度のPIC12F1501が3個ほどあったので、それを利用する
と考えたため。
PIC、FPGAの機能割り当ては、以下。
PIC担当内容
GBCのリセットおよびパラメータ設定。
GBCのリセットは、次のタイミングチャートを実現するだけ。
XRSTをLOWにした状態で、XCKのクロックを入れるだけ
なので、とても簡単です。
パラメータは、8バイト分の内部レジスタに次の
タイミングチャートで1バイトずつ設定。
アドレス+パラメータを11ビットにしてから
シリアル転送します。LOADの処理が面倒です
が、Cで記述したコードがあるので、移植する
だけ。
リセットとパラメータ設定が終われば、FPGAに
対してトリガーを与えて、処理開始を宣言。
ここまで動作がわかれば、PICのファームウエアは
以下のように定義できます。
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
#define OFF 0
#define ON OFF+1
#define MASK 0x0400
#define CNTBEGIN 6
#define PORTA.F0 PXCK
#define PORTA.F1 PXRST
#define PORTA.F2 PSIN
#define PORTA.F4 PLOAD
#define PORTA.F5 PTRG
volatile UWORD timcnt ;
volatile UBYTE state ;
volatile UWORD pat[8] ;
/* function prototype */
void init_usr(void);
void snd_rst(void);
void snd_param(UWORD x);
void snd_trg(void);
void delay_ms(UBYTE x);
/* interrupt handler */
void interrupt(void)
{
/* generate 1kHz */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = CNTBEGIN ;
/* increment */
timcnt++ ;
}
}
void main(void)
{
UBYTE idx ;
/* initialize */
init_usr() ;
/* endless loop */
state = 0 ;
while ( ON ) {
/* reset */
if ( state == 0 ) { snd_rst(); }
/* send parameters */
if ( 0 < state && state < 9 ) {
idx = state - 1 ;
snd_param( *(pat+idx) );
}
/* dynamic stop */
state++ ;
if ( state == 11 ) { state = 10 ; }
}
}
/* define function body */
void init_usr(void)
{
/* select 4MHz */
OSCCON = (0x0d << 3) | 0x03 ;
/* disable A/D converter */
ADCON0.ADON = OFF ;
ADCON2 = 0 ;
/* disable D/A converter */
DACCON0.DACEN = OFF ;
/* disable compare module */
CM1CON0.C1ON = OFF ;
CM1CON0.C1OE = OFF ;
/* I/O state */
PORTA = 0x02 ;
LATA = 0x02 ;
/* I/O directions */
TRISA = 0x08 ; /* bit0,1,2,4,5 as output */
/* initialize Timer 0 */
{
/*
4MHz/4 = 1MHz -> 1MHz/2 = 250kHz prescaler = 1:4
*/
OPTION_REG = 0x01 ;
/* 256 - 6 = 250 */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.TMR0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* initialize variables */
timcnt = 0 ;
*(pat+0) = 0x80 ;
*(pat+1) = 0x03 | (1 << 8) ;
*(pat+2) = 0x00 | (2 << 8) ;
*(pat+3) = 0x30 | (3 << 8) ;
*(pat+4) = 0x01 | (4 << 8) ;
*(pat+5) = 0x00 | (5 << 8) ;
*(pat+6) = 0x01 | (6 << 8) ;
*(pat+7) = 0x21 | (7 << 8) ;
}
void snd_rst(void)
{
/* L */
PXRST = OFF ;
/* XCK : H */
PXCK = ON ;
/* XCK : L */
PXCK = OFF ;
/* H */
PXRST = ON ;
}
void snd_param(UWORD x)
{
UWORD tmp ;
UBYTE i ;
/* copy */
tmp = x & 0x03ff ;
/* loop */
for ( i = 0 ; i < 11 ; i++ ) {
/* impress data */
PSIN = OFF ;
if ( tmp & MASK ) { PSIN = ON ;}
/* XCK : H */
PXCK = ON ;
/* shift */
tmp <<= 1 ;
/* XCK : L */
PXCK = OFF ;
}
/* default */
PSIN = OFF ;
PLOAD = OFF ;
}
void snd_trg(void)
{
/* H */
PTRG = ON ;
/* delay */
delay_ms(1);
/* L */
PTRG = OFF ;
}
void delay_ms(UBYTE x)
{
UWORD last ;
/* target */
last = timcnt + x ;
/* dalay */
while ( timcnt < last ) ;
}
このファームウエアは、1kワードで充分でした。
FPGA担当内容
PICから出力された信号をGBCへトスするのと
GBCが出力してくる画像データを内部メモリに
保存し、画像処理します。
PICからの信号をGBCにトスするのは、単純に
FPGA内部のシーケンサのステートにより制御
します。
VHDLコードは、単純で次のようにAND回路を使います。
-- enable or disable
iPICENABLE <= '1' when ( iFSTATE = 0 ) else '0' ;
-- XCK
iPXCK <= PXCK and iPICENABLE ;
iFXCK <= iGXCK and (not iPICENABLE) ;
iXCK <= iFXCK or iPXCK ;
-- XRST
iXRST <= PXRST and iPICENABLE ;
-- SIN
iSIN <= PSIN and iPICENABLE ;
-- LOAD
iLOAD <= PLOAD and iPICENABLE ;
XCKの方は、少し面倒なので後でゆっくり考えます。
PICがリセットとパラメータ設定が終わったことを
PTRGを利用して通知して貰えば、画像処理を開始
する仕様に。
同期のためにシフトレジスタを利用します。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPTRG_SFT <= "000" ;
elsif rising_edge( CLOCK ) then
iPTRG_SFT <= iPTRG_SFT(1 downto 0) & PTRG ;
end if ;
end process ;
iPTRG <= '1' when ( iPTRG_SFT = "011" ) else '0' ;
トリガーが入れば、START、READ、Voutを使う
シーケンサが動作するようにもっていきます。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iFSTATE <= 0 ;
iFCNT <= 0 ;
elsif rising_edge( CLOCK ) then
case iFSTATE is
-- wait trigger
when 0 => if ( iPTRG = '1' ) then
iFSTATE <= 1 ;
else
iFSTATE <= 0 ;
end if ;
-- send start (impress)
when 1 => iFSTATE <= 2 ;
-- send start (XCK:H)
when 2 => iFSTATE <= 3 ;
-- send start (XCK:L)
when 3 => iFSTATE <= 4 ;
-- wait READ
when 4 => if ( READ = '1' ) then
iFSTATE <= 7 ;
else
iFSTATE <= 5 ;
end if ;
-- send XCK : H
when 5 => iFSTATE <= 6 ;
-- send XCK : L
when 6 => iFSTATE <= 4 ;
-- data handling
when 7 => iFSTATE <= 8 ;
-- wait
when 8 => if ( iDEXIT = '1' ) then
iFSTATE <= 9 ;
iFCNT <= iFCNT + 1 ;
else
iFSTATE <= 8 ;
end if ;
-- judge
when 9 => if ( iFCNT = FCNTLAST ) then
iFSTATE <= 12 ;
else
iFSTATE <= 10 ;
end if ;
-- send XCK : H
when 10 => iFSTATE <= 11 ;
-- send XCK : L
when 11 => iFSTATE <= 8 ;
-- send data handling trigger
when 12 => iFSTATE <= 13 ;
-- loop
when 13 => iFSTATE <= 1 ;
iFCNT <= 0 ;
-- default
when others =>
iFSTATE <= 0 ;
end case ;
end if ;
end process ;
iSTART <= '1' when ( iFSTATE = 1 or iFSTATE = 2 ) else '0';
iGXCK <= '1' when ( iFSTATE = 2 ) else
'1' when ( iFSTATE = 5 ) else
'0';
iDFLAG <= '1' when ( iFSTATE = 7 ) else '0';
iDHAND <= '1' when ( iFSTATE = 12 ) else '0';
ハードウエアの半田付けが面倒なので、次の回路でも
2値化はできると考え直しました。
GBCの出力電圧を2倍して、4Vppとしておき
コンパレータで2値化します。
ハードウエアで一気に2値化するので、コンパレータ
出力を、3.3Vに接続すると、マイコン、FPGAの双方で
2値化したピクセルデータを取得できます。
OPアンプの動作速度は、高速な方がよいのでCMOSの
NJU7043Dを使えばよいでしょう。¥70で2値化の
ハードウエアが実現できるのなら、安いもの。
汎用ゲートICを、OPアンプと同じように利用することも
できるので、次の回路で増幅と2値化が可能。
閾値の調整は、フィードバック抵抗値を可変して対応します。
Arduinoでテストする場合のスケッチは、以下。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
#define MASK0F 0x0f
#define MASKFF 0xff
#define START_BIT 2
#define SIN_BIT 5
#define LOAD_BIT 4
#define XRST_BIT 3
#define XCK_BIT 2
#define READ_BIT 1
#define LED_BIT 5
#define CMP_BIT 4
#define IDX_MAX 255
#define PDX_MAX 16384
#define PIXLAST 9000
boolean uflag ;
byte tmp ;
byte xcnt ;
word scnt ;
char sbuf[8];
byte sindex ;
char cmd ;
word params[8] ;
byte rnum ;
byte lnum ;
word gdat[64];
byte bgdat[8] ;
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *x)
{
while ( *x != 0 ) {
rs_putchar( *x ) ;
x++ ;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("P set parameters"); crlf();
rs_puts("p show parameters"); crlf();
rs_puts("L set line number"); crlf();
rs_puts("l show line number"); crlf();
rs_puts("G get graphic data"); crlf();
rs_puts("S show graphic data"); crlf();
}
byte get_hex(char x)
{
byte result ;
/* default */
result = 0 ;
/* convert */
if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
char get_asc(byte x)
{
char result ;
/* default */
result = '0' ;
/* convert */
if ( x < 10 ) { result = x + '0' ; }
if ( 9 < x && x < 16 ) { result = x - 10 + 'A' ; }
return result ;
}
void put_sin(boolean x)
{
if ( x ) { PORTC |= (1 << SIN_BIT) ; }
else { PORTC &= ~(1 << SIN_BIT) ; }
}
void put_load(boolean x)
{
if ( x ) { PORTC |= (1 << LOAD_BIT) ; }
else { PORTC &= ~(1 << LOAD_BIT) ; }
}
void put_rst(boolean x)
{
if ( x ) { PORTC |= (1 << XRST_BIT) ; }
else { PORTC &= ~(1 << XRST_BIT) ; }
}
void put_xck(boolean x)
{
if ( x ) { PORTC |= (1 << XCK_BIT) ; }
else { PORTC &= ~(1 << XCK_BIT) ; }
/* delay */
delayMicroseconds(2);
}
void put_start(boolean x)
{
if ( x ) { PORTD |= (1 << START_BIT) ; }
else { PORTD &= ~(1 << START_BIT) ; }
}
void rst_gbc(void)
{
/* LOW */
put_rst(OFF);
/* XCK : H */
put_xck(ON);
/* XCK : L */
put_xck(OFF);
/* HIGH */
put_rst(ON);
}
void start_gbc(void)
{
/* HIGH */
put_start(ON);
/* XCK : H */
put_xck(ON);
/* XCK : L */
put_xck(OFF);
/* LOW */
put_start(OFF);
}
void send_params(word x)
{
word result ;
byte i ;
/* copy */
result = x ;
/* loop */
for ( i = 0 ; i < 11 ; i++ ) {
/* impress data */
put_sin(OFF);
if ( result & 0x400 ) { put_sin(ON); }
/* load */
if ( i == 10 ) { put_load(ON); }
/* XCK : H */
put_xck(ON);
/* shift */
result <<= 1;
/* XCK : L */
put_xck(OFF);
}
/* default */
put_sin(OFF);
put_load(OFF);
}
void init_gbc(void)
{
/* reset */
rst_gbc();
/* send parameters */
for ( byte i = 0 ; i < 8 ; i++ ) { send_params( *(params+i) ) ; }
}
boolean get_read()
{
boolean rflag ;
/* default */
rflag = OFF ;
/* judge */
if ( PINC & (1 << READ_BIT) ) { rflag = ON ; }
return rflag ;
}
boolean get_bin()
{
boolean rflag ;
/* default */
rflag = OFF ;
/* judge */
if ( PINB & (1 << CMP_BIT) ) { rflag = ON ; }
return rflag ;
}
boolean is_range(word x)
{
boolean rflag ;
word sy ;
word sz ;
/* calculate */
sy = (lnum << 7) ;
sz = sy + 128 ;
/* default */
rflag = OFF ;
/* judge */
if ( sy <= x && x < sz ) { rflag = ON ; }
return rflag ;
}
void store_binary(byte bx)
{
byte bm ;
byte bd ;
/* judge */
if ( bx > 63 ) return ;
/* calculate */
bm = (bx >> 3) ;
bd = 7 - (bx & 0x07) ;
/* store */
*(bgdat+bm) |= (1 << bd) ;
}
void clear_gdat()
{
for ( byte i = 0 ; i < 64 ; i++ ) { *(gdat+i) = 0 ; }
}
void clear_bgdat()
{
for ( byte i = 0 ; i < 8 ; i++ ) { *(bgdat+i) = 0 ; }
}
void get_gbc(byte x)
{
word ii ;
byte j ;
byte idx ;
word pdx ;
boolean tflag ;
boolean vflag ;
boolean xflag ;
/* default */
idx = IDX_MAX ;
pdx = PDX_MAX ;
/* initialize */
clear_gdat();
clear_bgdat();
init_gbc();
/* loop */
tflag = OFF ;
vflag = OFF ;
xflag = OFF ;
start_gbc();
for ( ii = 0 ; ii < PIXLAST ; ii++ ) {
/* XCK : H */
put_xck(ON);
/* judge READ state */
if ( get_read() ) {
/* valid */
vflag = ON ;
/* clear counter */
if ( idx == IDX_MAX ) { idx = 0 ; }
if ( pdx == PDX_MAX ) { pdx = 0 ; }
}
/* XCK : L */
put_xck(OFF);
/* read and store */
if ( is_range(pdx) == ON ) {
if ( (idx & 1) == 1 ) {
j = (idx >> 1) ;
*(gdat+j) = analogRead(0) ;
if ( get_bin() == ON ) { store_binary(j); }
}
idx++ ;
pdx++ ;
}
/* judge complete store */
if ( idx > 127 && vflag == ON ) {
vflag = OFF ;
xflag = ON ;
}
/* exit */
if ( xflag == ON ) break ;
}
}
void show_gdat()
{
word result ;
char msg[6] ;
/* default */
*(msg+5) = '\0' ;
*(msg+4) = ' ' ;
/* loop */
for ( byte i = 0 ; i < 64 ; i++ ) {
/* get data */
result = *(gdat+i) ;
/* separate */
*(msg+3) = get_asc( result % 10 ) ; result /= 10 ;
*(msg+2) = get_asc( result % 10 ) ; result /= 10 ;
*(msg+1) = get_asc( result % 10 ) ; result /= 10 ;
*(msg+0) = get_asc( result % 10 ) ;
/* surpress zero */
if ( *(msg+0) == '0' ) {
*(msg+0) = ' ' ;
if ( *(msg+1) == '0' ) {
*(msg+1) = ' ' ;
if ( *(msg+2) == '0' ) {
*(msg+2) = ' ' ;
}
}
}
/* show */
rs_puts( msg ) ;
/* new line */
if ( (i % 8) == 7 ) { crlf(); }
}
}
void binary_display(byte x)
{
for ( int i = 7 ; i > -1 ; i-- ) {
rs_putchar( '0' + ((x >> i) & 1) );
}
}
void show_bgdat()
{
for ( byte i = 0 ; i < 8 ; i++ ) {
binary_display( *(bgdat+i) );
if ( i < 7 ) { rs_putchar('_'); }
}
crlf();
}
/* MtTimer2 interrupt handler */
void update_trigger(void)
{
send_led( xcnt & ON );
xcnt++ ;
}
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT); }
else { PORTB &= ~(1 << LED_BIT); }
}
/* initialilze */
void setup()
{
/* initialize serial port */
Serial.begin(9600);
sindex = 0 ;
/* pin configuration */
PORTB = 0b00010000 ;
PORTC = 0b00001010 ;
PORTD = 0b00000001 ;
/* pin direction */
DDRB = 0b11101111 ;
DDRC = 0b11111100 ;
DDRD = 0b11111110 ;
/* clear flags */
uflag = OFF ;
/* others */
scnt = 0 ;
xcnt = 0 ;
/* initialize */
clear_gdat();
clear_bgdat();
*(params+0) = 0x000 | 0x80 ; *(params+1) = 0x100 | 0x04 ;
*(params+2) = 0x200 | 0x00 ; *(params+3) = 0x300 | 0x00 ;
*(params+4) = 0x400 | 0x01 ; *(params+5) = 0x500 | 0x00 ;
*(params+6) = 0x600 | 0x01 ; *(params+7) = 0x700 | 0x03 ;
lnum = 32 ;
/* 500ms period */
MsTimer2::set(500,update_trigger);
/* enable */
MsTimer2::start();
}
/* endless loop */
void loop()
{
char msg[4] ;
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* set parameter */
if ( cmd == 'P' ) {
/* get register number */
rnum = get_hex( *(sbuf+1) ) ;
/* get parameter */
tmp = get_hex( *(sbuf+2) ) ;
tmp <<= 4 ;
tmp |= get_hex( *(sbuf+3) ) ;
/* store */
*(params+rnum) = (rnum << 8) | tmp ;
}
/* show parameter */
if ( cmd == 'p' ) {
*(msg+3) = '\0' ;
for ( byte i = 0 ; i < 8 ; i++ ) {
/* get */
*(msg+2) = get_asc( *(params+i) & MASK0F ) ;
*(msg+1) = get_asc( (*(params+i) >> 4) & MASK0F ) ;
*(msg+0) = get_asc( (*(params+i) >> 8) & MASK0F ) ;
/* show */
rs_puts( msg );
/* new line */
crlf();
}
}
/* set line number */
if ( cmd == 'L' ) {
/* clear */
lnum = 0 ;
/* loop */
for ( byte i = 1 ; i < 3 ; i++ ) {
/* get code */
tmp = *(sbuf+i) ;
/* judge */
if ( tmp == '\r' ) break ;
/* add */
lnum *= 10 ;
lnum += get_hex(tmp) ;
}
/* judge */
if ( lnum > 63 ) {
lnum = 32 ;
rs_puts("0 -> 63");
crlf();
}
}
/* show line number */
if ( cmd == 'l' ) {
/* separate */
*(msg+2) = '\0' ;
*(msg+1) = get_asc( lnum % 10 ) ;
*(msg+0) = get_asc( lnum / 10 ) ;
/* surpress zero */
if ( *(msg+0) == '0' ) { *(msg+0) = ' ' ; }
/* show */
rs_puts( msg );
/* new line */
crlf();
}
/* get graphic data code */
if ( cmd == 'G' ) {
rs_puts("Start ");
get_gbc(lnum);
rs_puts("Comlete");
crlf() ;
}
/* show graphic data */
if ( cmd == 'S' ) {
/* binary data */
show_bgdat();
/* raw data */
show_gdat();
}
}
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
このスケッチでは、1ライン=128ピクセル分の
半分の生値と2値化の論理値を扱えるだけ。
GBCは128ピクセルx125ラインというサイズの画像を
取得できますが、MCR_VCで利用するのは1ライン分
あれば充分と判断。
2値化回路では、コンパレータを利用するので、閾値
をどこにするかを、次の台形をGBCに見せて測定を反復
します。
Arduinoスケッチでは、次のコマンドを用意。
- ? ヘルプ
- P 動作パラメータ設定
- p 設定パラメータ表示
- L 対象ラインの番号指定
- l 対象ラインの番号表示
- G GBCで撮影
- S 1ラインの生値、2値を表示
パラメータは、GBCの動作を変更するために利用。
レジスタ番号を16進数1桁、パラメータを16進数
2桁で指定します。
対象ラインは、10進数で設定しますが、0から63に
限定。範囲外だと、それを指定します。
ハードウエアで2値化しているので、高速処理が
可能だとわかりました。ただし閾値の調整は何度
か繰返して決めます。
目次
前
次