目次

予備マシン

 mugen2013に利用しているカメラは、OV7670ですが
 他にも画像処理ができるマシンを用意して、緊急
 事態に備えることにします。

 mugenシリーズでは、シリアルインタフェースのカメラ
 を利用したこともあるので、制御基板にGameBoyCamera
 を接続して、予備マシンにします。(mugen2010のハード
 を使うことに。)

 制御基板の回路構成は、以下でした。



 制御基板に、GameBoyCameraのインタフェース
 を用意します。制御をポート8、データ入力を
 ポート6(3ビット)、ポート7(0ビット)に担当
 させます。



 制御基板全体は、次のようにしました。



 mugen2010マシンの制御基板には、H8/3048FOneを利用
 していましたが、静電気による破損で3年近く、修理
 しないでいました。今回、H8/3052Fを採用します。

 H8/3052Fは、+5VでフラッシュROMの書換えができる
 ことに加えて、内蔵SRAMが8kバイトになってます。
 このSRAM容量が増えたことを、活かすファームを
 考えます。

 フラッシュROMへのダウンロード用に、新たに増設した
 回路は、以下です。



 スイッチの切り替えで、ファームウエアのダウンロード
 と通常動作を切り替えます。

 ハードウエア構成を変えたので、接続デバイスの動作を
 テストするファームウエアを作成します。

 ファームウエアの仕様を考えました。

 シリアルでコマンドを送信
 ITU0を使い、動作確認用LEDを点滅
 ITU1を使い、左右のモータにPWM波形を出力
 ITU1を使い、スタートトリガースイッチのクリック判定
 EEPROMにデータを入出力
 LCDに文字列を転送
 GameBoyCamera(GBC)から画像データ取得

 個別の動作を定義していきます。

 動作確認用LEDを点滅
  ITU0の割込みが動いているのかを確認します。

  コマンドは、'A'を使い、パラメータに1を指定
  すると点滅させ、0を指定すると消灯させます。

  TFLAG、AFLAGの2フラグを利用します。

  TFLAGは、ITU0の割込みでセットし通知に使います。
  TFLAGのリセットは、main関数内部のループ処理で
  担当します。

  この処理は、次のコードで実現します。

    if ( TFLAG == ON ) {
      TFLAG = OFF ;
      cnt++ ;
      tmp = MASKFF ;
      if ( cnt & 1 ) { tmp ^= MASKFF ; }
      if ( AFLAG == OFF ) { tmp = MASKFF ; }
      /* turn on or off LED */
      GLED = OFF ;
      if ( tmp ) { GLED = ON ; }
    }

  カウンタcntをインクリメントして、LEDへ出力する
  論理値を決定します。もし、消灯指定がされている
  と対応する論理値を1にします。(負論理利用)

  消灯指定のフラグAFLAGは、次のようにコマンドで
  セット、リセットします。
      if ( cmd == 'A' ) {
        /* get parameter */
        tmp = get_hex( *(sbuf+1) );
        /* judge */
        AFLAG = OFF ;
        if ( tmp > 0 ) { AFLAG = ON  ; }
      }

  ITU0の割込みは、コンペアマッチを使います。
  割込み周期は1msとします。

      void int_imia0(void)
      {
        UBYTE dummy ;
        /* clear flag */
        dummy = ITU0.TSR.BIT.IMFA ;
        ITU0.TSR.BIT.IMFA = OFF ;
        /* increment */
        timcnt++ ;
        /* judge */
        if ( (timcnt & 0x1ff) == 500 ) { TFLAG = ON ; }
      }

  変数timcntは、システムタイマーとして利用
  します。1ms単位のディレイを生成する時
  この値を参照します。

  LEDの点滅周期は1秒単位です。
  1秒ごとの点滅にするため、0.5秒ごとに
  点灯、消灯にしています。

  変数timcntを利用した1ms単位のディレイは
  次の関数で実現します。

      void delay_ms(UWORD x)
      {
        ULONG target ;
        /* calculate last value */
        target = timcnt + x ;
        /* wait */
        while ( timcnt < target ) ;
      }

  システムタイマーは、常にインクリメントされる
  ので、現在のカウント値に、希望するディレイの
  値を加えて、そこに達するまで待つ仕様です。

  LCDの初期化に、delay_msを使うことになる
  ので、ここで定義しておきました。


 左右のモータにPWM波形を出力
  ITU0の割込みが動くことが確認できれば、同じ種類の
  割込みを利用して、左右のモータにPWM波形を出力し
  回転を制御します。

  H8/3052Fに内蔵されているPWM波形生成用モジュールを
  使わずに、コンペアマッチ割込みでカウンタを動かし
  与えられたDUTY比との比較で、ポートに出力する論理値
  を決定します。

  左右のDUTY比を設定するコマンドを、'M'とします。

  DUTY比は、コマンドに続けて10進2けたで指定します。
  左右のどちらか両方のDUTY比にするかは、L、R、Bで
  指定します。

  この仕様を実現するコードは、以下。

      if ( cmd == 'M' ) {
        /* get duty ratio */
        tmp = get_hex( *(sbuf+1) );
        tmp *= 10 ;
        tmp += get_hex( *(sbuf+2) );
        /* set duty ratio */
        i = *(sbuf+3) ;
        if ( i == 'R' ) { rduty = tmp ; }
        if ( i == 'L' ) { lduty = tmp ; }
        if ( i == 'B' ) { rduty = tmp ; lduty = tmp ;}
      }

  ITU1の割込みは、コンペアマッチを使います。
  割込み周期は0.1msとします。

  割込み関数では、カウンタを0〜99まで変化させ
  指定DUTY比と比較し、出力論理値を確定します。

  以下のように、非常に単純です。
      void int_imia1(void)
      {
        UBYTE dummy ;
        UBYTE xport ;
        /* clear flag */
        dummy = ITU1.TSR.BIT.IMFA ;
        ITU1.TSR.BIT.IMFA = OFF ;
        /* impress */
        xport = 0 ;
        if ( pcnt < rdutyx ) { xport |= 0x01 ; }
        if ( pcnt < ldutyx ) { xport |= 0x04 ; }
        PBDR = xport ;
        /* increment */
        pcnt++ ;
        /* judge */
        if ( pcnt == 100 ) {
          pcnt = 0 ;
          rdutyx = rduty ;
          ldutyx = lduty ;
        }
      }

  左右のDUTY比は、割込み関数内部で使う変数に
  99から0にロールオーバーするときに、再設定
  します。これで、1周期中にDUTY比を変更して
  も、回転が乱れなくなります。

 スタートトリガースイッチのクリック判定
  スタートトリガースイッチをクリックしたことを
  フラグで通知し、システム側が認識できていると
  確認します。

  フラグSFLAGで、main関数中のループに通知して
  くるので、通知が来たことを端末に出力し、確認
  します。

    if ( SFLAG == ON ) {
      /* clear flag */
      SFLAG = OFF ;
      /* send event happened */
      rs1_puts("Get start trigger");
    }

  割込み関数内部で、シフトレジスタを使いチャタリング
  を除去します。スイッチの論理値が、1から0に変化した
  ことをシフトレジスタに格納された値で、判定します。

  シフトレジスタの処理を加えた、割込み関数は、以下です。
      void int_imia1(void)
      {
        UBYTE dummy ;
        UBYTE xport ;
        /* clear flag */
        dummy = ITU1.TSR.BIT.IMFA ;
        ITU1.TSR.BIT.IMFA = OFF ;
        /* impress */
        xport = 0 ;
        if ( pcnt < rdutyx ) { xport |= 0x01 ; }
        if ( pcnt < ldutyx ) { xport |= 0x04 ; }
        PBDR = xport ;
        /* increment */
        pcnt++ ;
        /* judge */
        if ( pcnt == 100 ) {
          pcnt = 0 ;
          rdutyx = rduty ;
          ldutyx = lduty ;
        }
        /* shift */
        sft <<= 1 ;
        sft &= 0x07 ;
        /* get start trigger switch */
        if ( SW_START == OFF ) { sft |= ON ; }
        /* judge */
        if ( sft == 0x03 ) { SFLAG = ON ; }
      }

 EEPROMにデータを入出力
  GameBoyCameraは、パラメータを設定しなければならない
  ので、何度がテストして、最適なパラメータを決定します。

  ファームウエアのコードとして、パラメータを入れてしまうと
  環境が変わったときに、再度コンパイル、リンクが必要になり
  時間がかかります。それを避けるために、EEPROMにパラメータ
  を保存して対応します。

  利用しているEEPROMは、3線式のAT93C46です。
  H8のポート4の上位4ビットを使い、データ入出力を
  制御します。EEPROMとの接続は、以下とします。

  AT93C46は、1ワード=16ビットのデータを入出力するので
  1ワードの入力、出力関数を定義します。

  リード、ライトともに、DIにコマンド、アドレスを与える
  ので、一つのループで制御ワードにまとめて、ビット操作
  します。

      void  eeprom_put(UBYTE ax,UWORD dx)
      {
        UWORD tmp ;
        UBYTE i ;
        /* judge */
        if ( ax > 63 ) return ;
        /* SB , OP code , dummy */
        tmp = 0x140 | (ax & MASK3F) ;
        /* enable chip select */
        EEPROM_CS = ON ;
        /* send address */
        for ( i = 0 ; i < 9 ; i++ ) {
          /* impress DI */
          EEPROM_DI = OFF ;
          if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
          /* SK : H */
          EEPROM_SK = ON ;
          /* shift */
          tmp <<= 1 ;
          /* SK : L */
          EEPROM_SK = OFF ;
        }
        /* send data */
        for ( i = 0 ; i < 16 ; i++ ) {
          /* impress DI */
          EEPROM_DI = OFF ;
          if ( dx & 0x8000 ) { EEPROM_DI = ON ; }
          /* SK : H */
          EEPROM_SK = ON ;
          /* shift */
          dx <<= 1 ;
          /* SK : L */
          EEPROM_SK = OFF ;
        }
        /* disable chip select */
        EEPROM_CS = OFF ;
        /* delay */
        delay_ms(10) ;
      }

      UWORD eeprom_get(UBYTE ax)
      {
        UWORD result ;
        UWORD tmp ;
        UBYTE i ;
        /* judge */
        if ( ax > 63 ) { return 0 ; }
        /* SB , OP code , dummy */
        tmp = 0x180 | (ax & MASK3F) ;
        /* enable chip select */
        EEPROM_CS = ON ;
        /* send address */
        for ( i = 0 ; i < 9 ; i++ ) {
          /* impress DI */
          EEPROM_DI = OFF ;
          if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
          /* SK : H */
          EEPROM_SK = ON ;
          /* shift */
          tmp <<= 1 ;
          /* SK : L */
          EEPROM_SK = OFF ;
        }
        /* default */
        result = 0 ;
        /* get data */
        for ( i = 0 ; i < 16 ; i++ ) {
          /* shift */
          result <<= 1 ;
          /* SK : H */
          EEPROM_SK = ON ;
          /* get data */
          result |= EEPROM_DO ;
          /* SK : L */
          EEPROM_SK = OFF ;
        }
        /* disable chip select */
        EEPROM_CS = OFF ;

        return result ;
      }

  EEPROMのデータを、不用意に書き換えないように
  電源を入れただけでは、リードオンリーになって
  います。リード、ライトできるように、コマンド
  を与えないといけないので、そのための関数を
  定義します。

      void  eeprom_ewen(void)
      {
        UWORD tmp ;
        UBYTE i ;
        /* SB , OP code , dummy */
        tmp = 0x130 ;
        /* enable chip select */
        EEPROM_CS = ON ;
        /* send */
        for ( i = 0 ; i < 9 ; i++ ) {
          /* impress DI */
          EEPROM_DI = OFF ;
          if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
          /* SK : H */
          EEPROM_SK = ON ;
          /* shift */
          tmp <<= 1 ;
          /* SK : L */
          EEPROM_SK = OFF ;
        }
        /* clear DI */
        EEPROM_DI = OFF ;
        /* disable chip select */
        EEPROM_CS = OFF ;
      }

  デバイスとやりとりする機能を定義したので、シリアル
  インタフェースで使うコマンドを考えます。

  1ワード=16ビットの値を、アドレスを指定して
  書込むため、コマンド'E'を定義します。
  EEPROMにどんなデータが格納されているのかを
  表示するため、コマンド’C'を定義します。
  E=Edit、C=Clarifyという連想でコマンド名を
  決めました。

  EEPROMのデータ入出力のためのアドレスとデータを
  変数eeprom_adr、eeprom_datに格納して、利用する
  ことにします。

  アドレスを指定して、データ(16ビット)を格納する
  ための処理は、以下とします。
      if ( cmd == 'E' ) {
        /* get address */
        eeprom_adr = 0 ;
        eeprom_adr |= get_hex( *(sbuf+1) ); eeprom_adr <<= 4;
        eeprom_adr |= get_hex( *(sbuf+2) );
        /* get data */
        eeprom_dat = 0 ;
        eeprom_dat |= get_hex( *(sbuf+3) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+4) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+5) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+6) );
        /* store */
        if ( eeprom_adr < 0x40 ) { eeprom_put(eeprom_adr,eeprom_dat); }
        else { rs1_puts("Address out of area !"); }
      }

  EERPOM内データは、アドレスが最大63である
  ことから、一気に表示します。
      if ( cmd == 'C' ) {
        /* loop */
        for ( i = 0 ; i < 0x40 ; i++ ) {
          /* get data */
          tmpx = eeprom_get( i );
          /* show */
          show_value( (tmpx >> 8) & MASKFF );
          show_value( tmpx & MASKFF );
          rs1_putchar(' ');
          /* new line */
          if ( (i & MASK07) == 7 ) { rs1_crlf(); }
        }
        /* new line */
        rs1_crlf() ;
      }

  1ワードを16進4けたで表示し、8ワードごとに
  改行して見やすくしています。

 LCDに文字列を転送
  マシン走行中に、どういう状態になっているのかを
  LCDを利用して表示させます。LCDに文字列表示する
  ためのピンアサインは、以下とします。

  LCDとデータ交換するには、4ビットか8ビットの
  データバスを使わなければなりません。
  接続ピン数を減らすため、8ビットシフトレジスタ
  を入れて、8ビットデータを格納してから、LCDに対
  して、データ保存させます。



  H8からは、書込みだけするので、R/W信号は'L'に
  固定してあります。LCDを表示器として利用する
  ため、この回路を採用しています。

  LCDを制御するためには、コマンドとデータを
  与えなければならないので、最下層で動作する
  関数を定義します。

      void  lcd_primitive(UWORD x)
      {
        UBYTE i ;
        UBYTE dd ;
        /* get kind */
        LCD_RS = OFF ;
        if ( x & 0x100 ) { LCD_RS = ON ; }
        /* send data */
        dd = x & MASKFF ;
        for ( i = 0 ; i < 8 ; i++ ) {
          /* impress data */
          HC_DAT = OFF ;
          if ( dd & MASK80 ) { HC_DAT = ON ; }
          /* CLK : H */
          HC_CLK = ON ;
          /* shift */
          dd <<= 1 ;
          /* CLK : L */
          HC_CLK = OFF ;
        }
        /* impress E */
        LCD_E = ON ;
        dd <<= 1 ; /* dummy */
        LCD_E = OFF ;
        /* default */
        LCD_RS = OFF ;
        HC_DAT = OFF ;
      }

  コマンド、データの区別は、LCD_RSに与える論理値に
  よるので、コマンド、データ用のラッパー関数を
  関数lcd_primitiveに被せます。
      void  lcd_cmd(UBYTE x)
      {
        lcd_primitive( (UWORD)x );
      }

      void  lcd_dat(UBYTE x)
      {
        lcd_primitive( x | 0x100 );
      }

  表示する文字列は、配列lcd_displayに格納します。

  利用するLCDは、2行x16桁なので、配列の大きさは
  32バイトにして、次のように定義しておきます。
      UBYTE lcd_display[32];

  行を指定して、文字列を表示する
  コマンド'L'を定義します。
  行は、0と1だけですが、LCD画面
  をクリアしたいので、9行を指定
  すると、スペースを表示して対応
  します。
  行は、コマンド'L'に続けて指定し
  行指定の後に、表示したい文字列
  を続けます。

  main関数のループに、次のコードを入れて
  LCDへの文字列表示をテストします。
      if ( cmd == 'L' ) {
        /* get line number */
        lnumber = get_hex( *(sbuf+1) );
        /* judge */
        if ( lnumber < 2 ) {
          /* calculate pointer */
          lcd_index = lnumber * 16 ;
          /* data transfer */
          for ( i = 0 ; i < 16 ; i++ ) {
            /* judge */
            if ( *(sbuf+2+i) == '\r' ) break ;
            /* copy */
            *(lcd_display+lcd_index+i) = *(sbuf+2+i) ;
          }
          /* show strings on LCD */
          send_lcd_handler();
        }
        if ( lnumber == 9 ) {
          /* space transfer */
          for ( i = 0 ; i < 32 ; i++ ) { *(lcd_display+i) = ' ' ; }
          /* show strings on LCD */
          send_lcd_handler();
        }
      }

  受信バッファから配列lcd_displayに
  文字列を転送後、関数send_lcd_handler
  で一気に表示します。

  関数send_lcd_handlerは、次のように定義しました。
      void  send_lcd_handler(void)
      {
        UBYTE line  ;
        UBYTE pixel ;
        /* line handling */
        for ( line = 0 ; line < 2 ; line++ ) {
          /* set address */
          lnumber = 0x00 ;
          if ( line ) { lnumber = 0x40 ; }
          lnumber |= MASK80 ;
          lcd_cmd( lnumber );
          /* calculate line */
          lcd_index = 16 * line ;
          /* pixel handling */
          for ( pixel = 0 ; pixel < 16 ; pixel++ ) {
            lcd_dat( *(lcd_display+lcd_index+pixel) );
          }
        }
      }

  LCD内部のデータバッファは、連続したアドレスでは
  ないので、0行と1行を区別するためのエントリー
  アドレスを指定します。

  LCDを利用するときは、初期化が必要なので
  関数init_lcdに記述して利用します。
      void init_lcd(void)
      {
        /* initialize hardware */
        delay_ms(15) ;
        lcd_cmd(0x30);
        delay_ms(5) ; /* 5ms */
        lcd_cmd(0x30);
        delay_ms(1); /* 1ms */
        lcd_cmd(0x30);
        delay_ms(1); /* 1ms */
        /* set function */
        lcd_cmd(0x38);
              /*
              Function
              001 DL N F * *
              DL(Data Length) = 1 (8bits)
              N(Row)          = 1 (4 row)
              F(Font)         = 0 (5x7)
              001 1 1 0 * * 
              */
        lcd_cmd(0x08);
              /*
              Display off
              0000 1 D C B 
              D(Display) = 0 (OFF)
              C(Cursor)  = 0 (OFF)
              B(Blink)   = 0 (OFF)
              0000 1 0 0 0
              */
        lcd_cmd(0x01);
        delay_ms(2); /* 2ms */
        lcd_cmd(0x0c);
              /*
              Display on
              0000 1 D C B 
              D(Display) = 1 (ON)
              C(Cursor)  = 0 (OFF)
              B(Blink)   = 0 (OFF)
              0000 1 1 0 0 
              */
      }

  LCDとのインタフェースは、lcd_primitiveにまとめて
  あるので、コマンド、データ転送には、ラッパー関数
  であるlcd_cmd、lcd_datを利用します。

  ハードウエアの違いを吸収するために、最下層の
  関数を定義し、ラッパー関数を利用しているので
  他のマイクロコンピュータへの移植が、容易です。

 GameBoyCamera(GBC)から画像データ取得
  GBCは、128x123の画像データを出力するので
  H8/3052Fの内部SRAMの半分を画像データ格納
  領域にします。

  内部SRAMの半分は、4096バイトなので、この
  領域で32ライン分のデータを格納できます。

  GBCがどんな画像を撮影したのかは、同じ場所を
  4回撮影して、合成します。

  32ライン分しか容量がないので、画像データ用
  バッファに保存する、ラインのエントリーポイント
  を決めて対応します。



  MCR_VCマシンでは、画像全体を使うのではなく
  上にある64ラインの中から、3あるいは5本
  のラインを利用します。
  従って、32ライン分程度のバッファ容量で
  充分移動処理を実現できます。

  GBCは、パラメータを転送して初期化しないと
  利用できないので、コマンド'P'をパラメータ
  転送に使います。

  パラメータは、レジスタ0〜7に設定するので
  コマンド'P'に続けて、レジスタ番号を与えます。
  レジスタ番号に続けて、16進数2桁でパラメータ
  を設定します。

  パラメータを再指定して、最適値を求めても電源
  オフで消失するので、EEPROMに保存できるように
  します。

  さらに、カメラで撮影したデータを端末に転送する
  ための処理も入れたいので、コマンドに続ける1つ
  のパラメータに、次のような意味を与えます。

  コマンドと続けるパラメータの意味を決めたので
  対応するコードを定義します。
      if ( cmd == 'P' ) {
        /* get register address */
        i = get_hex( *(sbuf+1) );
        /* store parameter */
        if ( i < 8 ) {
          tmp = get_hex( *(sbuf+2) ) ;
          tmp <<= 4 ;
          tmp |= get_hex( *(sbuf+3) ) ;
          *(gbcparam+i) = tmp ;
        }
        /* start line */
        if ( i == 8 ) {
          tmp = get_hex( *(sbuf+2) ) ;
          tmp *= 10 ;
          tmp |= get_hex( *(sbuf+3) ) ;
          slinex = (tmp << 7);
          elinex = slinex + 4095 ;
        }
        /* transfer parameters from EEPROM to array */
        if ( i == 9 ) {
          tmp = i ;
          for ( i = 0 ; i < 4 ; i++ ) {
            /* get parameter from EEPROM */
            eeprom_dat = eeprom_get( i ) ;
            /* transfer */
            *(gbcparam+2*i)   = (eeprom_dat >> 8) & MASKFF ;
            *(gbcparam+2*i+1) = (eeprom_dat & MASKFF) ;
          }
          i = tmp ;
        }
        /* get data from GBC */
        if ( i == 10 ) {
          reset_gbc();
          send_param_gbc();
          send_start_gbc();
        }
        /* display GBC data */
        if ( i == 13 ) { show_gbc_data(); }
      }

  GBCの画像データとパラメータ用配列は
  次のように宣言して利用します。
   UBYTE gbcdat[4096] ;
   UBYTE gbcparam[8] ;

  GBCで撮影するためには、シーケンスが必要なので
  次の3関数を利用して、リセット、パラメータ設定
  撮影という手順を踏みます。

      void  reset_gbc(void)
      {
        GBC_XCLK = OFF ;
        /* enable XRST */
        GBC_XRST = OFF ;
        /* XCLK : H */
        GBC_XCLK = ON ;
        /* XCLK : L */
        GBC_XCLK = OFF ;
        /* disable XRST */
        GBC_XRST = ON ;
      }

      void  send_param_gbc(void)
      {
        UBYTE loop ;
        UBYTE i ;
        UBYTE tmp ;
        /* loop */
        for ( loop = 0 ; loop < 8 ; loop++ ) {
          /* get data */
          tmp = *(gbcparam+loop);
          /* send data */
          for ( i = 0 ; i < 8 ; i++ ) {
            /* impress SIN */
            GBC_SIN = OFF ;
            if ( tmp & MASK80 ) { GBC_SIN = ON ; }
            /* enable LOAD */
            if ( i == 7 ) { GBC_LOAD = ON ; }
            /* XCLK : H */
            GBC_XCLK = ON ;
            /* shift */
            tmp <<= 1 ;
            /* XCLK : L */
            GBC_XCLK = OFF ;
            /* set default value */
            GBC_LOAD = OFF ;
          }
          /* set default values */
          GBC_SIN  = OFF ;
        }
      }

      void  send_start_gbc(void)
      {
        GBC_XCLK = OFF ;
        /* enable START */
        GBC_START = ON ;
        /* XCLK : H */
        GBC_XCLK = ON ;
        /* XCLK : L */
        GBC_XCLK = OFF ;
        /* disable START */
        GBC_START = OFF ;
        /* clear */
        gstate = 0 ;
        /* enable */
        GFLAG = ON ;
      }

  関数send_start_gbcで、画像データを取得する
  シーケンサを起動します。

  GBCが撮影した画像を取得するには、シーケンサ
  gbc_handlerを使います。
      void  gbc_handler(void)
      {
        switch ( gstate ) {
          /* wait trigger */
          case 0 : if ( GFLAG == ON ) {
                     GFLAG  = OFF ;
                     gstate = 1 ;
                   }
                   break ;
          /* wait READ */
          case 1 : if ( GBC_READ == ON ) {
                     gstate = 2 ;
                     gcnt   = 0 ;
                     gindex = 0 ;
                     rs1_puts("*** Start ***");
                   }
                   break ;
          /* judge */
          case 2 : if ( gcnt == 15744 ) {
                     gstate = 3 ;
                     gcnt = 0 ;
                     rs1_puts("*** Complete ***");
                   }
                   break ;
          /* return first state */
          case 3 : gstate = 0 ;
                   break ;
          /* */
          default : gstate = 0 ; break ;
        }
        /* store data */
        {
          /* XCLK : H */
          GBC_XCLK = ON ;
          if ( GBC_READ == ON ) {
            /* start A/D convert */
            AD.ADCSR.BIT.ADST = 1 ;
          }
          /* XCLK : L */
          GBC_XCLK = OFF ;
          if ( GBC_READ == ON ) {
            /* wait A/D conversion */
            while ( AD.ADCSR.BIT.ADF == 0 ) ;
            AD.ADCSR.BIT.ADF = 0 ;
            /* store */
            if ( slinex <= gcnt && gcnt <= elinex ) {
              *(gbcdat+gindex) = (AD.ADDRA >> 8);
              gindex++ ;
            }
            /* update counter */
            gcnt++ ;
          }
        }
      }

  GBCがREAD信号で、有効なデータを出力していると
  意思表示するので、この信号が'H'のときに、A/D
  コンバータを動かします。

  A/D変換されたデータは、取得したいライン範囲に
  あれば、配列に保存します。

  保存した画像データを、端末に数値で出力するため
  関数show_gbc_dataを使います。

  関数show_gbc_dataの内容は、以下。
      void  show_gbc_data(void)
      {
        UWORD loop ;
        UBYTE tmp ;
        UBYTE msg[3] ;
        for ( loop = 0 ; loop < 4096 ; loop++ ) {
          /* get data */
          tmp = *(gbcdat+loop);
          /* separate */
          *(msg+0) = tmp / 100 ; tmp /= 100 ;
          *(msg+1) = tmp / 10 ;
          *(msg+2) = tmp % 10 ;
          /* conversion */
          *(msg+0) = asc_hex[*(msg+0)] ;
          *(msg+1) = asc_hex[*(msg+1)] ;
          *(msg+2) = asc_hex[*(msg+2)] ;
          /* suppress zero */
          tmp = *(gbcdat+loop);
          if ( tmp < 100 ) {
            *(msg+0) = ' ' ;
          } else {
            if ( tmp < 10 ) {
              *(msg+1) = ' ' ;
            }
          }
          /* send */
          rs1_putchar( *(msg+0) ) ; rs1_putchar( *(msg+1) ) ;
          rs1_putchar( *(msg+2) ) ; rs1_putchar( ' ' ) ;
          /* new line */
          if ( (loop % 8) == 7 ) { rs1_crlf() ; }
        }
      }

  画像データの1ピクセルを0〜255の10進で表示します。
  8データごとに改行しておきます。

  画像として表示するためには、PersonalComputer側の
  フリーソフトImageMagickを使います。

  ハードウエアをテストするためのファームウエアの
  全ソースコードは、以下。
