目次

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スケッチでは、次のコマンドを用意。

 パラメータは、GBCの動作を変更するために利用。
 レジスタ番号を16進数1桁、パラメータを16進数
 2桁で指定します。

 対象ラインは、10進数で設定しますが、0から63に
 限定。範囲外だと、それを指定します。

 ハードウエアで2値化しているので、高速処理が
 可能だとわかりました。ただし閾値の調整は何度
 か繰返して決めます。


目次

inserted by FC2 system