目次

CCD emulator

 CCDを利用したラインセンサーを入手する前に
 マイクロコンピュータとの接続テストに、エミュ
 レータを考えました。



 利用するCCDは、大阪のデジットで販売されている
 ラインセンサーに含まれていて、次のタイミング
 チャートを持ちます。



 タイミングチャートから理解できることは、以下。

 ピクセル情報をアナログで出力していますが
 デジタル出力としたいので、簡単なDACを用意
 して対応します。4ビットのDACを使います。



 SIがトリガーになっているので、内部カウンタを
 ゼロクリアするために、使います。



 ゼロクリアするときは、シフトレジスタを使い
 立ち上がりエッジを使います。


  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSI_SFT <= "00" ;
    elsif rising_edge(CLOCK) then
      iSI_SFT <= iSI_SFT(0) & iSI ;
    end if ;
  end process ;
  iSI_TRG <= '1' when ( iSI_SFT = "01" ) else '0' ;

 内部カウンタは、次のように定義します。

  process (iSI_TRG,CLK)
  begin
    if ( iSI_TRG = '1' ) then
      iCNT <= 0 ;
    elsif rising_edge(CLK) then
      if ( iCNT = 130 ) then
        iCNT <= 0 ;
      else
        iCNT <= iCNT + 1 ;
      end if ;
    end if ;
  end process ;

 A0は、内部カウンタの値に応じて
 4ビット値を出力すればよいので
 デコーダを用意します。

  iAOUT <= X"F" when ( 31 < iCNT and iCNT <  64 ) else
           X"9" when ( 10 < iCNT and iCNT <  32 ) else
           X"3" when ( 63 < iCNT and iCNT < 100 ) else
           X"0" ;

 このデコーダは、次のような波形を出力します。



 デコーダ部分を変更すれば、様々なラインを実現できます。

 VHDLコードにまとめます。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity tstline is
  port (
    -- system
    nRESET : in  std_logic ;
    CLOCK  : in  std_logic ; -- 4MHz
    -- interface
    SI     : in  std_logic ;
    CLK    : in  std_logic ;
    AOUT   : out std_logic_vector(3 downto 0) ;
    -- monitor
    COUT   : out std_logic_vector(7 downto 0) --;
  );
end tstline ;

architecture behavioral of tstline is
  -- input
  signal iSI  : std_logic ;
  -- shift register
  signal iSI_SFT : std_logic_vector(1 downto 0) ;
  signal iSI_TRG : std_logic ;
  -- internal counter
  signal iCNT : integer range 0 to 130 ;
  -- register
  signal iAOUT : std_logic_vector(3 downto 0) ;
begin
  -- input
  iSI <= SI ;

  -- output
  AOUT <= iAOUT ;
  COUT <= conv_std_logic_vector( iCNT , 8 );

  -- shift registet
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSI_SFT <= "00" ;
    elsif rising_edge(CLOCK) then
      iSI_SFT <= iSI_SFT(0) & iSI ;
    end if ;
  end process ;
  iSI_TRG <= '1' when ( iSI_SFT = "01" ) else '0' ;

  -- internal counter
  process (iSI_TRG,CLK)
  begin
    if ( iSI_TRG = '1' ) then
      iCNT <= 0 ;
    elsif rising_edge(CLK) then
      if ( iCNT = 130 ) then
        iCNT <= 0 ;
      else
        iCNT <= iCNT + 1 ;
      end if ;
    end if ;
  end process ;

  -- decoder 
  iAOUT <= X"F" when ( 31 < iCNT and iCNT <  64 ) else
           X"9" when ( 10 < iCNT and iCNT <  32 ) else
           X"3" when ( 63 < iCNT and iCNT < 100 ) else
           X"0" ;

end behavioral;

 ピンアサインは、以下。

# system
NET "CLOCK"  LOC = "P5"  ;
NET "nRESET" LOC = "P39" ;

# SPI interface
NET "AOUT<0>" LOC = "P1" ;
NET "AOUT<1>" LOC = "P2" ;
NET "AOUT<2>" LOC = "P3" ;
NET "AOUT<3>" LOC = "P4" ;
NET "CLK"     LOC = "P6" ;
NET "SI"      LOC = "P7" ;

# monitor output
NET "COUT<0>" LOC = "P11" ;
NET "COUT<1>" LOC = "P12" ;
NET "COUT<2>" LOC = "P13" ;
NET "COUT<3>" LOC = "P14" ;
NET "COUT<4>" LOC = "P18" ;
NET "COUT<5>" LOC = "P19" ;
NET "COUT<6>" LOC = "P20" ;
NET "COUT<7>" LOC = "P22" ;

 CPLD(XilinxのXC9572)に入れてみると
 マクロセルは最大72マクロセル中の14のみ
 を消費するだけでした。

 デコード部分を拡張しても、XC9572だけで
 対応できると思われます。

 対象となるArduinoのスケッチは、以下。