#include "3052.h"

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;
typedef unsigned long  ULONG ;
typedef   signed char  SBYTE ;
typedef   signed short SWORD ;

#define NO  0
#define YES 1

/*----------------*/
/* user variables */
/*----------------*/
#define ITU0_AREG 24999
#define ITU1_AREG 2499

typedef union {
  struct {
    unsigned char B7:1;
    unsigned char B6:1;
    unsigned char B5:1;
    unsigned char B4:1;
    unsigned char B3:1;
    unsigned char B2:1;
    unsigned char B1:1;
    unsigned char B0:1;
  } BIT ;
  unsigned char DR ;
} FLAGSP ;

FLAGSP x_flags ;

ULONG timcnt ;

#define SFLAG   x_flags.BIT.B0
#define AFLAG   x_flags.BIT.B1
#define TFLAG   x_flags.BIT.B2
#define GFLAG   x_flags.BIT.B5
#define UFLAG   x_flags.BIT.B6
#define DFLAG   x_flags.BIT.B7

#define P4DDR P4.DDR
#define P4DR  P4.DR.BYTE
#define P6DDR P6.DDR
#define P6DR  P6.DR.BYTE
#define P8DDR P8.DDR
#define P8DR  P8.DR.BYTE
#define P7DR  P7.DR.BYTE
#define P9DDR P9.DDR
#define P9DR  P9.DR.BYTE
#define PADDR PA.DDR
#define PADR  PA.DR.BYTE
#define PBDDR PB.DDR
#define PBDR  PB.DR.BYTE

#define GLED  P6.DR.BIT.B6

#define EEPROM_CS P4.DR.BIT.B7
#define EEPROM_SK P4.DR.BIT.B6
#define EEPROM_DI P4.DR.BIT.B5
#define EEPROM_DO P4.DR.BIT.B4

#define MASKFFFF 0xffff
#define MASKFF   0xff
#define MASKCF   0xcf
#define MASK0F   0x0f
#define MASK3F   0x3f
#define MASK07   0x07
#define OFF      0
#define ON       OFF+1

#define MASK80 0x80

#define LCD_E  P4.DR.BIT.B3
#define LCD_RS P4.DR.BIT.B2
#define HC_CLK P4.DR.BIT.B1
#define HC_DAT P4.DR.BIT.B0

#define GBC_START P8.DR.BIT.B4
#define GBC_SIN   P8.DR.BIT.B3
#define GBC_LOAD  P8.DR.BIT.B2
#define GBC_XRST  P8.DR.BIT.B1
#define GBC_XCLK  P8.DR.BIT.B0

#define GBC_READ  P6.DR.BIT.B3

#define SW_START  PB.DR.BIT.B4
#define SW_DIR    PB.DR.BIT.B5

void init_sci_1(TBaudRate x);
void rs1_putchar(UBYTE x);
void rs1_crlf(void);
void rs1_puts(UBYTE *x);
void show_help(void);
void show_value(UBYTE x);