#include <MsTimer2.h>

#define MAXPIXELS 128

#define OFF 0
#define ON  OFF+1

#define SI_BIT  2
#define CLK_BIT 1

#define LED_BIT 5

#define Threshold_MSG 0
#define SI_MSG        1
#define CLK_MSG       2

/* function prototype */
void  rs_putchar(char x);
void  crlf(void);
void  show_help(void);
void  send_si(byte x);
void  send_clk(byte x);
void  send_led(byte x);
void  update_trigger(void);
void  get_sensor(byte x);
void  show_raw_values(void);
void  show_binary(byte x);
void  show_digit4(word x);
void  clear_buffer(void);
byte  get_hex(char x);
void  show_value_x(byte idx,word x);
word  xcopy(byte x);

/* global variables */
word sensor ;
word xmax ;
word xmin ;
word xth ;
byte xcnt ;
byte eflag ;
byte uflag ;
byte rflag ;
byte mflag ;
byte sindex ;
byte sbuf[8] ;
byte cmd ;
word gdat[MAXPIXELS] ;
byte xclkd ;
byte xsid  ;

void setup()
{
  /* initialize serial port */
  Serial.begin(9600);
  /* set initial state */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x01 ;
  /* set pin directions */
  DDRB = 0xff ;
  DDRC = 0xfe ;
  DDRD = 0xfe ;
  /* clear flags */
  eflag = OFF ;
  uflag = OFF ;
  rflag = OFF ;
  mflag = OFF ;
  /* command interpreter buffer */
  sindex = 0 ;
  /* initialize */
  xcnt = 0 ;
  clear_buffer();
  /* 500ms period */
  MsTimer2::set(500,update_trigger);
  /* enable */ 
  MsTimer2::start();
  /* set delay (us) */
  xclkd = 5 ;
  xsid  = 10 ;
  /* rapid ADC */
  byte tmp ;
  tmp = ADCSRA & 0xf8 ;
  tmp |= 0x05 ;
  ADCSRA = tmp ;
}

void loop()
{
  byte i ;
  /* sensing */
  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* sampling */
    get_sensor(mflag);
    /* clear flag */
    if ( mflag == ON ) { mflag = OFF ; }
  }
  /* serial handling */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* judge */
    if ( cmd == '?' ) { show_help(); }
    /* execute */
    if ( cmd == 'M' ) { mflag = ON ; }
    /* show */
    if ( cmd == 'S' ) { show_raw_values(); }
    /* show */
    if ( cmd == 'B' ) { show_binary(OFF); }
    /* clear raw values buffer */
    if ( cmd == 'C' ) { clear_buffer(); }
    /* threshold */
    if ( cmd == 'T' ) {
      /* copy */
      xth = xcopy(4) ;
      /* judge */
      if ( xth > 1023 ) { xth = 1023 ; }
      /* show */
      show_value_x( Threshold_MSG , xth );
    }
    /* show with threshold */
    if ( cmd == 'b' ) { show_binary(ON); }
    /* set SI delay */
    if ( cmd == 'I' ) {
      /* copy */
      xsid = (byte)xcopy(3);
      /* judge */
      if ( xsid < 10 ) { xsid = 10 ; }
      /* show */
      show_value_x( SI_MSG , xsid ) ;
    }
    /* CLK periode */
    if ( cmd == 'P' ) {
      /* copy */
      xclkd = (byte)xcopy(3);
      /* judge */
      if ( xclkd < 5 ) { xsid = 5 ; }
      /* show */
      show_value_x( CLK_MSG , xclkd ) ;
    }
    /* show SI and CLK delay */
    if ( cmd == 's' ) {
      /* SI */
      show_value_x( SI_MSG , xsid ) ;
      /* CLK */
      show_value_x( CLK_MSG , xclkd ) ;
    }
  }
}

void  rs_putchar(char x)
{
  Serial.write(x);
}

void  rs_puts(char *x)
{
  while ( *x ) {
    rs_putchar(*x);
    x++ ;
  }
}

void  crlf(void)
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void  show_help(void)
{
  rs_puts("? help");                       crlf();
  rs_puts("M start measure");              crlf();
  rs_puts("S show raw values");            crlf();
  rs_puts("B show binary");                crlf();
  rs_puts("C clear raw data buffer");      crlf();
  rs_puts("T set threshold");              crlf();
  rs_puts("b show binary with threshold"); crlf();
  rs_puts("I set SI delay");               crlf();
  rs_puts("P set CLK period");             crlf();
  rs_puts("s show SI and CLK delay");      crlf();
}

void  send_si(byte x)
{
  if ( x ) { 
    PORTC |=  (1 << SI_BIT);
    /* delay */
    delayMicroseconds(xsid);
  } else {
    PORTC &= ~(1 << SI_BIT);
  }
}