void lcd_primitive(UWORD x);
void lcd_cmd(UBYTE x);
void lcd_dat(UBYTE x);
void send_lcd_handler(void);
void init_lcd(void);

volatile UBYTE sindex ;
volatile UBYTE sbuf[32];
volatile UBYTE cmd ;

volatile UBYTE cnt ;

volatile UBYTE sft ;

volatile UBYTE asc_hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} ;

volatile UBYTE eeprom_adr ;
volatile UWORD eeprom_dat ;

#define LCDBUFS 32

volatile UBYTE lcd_display[LCDBUFS] ;

volatile UBYTE lnumber ;
volatile UBYTE lcd_index ;

volatile UBYTE pcnt ;
volatile UBYTE rduty ;
volatile UBYTE lduty ;
volatile UBYTE rdutyx ;
volatile UBYTE ldutyx ;

volatile UBYTE gbcdat[4096] ;
volatile UBYTE gbcparam[8] ;
volatile UWORD gindex ;
volatile UWORD slinex ;
volatile UWORD elinex ;
volatile UBYTE gstate ;
volatile UWORD gcnt ;

/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void  user_initialize(void);
void  init_timer0();
void  init_timer1();

UBYTE get_hex(UBYTE x);
void  delay_ms(UWORD x);

void  eeprom_ewen(void);
void  eeprom_put(UBYTE ax,UWORD dx);
UWORD eeprom_get(UBYTE ax);

void  reset_gbc(void);
void  send_param_gbc(void);
void  send_start_gbc(void);
void  gbc_handler(void);
void  show_gbc_data(void);

/*------*/
/* main */
/*------*/
int main(void)
{
  UBYTE tmp  ;
  UWORD tmpx ;
  UBYTE i ;
  /* disable interrupt */
  DI ;
  /* initialize */
  user_initialize();
  /* enable interrupt */
  EI ;
  /* opening message */
  rs1_puts("Hello");
  /* enable EEPROM */
  eeprom_ewen();
  /* initialize LCD */
  init_lcd();
  /* loop */
  while ( ON ) {
    /* command interpreter */
    if ( UFLAG == ON ) {
      /* clear flag */
      UFLAG = OFF ;
      /* new line */
      rs1_crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* judge */
      if ( cmd == '?' ) { show_help() ; }
      if ( cmd == 'A' ) {
        /* get parameter */
        tmp = get_hex( *(sbuf+1) );
        /* judge */
        AFLAG = OFF ;
        if ( tmp > 0 ) { AFLAG = ON  ; }
      }
      if ( cmd == 'E' ) {
        /* get address */
        eeprom_adr = 0 ;
        eeprom_adr |= get_hex( *(sbuf+1) ); eeprom_adr <<= 4;
        eeprom_adr |= get_hex( *(sbuf+2) );
        /* get data */
        eeprom_dat = 0 ;
        eeprom_dat |= get_hex( *(sbuf+3) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+4) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+5) ); eeprom_dat <<= 4;
        eeprom_dat |= get_hex( *(sbuf+6) );
        /* store */
        if ( eeprom_adr < 0x40 ) { eeprom_put(eeprom_adr,eeprom_dat); }
        else { rs1_puts("Address out of area !"); }
      }
      if ( cmd == 'C' ) {
        /* loop */
        for ( i = 0 ; i < 0x40 ; i++ ) {
          /* get data */
          tmpx = eeprom_get( i );
          /* show */
          show_value( (tmpx >> 8) & MASKFF );
          show_value( tmpx & MASKFF );
          rs1_putchar(' ');
          /* new line */
          if ( (i & MASK07) == 7 ) { rs1_crlf(); }
        }
        /* new line */
        rs1_crlf() ;
      }
      /* LCD handling */
      if ( cmd == 'L' ) {
        /* get line number */
        lnumber = get_hex( *(sbuf+1) );
        /* judge */
        if ( lnumber < 2 ) {
          /* calculate pointer */
          lcd_index = lnumber * 16 ;
          /* data transfer */
          for ( i = 0 ; i < 16 ; i++ ) {
            /* judge */
            if ( *(sbuf+2+i) == '\r' ) break ;
            /* copy */
            *(lcd_display+lcd_index+i) = *(sbuf+2+i) ;
          }
          /* show strings on LCD */
          send_lcd_handler();
        }
        if ( lnumber == 9 ) {
          /* space transfer */
          for ( i = 0 ; i < 32 ; i++ ) { *(lcd_display+i) = ' ' ; }
          /* show strings on LCD */
          send_lcd_handler();
        }
      }
      /* motor test */
      if ( cmd == 'M' ) {
        /* get duty ratio */
        tmp = get_hex( *(sbuf+1) );
        tmp *= 10 ;
        tmp += get_hex( *(sbuf+2) );
        /* set duty ratio */
        i = *(sbuf+3) ;
        if ( i == 'R' ) { rduty = tmp ; }
        if ( i == 'L' ) { lduty = tmp ; }
        if ( i == 'B' ) { rduty = tmp ; lduty = tmp ;}
      }
      /* GBC control */
      if ( cmd == 'P' ) {
        /* get register address */
        i = get_hex( *(sbuf+1) );
        /* store parameter */
        if ( i < 8 ) {
          tmp = get_hex( *(sbuf+2) ) ;
          tmp <<= 4 ;
          tmp |= get_hex( *(sbuf+3) ) ;
          *(gbcparam+i) = tmp ;
        }
        /* start line */
        if ( i == 8 ) {
          tmp = get_hex( *(sbuf+2) ) ;
          tmp *= 10 ;
          tmp |= get_hex( *(sbuf+3) ) ;
          slinex = (tmp << 7);
          elinex = slinex + 4095 ;
        }
        /* transfer parameters from EEPROM to array */
        if ( i == 9 ) {
          tmp = i ;
          for ( i = 0 ; i < 4 ; i++ ) {
            /* get parameter from EEPROM */
            eeprom_dat = eeprom_get( i ) ;
            /* transfer */
            *(gbcparam+2*i)   = (eeprom_dat >> 8) & MASKFF ;
            *(gbcparam+2*i+1) = (eeprom_dat & MASKFF) ;
          }
          i = tmp ;
        }
        /* get data from GBC */
        if ( i == 10 ) {
          reset_gbc();
          send_param_gbc();
          send_start_gbc();
        }
        /* display GBC data */
        if ( i == 13 ) { show_gbc_data(); }
      }
    }
    /* get data from GBC */
    gbc_handler();
    /* start trigge handling */
    if ( SFLAG == ON ) {
      /* clear flag */
      SFLAG = OFF ;
      /* send event happened */
      rs1_puts("Get start trigger");
    }
    /* test handling */
    if ( TFLAG == ON ) {
      TFLAG = OFF ;
      cnt++ ;
      tmp = MASKFF ;
      if ( cnt & 1 ) { tmp ^= MASKFF ; }
      if ( AFLAG == OFF ) { tmp = MASKFF ; }
      /* turn on or off LED */
      GLED = OFF ;
      if ( tmp ) { GLED = ON ; }
    }
  }
  return 0 ;
}

/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
  int i ;
  /* PORT 6 */
  P6DR  = 0xf0 ;
  P6DDR = 0xf6 ; /* P60 , P63 inputs , others output */
  /* PORT 8 */
  P8DR  = 0x02 ;
  P8DDR = MASKFF ; /* all outputs */
  /* PORT B */
  PBDR  = 0x00 ;
  PBDDR = MASKCF ; /* PB5,PB4 inputs , others are outputs */
  /* PORT A */
  PADR  = 0      ;
  PADDR = MASKFF ; /* all outputs */
  /* PORT 4 */
  P4DR  = 0x00 ; /* disable all */
  P4DDR = 0xef ; /* P44 : inputs , others : outputs */
  /* initialize timers */
  init_timer0();
  init_timer1();
  /* clear flags */
  x_flags.DR = 0 ;
  /* clear SCI buffer */
  *(sbuf+0) = 0 ; sindex = 0 ;
  /* initialize */
  timcnt = 0 ;
  cnt = 0 ;
  pcnt   = 0 ;
  rduty  = 0 ;
  lduty  = 0 ;
  rdutyx = 0 ;
  ldutyx = 0 ;
  sft    = 0 ;
  for ( i = 0 ; i < 32 ; i++ ) { *(lcd_display+i) = ' ' ; }
  /* */
  init_sci_1(br19200);
  /* initialize A/D converter 
       single mode AN0
  */
  AD.ADCSR.BYTE = 0x00 ;
}

void init_timer0(void)
{
  /* stop timer */
  ITU.TSTR.BIT.STR0 = OFF ;
  /* TOER : Timer Output Enable Register
        7 **** -> 0
        6 **** -> 0
        5 EXB4 -> 0
        4 EXA4 -> 0
        3 EB3  -> 0
        2 EB4  -> 0
        1 EA4  -> 0
        0 EA3  -> 0
  */
  ITU.TOER.BYTE = 0 ;
  /* TIOR : Timer I/O Control Register
        7 **** -> 0
        6 IOB2 -> 0 GRB is not output compare match register
        5 IOB1 -> 0
        4 IOB0 -> 0
        3 **** -> 0
        2 IOA2 -> 0 GRA is not output compare match register
        1 IOA1 -> 0
        0 IOA0 -> 0
  */
  ITU0.TIOR.BYTE = 0 ;
  /* TCR : Timer Control Register
        7 ****  -> 0
        6 CCLR1 -> 0 clear TCNT if GRA = TCNT
        5 CCLR0 -> 1
        4 CKEG1 -> 0 rising edge
        3 CKEG0 -> 0
        2 TPSC2 -> 0 φ利用
        1 TPSC1 -> 0
        0 TPSC0 -> 0
  */
  ITU0.TCR.BYTE = 0x20 ;
  /* TIER : Timer Interrupt Enable Register
        7 ****  -> 0
        6 ***   -> 0
        5 ***   -> 0
        4 ***   -> 0
        3 ***   -> 0
        2 OVIE  -> 0
        1 IMIEB -> 0
        0 IMIEA -> 1 select compare match interrupt
  */
  ITU0.TIER.BIT.IMIEA = ON ;
  /* reference */
  ITU0.GRA = ITU0_AREG ;
  ITU0.GRB = MASKFFFF ;
  /* counter */
  ITU0.TCNT = 0 ;
  /* start timer */
  ITU.TSTR.BIT.STR0 = ON ;
}

void init_timer1(void)
{
  /* stop timer */
  ITU.TSTR.BIT.STR1 = OFF ;
  /* TOER : Timer Output Enable Register
        7 **** -> 0
        6 **** -> 0
        5 EXB4 -> 0
        4 EXA4 -> 0
        3 EB3  -> 0
        2 EB4  -> 0
        1 EA4  -> 0
        0 EA3  -> 0
  */
  ITU.TOER.BYTE = 0 ;
  /* TIOR : Timer I/O Control Register
        7 **** -> 0
        6 IOB2 -> 0 GRB is not output compare match register
        5 IOB1 -> 0
        4 IOB0 -> 0
        3 **** -> 0
        2 IOA2 -> 0 GRA is not output compare match register
        1 IOA1 -> 0
        0 IOA0 -> 0
  */
  ITU1.TIOR.BYTE = 0 ;
  /* TCR : Timer Control Register
        7 ****  -> 0
        6 CCLR1 -> 0 clear TCNT if GRA = TCNT
        5 CCLR0 -> 1
        4 CKEG1 -> 0 rising edge
        3 CKEG0 -> 0
        2 TPSC2 -> 0 φ利用
        1 TPSC1 -> 0
        0 TPSC0 -> 0
  */
  ITU1.TCR.BYTE = 0x20 ;
  /* TIER : Timer Interrupt Enable Register
        7 ****  -> 0
        6 ***   -> 0
        5 ***   -> 0
        4 ***   -> 0
        3 ***   -> 0
        2 OVIE  -> 0
        1 IMIEB -> 0
        0 IMIEA -> 1 select compare match interrupt
  */
  ITU1.TIER.BIT.IMIEA = ON ;
  /* reference */
  ITU1.GRA = ITU1_AREG ;
  ITU1.GRB = MASKFFFF ;
  /* counter */
  ITU1.TCNT = 0 ;
  /* start timer */
  ITU.TSTR.BIT.STR1 = ON ;
}

/*+++++++++++++++++++++++++++++++++++++*/
/* ITU0 interrupt with compare match A */
/*                        1ms interval */
/*+++++++++++++++++++++++++++++++++++++*/
void int_imia0(void)
{
  UBYTE dummy ;
  /* clear flag */
  dummy = ITU0.TSR.BIT.IMFA ;
  ITU0.TSR.BIT.IMFA = OFF ;
  /* increment */
  timcnt++ ;
  /* judge */
  if ( (timcnt & 0x1ff) == 500 ) { TFLAG = ON ; }
}

/*+++++++++++++++++++++++++++++++++++++*/
/* ITU1 interrupt with compare match A */
/*                      0.1ms interval */
/*+++++++++++++++++++++++++++++++++++++*/
void int_imia1(void)
{
  UBYTE dummy ;
  UBYTE xport ;
  /* clear flag */
  dummy = ITU1.TSR.BIT.IMFA ;
  ITU1.TSR.BIT.IMFA = OFF ;
  /* impress */
  xport = 0 ;
  if ( pcnt < rdutyx ) { xport |= 0x01 ; }
  if ( pcnt < ldutyx ) { xport |= 0x04 ; }
  PBDR = xport ;
  /* increment */
  pcnt++ ;
  /* judge */
  if ( pcnt == 100 ) {
    pcnt = 0 ;
    rdutyx = rduty ;
    ldutyx = lduty ;
  }
  /* shift */
  sft <<= 1 ;
  sft &= 0x07 ;
  /* get start trigger switch */
  if ( SW_START == OFF ) { sft |= ON ; }
  /* judge */
  if ( sft == 0x03 ) { SFLAG = ON ; }
}

/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void  init_sci_1(TBaudRate x)
{
  volatile UWORD i;
  /* SCR : Serial Control Register
    7 bit TIE  -> 0 Transmit Interrupt Enable(disable)
    6 bit RIE  -> 0 Receive  Interrupt Enable(disable)
    5 bit TE   -> 0 Transmit Enable(disable)
    4 bit RE   -> 0 Receive  Enable(disable)
    3 bit MPIE -> 0 Multi Processor Interrupt Enable(disable)
    2 bit TEIE -> 0 Transmit End Interrupt Enable(disable)
    1 bit CKE1 -> 0 Clock Source (Use Internal Baud Rate Generator)
    0 bit CKE0 -> 0 
  */
  SCI1.SCR.BYTE = 0 ;
  /* SMR : Serial Mode Register
    7 bit C/nA -> 0 Communication Mode(Asynchronous)
    6 bit CHR  -> 0 data Charactor (8 bits)
    5 bit PE   -> 0 Parity Enable(disable)
    4 bit O/nE -> 0 Parity Mode(even)
    3 bit STOP -> 0 Stop Bit(1 bit)
    2 bit MP   -> 0 Multi Processor(disable)
    1 bit CKS1 -> 0 Clock Source ( φ )
    0 bit CKS0 -> 0 
  */
  SCI1.SMR.BYTE = 0 ;
  /* data transfer speed */
  SCI1.BRR = x ;
  /* wait 1 frame */
  for (i = 0; i < 3000 ; i++) ;
  /* enable Transmmit and Receive with interrupt */
  SCI1.SCR.BYTE = 0x70 ;
}

/*+++++++++++++++++++++++++*/
/* SCI_1 receive interrupt */
/*+++++++++++++++++++++++++*/
void int_rxi1(void)
{
  volatile UBYTE ch,dummy ;
  /* clear flag */
  dummy = SCI1.SSR.BYTE ;
  SCI1.SSR.BIT.RDRF = OFF ;
  /* get a character */
  ch = SCI1.RDR ;
  /* store */
  *(sbuf+sindex) = ch ;
  sindex++ ;
  /* check */
  if ( ch == '\r' ) {
    *(sbuf+sindex) = 0 ;
    sindex = 0 ;
    UFLAG  = ON ;
  }
}

/*+++++++++++++++*/
/* SCI_1 putchar */
/*+++++++++++++++*/
void rs1_putchar(UBYTE x)
{
  /* wait data transfer */
  while ( SCI1.SSR.BIT.TDRE == OFF ) ;
  /* put */
  SCI1.TDR = x ;
  SCI1.SSR.BIT.TDRE = OFF ;
}

/*++++++++++++*/
/* SCI_1 puts */
/*++++++++++++*/
void rs1_puts(UBYTE *x)
{
  /* send 1 charactors */
  while ( *x ) {
    rs1_putchar(*x);
    x++ ;
  }
  /* new line */
  rs1_crlf();
}

/*++++++++++++*/
/* SCI_1 crlf */
/*++++++++++++*/
void rs1_crlf(void)
{
  rs1_putchar('\r');
  rs1_putchar('\n');
}

/*++++++++++++++++++++*/
/* SCI_1 command help */
/*++++++++++++++++++++*/
void show_help(void)
{
  rs1_puts("? help");
  rs1_puts("A lamp flashing");
  rs1_puts("E store data to EEPROM");
  rs1_puts("C show EEPROM contents");
  rs1_puts("L send strings to LCD");
  rs1_puts("M test motor");
  rs1_puts("P set GBC parameters");
}

void show_value(UBYTE x)
{
  volatile UBYTE msg[2] ;
  volatile UBYTE i ;
  /* separate */
  *(msg+0) = (x >> 4) & MASK0F ;
  *(msg+1) = x & MASK0F ;
  /* conversion */
  *(msg+0) = asc_hex[ *(msg+0) ] ;
  *(msg+1) = asc_hex[ *(msg+1) ] ;
  /* output */
  rs1_putchar( *(msg+0) ) ;
  rs1_putchar( *(msg+1) ) ;
}

UBYTE get_hex(UBYTE x)
{
  UBYTE 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 ;
}

void delay_ms(UWORD x)
{
  ULONG target ;
  /* calculate last value */
  target = timcnt + x ;
  /* wait */
  while ( timcnt < target ) ;
}

/*+++++++++++++++++++++++++++++++*/
/* EEPROM enable erase and write */
/*+++++++++++++++++++++++++++++++*/
void  eeprom_ewen(void)
{
  UWORD tmp ;
  UBYTE i ;
  /* SB , OP code , dummy */
  tmp = 0x130 ;
  /* enable chip select */
  EEPROM_CS = ON ;
  /* send */
  for ( i = 0 ; i < 9 ; i++ ) {
    /* impress DI */
    EEPROM_DI = OFF ;
    if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
    /* SK : H */
    EEPROM_SK = ON ;
    /* shift */
    tmp <<= 1 ;
    /* SK : L */
    EEPROM_SK = OFF ;
  }
  /* clear DI */
  EEPROM_DI = OFF ;
  /* disable chip select */
  EEPROM_CS = OFF ;
}