void  send_clk(byte x)
{
  if ( x ) { PORTC |=  (1 << CLK_BIT); }
  else     { PORTC &= ~(1 << CLK_BIT); }
  /* delay more than or equal 5 us */
  delayMicroseconds(xclkd);
}

void  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT); }
  else     { PORTB &= ~(1 << LED_BIT); }
}

void  update_trigger(void)
{
  eflag = ON ;
  send_led( xcnt & ON );
  xcnt++ ;
}

void  get_sensor(byte x)
{
  byte i ;
  /* start SI handling */
  send_si( ON ) ;
  /* show message */
  if ( x == ON ) {
    rs_puts("Start");
    crlf();
  }
  /* transfer */
  for ( i = 0 ; i < MAXPIXELS ; i++ ) {
    /* CLK : H */
    send_clk( ON ) ;
    /* SI handling */
    if ( i == 0 ) { send_si( OFF ) ; }
    /* get value of pixel */
    sensor = analogRead(0) ;
    /* store */
    if ( x == ON ) {
      /* pseudo values */
      if ( i == 0 ) {
        xmax = sensor ;
        xmin = sensor ;
      }
      /* latch data */
      *(gdat+i) = sensor ;
      /* update */
      if ( xmax < sensor ) { xmax = sensor ; }
      if ( xmin > sensor ) { xmin = sensor ; }
      /* disable start trigger */
    }
    /* CLK : L */
    send_clk( OFF ) ;
  }
  /* reset sensor lamp circuit */
  send_clk( ON ) ;
  send_clk( OFF ) ;
  /* delay 20us */
  delayMicroseconds(20);
  /* show message */
  if ( x == ON ) {
    rs_puts("Complete");
    crlf();
  }
}

void  show_raw_values(void)
{
  byte i ;
  word tmp ;
  word xavr ;
  /* calculate average */
  xavr = ((xmax+xmin) >> 1) ;
  /* show maximum , minimum and average */
  rs_puts("Max = ") ; show_digit4( xmax ); crlf();
  rs_puts("Min = ") ; show_digit4( xmin ); crlf();
  rs_puts("Avr = ") ; show_digit4( xavr ); crlf();
  /* raw values */
  for ( i = 0 ; i < MAXPIXELS ; i++ ) {
    /* decimal data */
    show_digit4( *(gdat+i) );
    rs_putchar(' ');
    /* new line */
    if ( (i % 16) == 15 ) { crlf(); }
  }
}

void  show_binary(byte x)
{
  byte i ;
  word xavr ;
  char bn ;
  /* calculate average */
  xavr = ((xmax+xmin) >> 1) ;
  /* judge */
  if ( x ) { xavr = xth ; }
  /* show */
  for ( i = 0 ; i < MAXPIXELS ; i++ ) {
    /* set character */
    bn = '0' ;
    if ( *(gdat+i) > xavr ) { bn = '1' ; }
    /* send */
    rs_putchar(bn);
    /* new line */
    if ( (i % 32) == 31 ) { crlf(); }
  }
}

void  show_digit4(word x)
{
  char msg[4] ;
  byte i ;
  /* separate */
  for ( i = 0 ; i < 4 ; i++ ) {
    *(msg+3-i) = x % 10 + '0' ; 
    x /= 10 ;
  }
  /* zero suppress */
  if ( *(msg+0) == '0' ) {
    *(msg+0) = ' ' ;
    if ( *(msg+1) == '0' ) {
      *(msg+1) = ' ' ;
      if ( *(msg+2) == '0' ) { 
        *(msg+2) = ' ' ; 
      }
    }
  }
  /* send */
  for ( i = 0 ; i < 4 ; i++ ) {
    rs_putchar( *(msg+i) ); 
  }
}

void  show_value_x(byte idx,word x)
{
  /* prompt */
  if ( idx == Threshold_MSG ) { rs_puts("Threshold = "); }
  if ( idx == SI_MSG        ) { rs_puts("SI delay (us) = "); }
  if ( idx == CLK_MSG       ) { rs_puts("CLK delay (us) = "); }
  /* value */
  show_digit4( x ) ;
  /* new line */
  crlf();
}

word  xcopy(byte x)
{
  byte last ;
  byte ii ;
  word result ;
  /* default */
  result = 0 ;
  /* calculate */
  last = x ;
  for ( ii = 0 ; ii < last ; ii++ ) {
    /* judge */ 
    if ( *(sbuf+1+ii) == '\r' ) break ;
    /* add */
    result *= 10 ;
    result += get_hex( *(sbuf+1+ii) ); 
  }
  /* adjust */
  if ( last == 3 && result > 255 ) {
    result = 255 ; 
  }

  return result ;
}

void  clear_buffer(void)
{
  for ( byte i = 0 ; i < MAXPIXELS ; i++ ) { *(gdat+i) = 0 ; }
}

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 ;
}

/* 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 ;
    }
  }
}


目次

inserted by FC2 system