/*+++++++++++++++++*/
/* EEPROM put data */
/*+++++++++++++++++*/
void  eeprom_put(UBYTE ax,UWORD dx)
{
  UWORD tmp ;
  UBYTE i ;
  /* judge */
  if ( ax > 63 ) return ;
  /* SB , OP code , dummy */
  tmp = 0x140 | (ax & MASK3F) ;
  /* enable chip select */
  EEPROM_CS = ON ;
  /* send address */
  for ( i = 0 ; i < 9 ; i++ ) {
    /* impress DI */
    EEPROM_DI = OFF ;
    if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
    /* SK : H */
    EEPROM_SK = ON ;
    /* shift */
    tmp <<= 1 ;
    /* SK : L */
    EEPROM_SK = OFF ;
  }
  /* send data */
  for ( i = 0 ; i < 16 ; i++ ) {
    /* impress DI */
    EEPROM_DI = OFF ;
    if ( dx & 0x8000 ) { EEPROM_DI = ON ; }
    /* SK : H */
    EEPROM_SK = ON ;
    /* shift */
    dx <<= 1 ;
    /* SK : L */
    EEPROM_SK = OFF ;
  }
  /* disable chip select */
  EEPROM_CS = OFF ;
  /* delay */
  delay_ms(10) ;
}

/*+++++++++++++++++*/
/* EEPROM get data */
/*+++++++++++++++++*/
UWORD eeprom_get(UBYTE ax)
{
  UWORD result ;
  UWORD tmp ;
  UBYTE i ;
  /* judge */
  if ( ax > 63 ) { return 0 ; }
  /* SB , OP code , dummy */
  tmp = 0x180 | (ax & MASK3F) ;
  /* enable chip select */
  EEPROM_CS = ON ;
  /* send address */
  for ( i = 0 ; i < 9 ; i++ ) {
    /* impress DI */
    EEPROM_DI = OFF ;
    if ( tmp & 0x100 ) { EEPROM_DI = ON ; }
    /* SK : H */
    EEPROM_SK = ON ;
    /* shift */
    tmp <<= 1 ;
    /* SK : L */
    EEPROM_SK = OFF ;
  }
  /* default */
  result = 0 ;
  /* get data */
  for ( i = 0 ; i < 16 ; i++ ) {
    /* shift */
    result <<= 1 ;
    /* SK : H */
    EEPROM_SK = ON ;
    /* get data */
    result |= EEPROM_DO ;
    /* SK : L */
    EEPROM_SK = OFF ;
  }
  /* disable chip select */
  EEPROM_CS = OFF ;

  return result ;
}

void  lcd_primitive(UWORD x)
{
  UBYTE i ;
  UBYTE dd ;
  /* get kind */
  LCD_RS = OFF ;
  if ( x & 0x100 ) { LCD_RS = ON ; }
  /* send data */
  dd = x & MASKFF ;
  for ( i = 0 ; i < 8 ; i++ ) {
    /* impress data */
    HC_DAT = OFF ;
    if ( dd & MASK80 ) { HC_DAT = ON ; }
    /* CLK : H */
    HC_CLK = ON ;
    /* shift */
    dd <<= 1 ;
    /* CLK : L */
    HC_CLK = OFF ;
  }
  /* impress E */
  LCD_E = ON ;
  dd <<= 1 ; /* dummy */
  LCD_E = OFF ;
  /* default */
  LCD_RS = OFF ;
  HC_DAT = OFF ;
}

void  lcd_cmd(UBYTE x)
{
  lcd_primitive( (UWORD)x );
}

void  lcd_dat(UBYTE x)
{
  lcd_primitive( x | 0x100 );
}

void  send_lcd_handler(void)
{
  UBYTE line  ;
  UBYTE pixel ;
  /* line handling */
  for ( line = 0 ; line < 2 ; line++ ) {
    /* set address */
    lnumber = 0x00 ;
    if ( line ) { lnumber = 0x40 ; }
    lnumber |= MASK80 ;
    lcd_cmd( lnumber );
    /* calculate line */
    lcd_index = 16 * line ;
    /* pixel handling */
    for ( pixel = 0 ; pixel < 16 ; pixel++ ) {
      lcd_dat( *(lcd_display+lcd_index+pixel) );
    }
  }
}

void init_lcd(void)
{
  /* initialize hardware */
  delay_ms(15) ;
  lcd_cmd(0x30);
  delay_ms(5) ; /* 5ms */
  lcd_cmd(0x30);
  delay_ms(1); /* 1ms */
  lcd_cmd(0x30);
  delay_ms(1); /* 1ms */
  /* set function */
  lcd_cmd(0x38);
        /*
        Function
        001 DL N F * *
        DL(Data Length) = 1 (8bits)
        N(Row)          = 1 (4 row)
        F(Font)         = 0 (5x7)
        001 1 1 0 * * 
        */
  lcd_cmd(0x08);
        /*
        Display off
        0000 1 D C B 
        D(Display) = 0 (OFF)
        C(Cursor)  = 0 (OFF)
        B(Blink)   = 0 (OFF)
        0000 1 0 0 0
        */
  lcd_cmd(0x01);
  delay_ms(2); /* 2ms */
  lcd_cmd(0x0c);
        /*
        Display on
        0000 1 D C B 
        D(Display) = 1 (ON)
        C(Cursor)  = 0 (OFF)
        B(Blink)   = 0 (OFF)
        0000 1 1 0 0 
        */
}

void  reset_gbc(void)
{
  GBC_XCLK = OFF ;
  /* enable XRST */
  GBC_XRST = OFF ;
  /* XCLK : H */
  GBC_XCLK = ON ;
  /* XCLK : L */
  GBC_XCLK = OFF ;
  /* disable XRST */
  GBC_XRST = ON ;
}

void  send_param_gbc(void)
{
  UBYTE loop ;
  UBYTE i ;
  UBYTE tmp ;
  /* loop */
  for ( loop = 0 ; loop < 8 ; loop++ ) {
    /* get data */
    tmp = *(gbcparam+loop);
    /* send data */
    for ( i = 0 ; i < 8 ; i++ ) {
      /* impress SIN */
      GBC_SIN = OFF ;
      if ( tmp & MASK80 ) { GBC_SIN = ON ; }
      /* enable LOAD */
      if ( i == 7 ) { GBC_LOAD = ON ; }
      /* XCLK : H */
      GBC_XCLK = ON ;
      /* shift */
      tmp <<= 1 ;
      /* XCLK : L */
      GBC_XCLK = OFF ;
      /* set default value */
      GBC_LOAD = OFF ;
    }
    /* set default values */
    GBC_SIN  = OFF ;
  }
}

void  send_start_gbc(void)
{
  GBC_XCLK = OFF ;
  /* enable START */
  GBC_START = ON ;
  /* XCLK : H */
  GBC_XCLK = ON ;
  /* XCLK : L */
  GBC_XCLK = OFF ;
  /* disable START */
  GBC_START = OFF ;
  /* clear */
  gstate = 0 ;
  /* enable */
  GFLAG = ON ;
}

void  gbc_handler(void)
{
  switch ( gstate ) {
    /* wait trigger */
    case 0 : if ( GFLAG == ON ) {
               GFLAG  = OFF ;
               gstate = 1 ;
             }
             break ;
    /* wait READ */
    case 1 : if ( GBC_READ == ON ) {
               gstate = 2 ;
               gcnt   = 0 ;
               gindex = 0 ;
               rs1_puts("*** Start ***");
             }
             break ;
    /* judge */
    case 2 : if ( gcnt == 15744 ) {
               gstate = 3 ;
               gcnt = 0 ;
               rs1_puts("*** Complete ***");
             }
             break ;
    /* return first state */
    case 3 : gstate = 0 ;
             break ;
    /* */
    default : gstate = 0 ; break ;
  }
  /* store data */
  {
    /* XCLK : H */
    GBC_XCLK = ON ;
    if ( GBC_READ == ON ) {
      /* start A/D convert */
      AD.ADCSR.BIT.ADST = 1 ;
    }
    /* XCLK : L */
    GBC_XCLK = OFF ;
    if ( GBC_READ == ON ) {
      /* wait A/D conversion */
      while ( AD.ADCSR.BIT.ADF == 0 ) ;
      AD.ADCSR.BIT.ADF = 0 ;
      /* store */
      if ( slinex <= gcnt && gcnt <= elinex ) {
        *(gbcdat+gindex) = (AD.ADDRA >> 8);
        gindex++ ;
      }
      /* update counter */
      gcnt++ ;
    }
  }
}

void  show_gbc_data(void)
{
  UWORD loop ;
  UBYTE tmp ;
  UBYTE msg[3] ;
  for ( loop = 0 ; loop < 4096 ; loop++ ) {
    /* get data */
    tmp = *(gbcdat+loop);
    /* separate */
    *(msg+0) = tmp / 100 ; tmp /= 100 ;
    *(msg+1) = tmp / 10 ;
    *(msg+2) = tmp % 10 ;
    /* conversion */
    *(msg+0) = asc_hex[*(msg+0)] ;
    *(msg+1) = asc_hex[*(msg+1)] ;
    *(msg+2) = asc_hex[*(msg+2)] ;
    /* suppress zero */
    tmp = *(gbcdat+loop);
    if ( tmp < 100 ) {
      *(msg+0) = ' ' ;
    } else {
      if ( tmp < 10 ) {
        *(msg+1) = ' ' ;
      }
    }
    /* send */
    rs1_putchar( *(msg+0) ) ; rs1_putchar( *(msg+1) ) ;
    rs1_putchar( *(msg+2) ) ; rs1_putchar( ' ' ) ;
    /* new line */
    if ( (loop % 8) == 7 ) { rs1_crlf() ; }
  }
}

 このファームウエアで、動作テストが完了したなら
 画像処理と走行制御を付加して、完成させます。

 H8でGameBoyCameraを制御する前に、ATmega168により
 動作確認しました。接続は、次のようにしました。


 GameBoyCameraとのインタフェースは、ポートC
 全体とポートBの1ビットを利用します。

 他のマイコン、デジタル回路とのインタフェース
 は、ポートDをデータバス、ポートBを制御バス
 にします。

 ATmega168は、GameBoyCameraと一体になったユニット
 基板のインタフェースを担当します。



 制御、データバスを利用してコマンドや
 情報交換できるようにしました。

 コマンドの種別は、以下。

 GBCの制御コマンドを3種、画像処理の結果を
 レジスタに保存しているとして、コマンドを
 2種用意しました。これらは、バスに8ビット
 のコマンドを出力し、CTRGでATmega168に入力
 します。

 GBCで3ラインのデータを画像処理し、レジスタ0から5の
 各8バイトに格納。偶数アドレスレジスタにはセンター位置
 (0〜160の値)、奇数アドレスレジスタにはセンターライン
 の幅を格納します。

 3ラインをどれにするかは、バスにライン番号を
 出力し、LTRGでATmega168に入力します。

 バスの方向は、ITRG、OTRGによりATmega168に指示します。

 レジスタ0〜5の6バイトの内容を取り出す
 には、次のシーケンスを使います。
  1. CMD_ZEROをバスに出力し、CTRGを与える
  2. OTRGを与えて、バスにデータを出力させる
  3. バス上のデータをリード後、ITRGを与える(レジスタ0の値)
  4. CMD_INCRをバスに出力し、CTRGを与える
  5. OTRGを与えて、バスにデータを出力させる
  6. バス上のデータをリード後、ITRGを与える(レジスタ1の値)
  7. CMD_INCRをバスに出力し、CTRGを与える
  8. OTRGを与えて、バスにデータを出力させる
  9. バス上のデータをリード後、ITRGを与える(レジスタ2の値)
  10. CMD_INCRをバスに出力し、CTRGを与える
  11. OTRGを与えて、バスにデータを出力させる
  12. バス上のデータをリード後、ITRGを与える(レジスタ3の値)
  13. CMD_INCRをバスに出力し、CTRGを与える
  14. OTRGを与えて、バスにデータを出力させる
  15. バス上のデータをリード後、ITRGを与える(レジスタ4の値)
  16. CMD_INCRをバスに出力し、CTRGを与える
  17. OTRGを与えて、バスにデータを出力させる
  18. バス上のデータをリード後、ITRGを与える(レジスタ5の値)
 GBCを動かして、画像処理中は、BUSY信号が'H'  であることを見て確認します。  このような操作ができるように作成した  ATmega168のファームウエアは、以下です。 #include <avr/io.h> #include <avr/interrupt.h> #define OFF 0 #define ON OFF+1 typedef unsigned char UBYTE ; typedef unsigned short UWORD ; typedef signed char SBYTE ; typedef signed short SWORD ; typedef union { struct { unsigned char B0:1; unsigned char B1:1; unsigned char B2:1; unsigned char B3:1; unsigned char B4:1; unsigned char B5:1; unsigned char B6:1; unsigned char B7:1; } BIT ; unsigned char DR ; } FLAGSP ; volatile FLAGSP x_flags ; #define BTRG x_flags.BIT.B0 /* BUS command trigger */ #define LTRG x_flags.BIT.B1 /* set target line number */ #define CTRG x_flags.BIT.B2 /* convert binary */ #define OTRG x_flags.BIT.B3 /* change PORTD output */ #define ITRG x_flags.BIT.B4 /* change PORTD input */ #define RTRG x_flags.BIT.B5 /* READ rising edge flag */ #define ECLKFLAG x_flags.BIT.B6 /* enable XCK signal */ #define LINE_MAX 64 /* maximum line counter */ #define PIXEL_MAX 128 /* maximum pixel counter */ volatile UBYTE param[8] ; /* graphic data */ volatile UBYTE line_top[PIXEL_MAX] ; volatile UBYTE line_middle[PIXEL_MAX] ; volatile UBYTE line_bottom[PIXEL_MAX] ; /* binary data */ volatile UBYTE bline_top[PIXEL_MAX] ; volatile UBYTE bline_middle[PIXEL_MAX] ; volatile UBYTE bline_bottom[PIXEL_MAX] ; #define NO 0 #define YES NO+1 #define MASK07 0x07 #define MASK80 0x80 #define MASK40 0x40 #define MASK20 0x20 #define MASK10 0x10 #define MASK08 0x08 #define MASK04 0x04 #define MASK02 0x02 #define MASK01 0x01 #define CMD_RESET 0x01 /* send RESET signal to GBC */ #define CMD_SETP 0x02 /* send parameters to GBC */ #define CMD_START 0x04 /* start take shot */ #define CMD_ZERO 0x08 /* clear register address */ #define CMD_INCR 0x10 /* register address increment */ volatile UBYTE cmd ; volatile UBYTE tline[3] ; /* target line */ volatile UBYTE bsft ; /* BUS command */ volatile UBYTE lsft ; /* target line */ volatile UBYTE osft ; /* output trigger */ volatile UBYTE isft ; /* input trigger */ volatile UBYTE cam_dat ; /* camera data */ volatile UBYTE cstate ; /* camera handling sequencer */ volatile UBYTE xlcnt ; /* line counter */ volatile UBYTE xpcnt ; /* pixel counter */ volatile UBYTE reg[6] ; /* register values */ volatile UBYTE rindex ; /* register index */ #define XCK_H (PORTC |= (1 << PC1)) #define XCK_L (PORTC &= ~(1 << PC1)) #define XRST_H (PORTC |= (1 << PC2)) #define XRST_L (PORTC &= ~(1 << PC2)) #define LOAD_H (PORTC |= (1 << PC3)) #define LOAD_L (PORTC &= ~(1 << PC3)) #define SIN_H (PORTC |= (1 << PC4)) #define SIN_L (PORTC &= ~(1 << PC4)) #define START_H (PORTC |= (1 << PC5)) #define START_L (PORTC &= ~(1 << PC5)) #define BUSY_H (PORTB |= (1 << PB4)) #define BUSY_L (PORTB &= ~(1 << PB4)) /*--------------------------------*/ /* Insert user functions protoype */ /*--------------------------------*/ void user_initialize(void); void gbc_reset(void); void put_param(UWORD x); void gbc_set_param(void); void gbc_start(void); UBYTE get_adv(void); void camera_handler(void); void bconv_handler(void); void lnsort(void); UBYTE dhalf(UBYTE x,UBYTE y); UBYTE xabs(UBYTE x,UBYTE y); /*------*/ /* main */ /*------*/ int main(void) { user_initialize(); /* enable interrupt */ sei(); /* loop */ while ( ON ) { /* command interpreter */ if ( BTRG == ON ) { /* clear flag */ BTRG = OFF ; /* get command */ cmd = PIND ; /* judge */ if ( cmd == CMD_RESET ) { gbc_reset(); } if ( cmd == CMD_SETP ) { gbc_set_param(); } if ( cmd == CMD_START ) { gbc_start(); ECLKFLAG = ON ; BUSY_H ; } if ( cmd == CMD_ZERO ) { rindex = 0 ; } if ( cmd == CMD_INCR ) { rindex++ ; if ( rindex == 6 ) { rindex = 0 ; } } } /* store target line number */ if ( LTRG == ON ) { /* clear flag */ LTRG = OFF ; /* shift */ *(tline+0) = *(tline+1) ; *(tline+1) = *(tline+2) ; /* get line number */ *(tline+2) = PIND ; /* sort */ lnsort(); } /* output register data */ if ( OTRG == ON ) { /* clear flag */ OTRG = OFF ; /* impress data */ PORTD = *(reg+rindex) ; /* change output */ DDRD = 0xff ; } /* PORTD input */ if ( ITRG == ON ) { /* clear flag */ ITRG = OFF ; /* change input */ DDRD = 0x00 ; } /* binary conversion */ if ( CTRG == ON ) { /* clear flag */ CTRG = OFF ; /* do */ bconv_handler(); } /* get data from camera */ camera_handler(); } /* dummy */ return 0 ; } void user_initialize(void) { UBYTE i ; /* PORT C */ PORTC = 0b11111100 ; /* 11111110 */ DDRB = 0b11111110 ; /* oooooooi */ /* PORT B */ PORTB = 0b11101111 ; /* 11101111 */ DDRB = 0b00010000 ; /* iiioiiii */ /* PORT D */ PORTC = 0b00000000 ; /* 00000000 */ DDRB = 0b00000000 ; /* iiiiiiii */ /* initialize A/D converter */ { ADMUX = 0x40 ; /* Vref = AVcc , channel 0 */ ADCSRA = 0x80 ; /* enable A/D conversion */ DIDR0 = 0x01 ; /* PC0 is disable digital inputs */ } /* initialize timer 0 */ { /* CTC */ TCCR0A = (1 << WGM01) ; /* prescaler 1/64 250kHz */ TCCR0B = (3 << CS00) ; /* set timer0 ouput compare match register A */ OCR0A = 249 ; /* 1kHz */ OCR0B = 255 ; /* clear counter */ TCNT0 = 0 ; /* enable interrupt */ TIMSK0 |= (1 << OCIE0A) ; } /* clear shift registers */ bsft = 0 ; lsft = 0 ; osft = 0 ; isft = 0 ; /* target line */ *(tline+0) = 0 ; *(tline+1) = 0 ; *(tline+2) = 0 ; /* set camera parameters */ *(param+0) = 0xa1 ; *(param+1) = 0xec ; *(param+2) = 0x05 ; *(param+3) = 0x00 ; *(param+4) = 0x01 ; *(param+5) = 0x00 ; *(param+6) = 0x01 ; *(param+7) = 0x03 ; /* clear pixel data */ for ( i = 0 ; i < PIXEL_MAX ; i++ ) { /* graphic data */ *(line_top+i) = 0 ; *(line_middle+i) = 0 ; *(line_bottom+i) = 0 ; /* binary data */ *(bline_top+i) = 0 ; *(bline_middle+i) = 0 ; *(bline_bottom+i) = 0 ; } /* */ cstate = 0 ; cam_dat = 0 ; rindex = 0 ; } /************************/ /* Timer0 compare match */ /* frequency 1ms */ /************************/ ISR(TIMER0_COMPA_vect) { UBYTE tmp ; /* shift */ bsft <<= 1 ; lsft <<= 1 ; osft <<= 1 ; isft <<= 1 ; /* get state */ tmp = PINB ; if ( tmp & MASK01 ) { bsft |= ON ; } if ( tmp & MASK02 ) { lsft |= ON ; } if ( tmp & MASK04 ) { osft |= ON ; } if ( tmp & MASK08 ) { isft |= ON ; } /* mask */ bsft &= MASK07 ; lsft &= MASK07 ; osft &= MASK07 ; isft &= MASK07 ; /* judge and generate trigger */ if ( bsft == 0x03 || bsft == 0x01 ) { BTRG = ON ; } if ( lsft == 0x03 || lsft == 0x01 ) { LTRG = ON ; } if ( osft == 0x03 || osft == 0x01 ) { OTRG = ON ; } if ( isft == 0x03 || isft == 0x01 ) { ITRG = ON ; } } void gbc_reset(void) { XRST_L ; /* XRST : L */ XCK_H ; /* XCK : H */ XRST_H ; /* XRST : H */ XCK_L ; /* XCK : L */ } #define SIN_BIT 0x400 void put_param(UWORD x) { UBYTE i ; for ( i = 0 ; i < 11 ; i++ ) { /* impress SIN */ SIN_L ; if ( x & SIN_BIT ) { SIN_H ; } /* LOAD */ LOAD_L ; if ( i == 10 ) { LOAD_H ; } /* XCK : H */ XCK_H ; /* shift */ x <<= 1 ; /* XCK : L */ XCK_L ; } LOAD_L ; } void gbc_set_param(void) { UWORD params[8] ; UBYTE i ; for ( i = 0 ; i < 8 ; i++ ) { /* set address */ *(params+i) = (i << 8) ; /* add parameter */ *(params+i) |= *(param+i) ; /* transfer parameter */ put_param( *(params+i) ) ; } } void gbc_start(void) { START_H ; /* START : H */ XCK_H ; /* XCK : H */ START_L ; /* START : L */ XCK_L ; /* XCK : L */ } UBYTE get_adv(void) { UBYTE advh ; UWORD result ; /* start A/D conversion */ ADCSRA |= (1 << ADSC) ; /* ? complete conversion */ while ( ADCSRA & (1 << ADSC) ) ; /* get values */ advh = ADCH ; /* concatenate */ result = (advh << 8) | ADCL ; return (UBYTE)(result >> 2); } void camera_handler(void) { /* READ state */ RTRG = OFF ; if ( PINB & MASK20 ) { RTRG = ON ; } /* get data from CAMERA */ switch ( cstate ) { /* dummy XCK_H */ case 0 : if ( ECLKFLAG == ON ) { XCK_H ; cstate = 1 ; /* next state */ } break ; /* dummy XCK_L */ case 1 : XCK_L ; if ( RTRG == ON ) { cstate = 5 ; ECLKFLAG = OFF ; xlcnt = 0 ; /* clear line counter */ xpcnt = 0 ; /* clear pixel counter */ } else { cstate = 2 ; /* next state */ } break ; /* judge */ case 2 : cstate = 0 ; /* first state */ break ; /* XCLK : H */ case 3 : XCK_H ; /* next state */ cstate = 4 ; break ; /* XCLK : L */ case 4 : XCK_L ; /* next state */ cstate = 5 ; break ; /* store */ case 5 : cam_dat = get_adv(); if ( xlcnt == *(tline+0) ) { *(line_top+xpcnt) = cam_dat ; } if ( xlcnt == *(tline+1) ) { *(line_top+xpcnt) = cam_dat ; } if ( xlcnt == *(tline+2) ) { *(line_bottom+xpcnt) = cam_dat ; } /* next state */ cstate = 6 ; break ; /* update pixel counter */ case 6 : xpcnt++ ; if ( xpcnt == PIXEL_MAX ) { /* clear pixel counter */ xpcnt = 0 ; /* next state */ cstate = 7 ; } else { /* get data */ cstate = 4 ; } break ; /* update line counter */ case 7 : xlcnt++ ; if ( xlcnt == LINE_MAX ) { /* clear line counter */ xlcnt = 0 ; /* next state */ cstate = 8 ; } else { /* get data */ cstate = 4 ; } break ; /* return first state */ case 8 : cstate = 0 ; /* set conversion trigger */ CTRG = ON ; /* complete */ BUSY_L ; break ; /* */ default : cstate = 0 ; break ; } } void bconv_handler(void) { UBYTE i ; UBYTE xmax[3] ; UBYTE xmin[3] ; UBYTE thv[3] ; UBYTE lpix[3] ; UBYTE rpix[3] ; /* psuedo */ *(xmax+0) = *(line_top+0) ; *(xmin+0) = *(line_top+0) ; *(xmax+1) = *(line_middle+0) ; *(xmin+1) = *(line_middle+0) ; *(xmax+2) = *(line_bottom+0) ; *(xmin+2) = *(line_bottom+0) ; /* search */ for ( i = 1 ; i < PIXEL_MAX ; i++ ) { /* max */ if ( *(xmax+0) < *(line_top+i) ) { *(xmax+0) = *(line_top+i) ; } if ( *(xmax+1) < *(line_middle+i) ) { *(xmax+1) = *(line_middle+i) ; } if ( *(xmax+2) < *(line_bottom+i) ) { *(xmax+2) = *(line_bottom+i) ; } /* min */ if ( *(xmin+0) > *(line_top+i) ) { *(xmin+0) = *(line_top+i) ; } if ( *(xmin+1) > *(line_middle+i) ) { *(xmin+1) = *(line_middle+i) ; } if ( *(xmin+2) > *(line_bottom+i) ) { *(xmin+2) = *(line_bottom+i) ; } } /* calculate */ *(thv+0) = dhalf(*(xmax+0),*(xmin+0)) ; *(thv+1) = dhalf(*(xmax+1),*(xmin+1)) ; *(thv+2) = dhalf(*(xmax+2),*(xmin+2)) ; /* conversion */ for ( i = 0 ; i < PIXEL_MAX ; i++ ) { /* default */ *(bline_top+i) = 0 ; *(bline_middle+i) = 0 ; *(bline_bottom+i) = 0 ; /* top */ if ( *(line_top+i) > *(thv+0) ) { *(bline_top+i) = 1 ; } /* middle */ if ( *(line_middle+i) > *(thv+1) ) { *(bline_middle+i) = 1 ; } /* bottom */ if ( *(line_bottom+i) > *(thv+2) ) { *(bline_bottom+i) = 1 ; } } /* initialize location */ *(lpix+0) = PIXEL_MAX ; *(rpix+0) = PIXEL_MAX ; *(lpix+1) = PIXEL_MAX ; *(rpix+1) = PIXEL_MAX ; *(lpix+2) = PIXEL_MAX ; *(rpix+2) = PIXEL_MAX ; /* search */ for ( i = 0 ; i < 127 ; i++ ) { /* top */ *(xmax+0) = *(bline_top+i) ; *(xmin+0) = *(bline_top+i+1) ; if ( *(xmax+0) == 0 && *(xmin+0) == 1 ) { *(lpix+0) = i ; } if ( *(xmax+0) == 1 && *(xmin+0) == 0 ) { *(rpix+0) = i ; } /* middle */ *(xmax+1) = *(bline_middle+i) ; *(xmin+1) = *(bline_middle+i+1) ; if ( *(xmax+1) == 0 && *(xmin+1) == 1 ) { *(lpix+1) = i ; } if ( *(xmax+1) == 1 && *(xmin+1) == 0 ) { *(rpix+1) = i ; } /* bottom */ *(xmax+2) = *(bline_bottom+i) ; *(xmin+2) = *(bline_bottom+i+1) ; if ( *(xmax+2) == 0 && *(xmin+2) == 1 ) { *(lpix+2) = i ; } if ( *(xmax+2) == 1 && *(xmin+2) == 0 ) { *(rpix+2) = i ; } } /* center */ *(reg+0) = dhalf( *(lpix+0) , *(rpix+0) ) ; *(reg+2) = dhalf( *(lpix+1) , *(rpix+1) ) ; *(reg+4) = dhalf( *(lpix+2) , *(rpix+2) ) ; /* width */ *(reg+1) = xabs( *(lpix+0) , *(rpix+0) ) ; *(reg+3) = xabs( *(lpix+1) , *(rpix+1) ) ; *(reg+5) = xabs( *(lpix+1) , *(rpix+1) ) ; } void lnsort(void) { UBYTE tmp ; /* first sort */ if ( *(tline+0) > *(tline+1) ) { tmp = *(tline+0) ; *(tline+0) = *(tline+1) ; *(tline+1) = tmp ; } if ( *(tline+0) > *(tline+2) ) { tmp = *(tline+0) ; *(tline+0) = *(tline+2) ; *(tline+2) = tmp ; } /* second sort */ if ( *(tline+1) > *(tline+2) ) { tmp = *(tline+1) ; *(tline+1) = *(tline+2) ; *(tline+2) = tmp ; } } UBYTE dhalf(UBYTE x,UBYTE y) { UWORD result ; result = x + y ; result >>= 1 ; return (UBYTE)result ; } UBYTE xabs(UBYTE x,UBYTE y) { UBYTE result ; if ( x > y ) { result = x - y ; } else { result = y - x ; } return result ; }  画像処理、次のシーケンスで実現しています。
  1. 128x63のうちの3ライン分の情報をバッファに格納
  2. バッファ中の最大値、最小値を求める
  3. 最大値と最小値の相加平均を閾値とする
  4. 閾値を使い、バッファの値を2値化し他のバッファに格納
  5. 2値化値格納バッファで01と変化する位置を計算
  6. 2値化値格納バッファで10と変化する位置を計算
  7. 5、6の値の相加平均をセンターラインの中心位置とする
  8. 5、6の値の差をラインの幅とする
 画像処理の結果は、センターラインの中心位置と幅になる  ので、センターラインの中心位置はピクセル数160未満で  幅は、スタート前に取得しておいた値と比較できます。  センターラインの中央位置、幅の2パラメータから  マシンはセンターラインの上、左、右のどこにいる  のかと、全白、左白、右白のようなマーカーの判定  ができるようになります。  本番の1週間前にアクシデントがあったときは  このマシンを利用します。
目次

inserted by FC2 system