目次

BarCodeScanner処理再考

 BarCodeScanner(BCS)は、4信号で路面情報を
 2048ピクセルのデータとして送信してきます。
 4信号は、以下。

 ピクセルデータは、CLKA、CLKBの立上がりに
 同期して、OSに出力されてきます。



 SHは、ピクセルデータの出力開始を外部に提示します。

 3.3Vで動作するFPGAでは、BCSの5Vを使った信号では
 入出力ピンを破壊することがあるので、バスバッファ
 を入れて対応します。



 74HC14を利用したのは、ヒステリシスを持つので
 信号の変化を確実にとらえるため。

 FPGA側に、プルアップ抵抗を接続し、3.3Vで電圧を
 クランプしています。

 BCSから2048ピクセルのデータが出力されてくるので
 カウンタ値で、8ピクセルだけを記憶してセンサー
 データにします。



 FPGAの内部にシーケンサ(ステートマシン)を用意し
 他の同期化回路から出力されるトリガーで、ピクセル
 データの記憶タイミングを確定します。

 CLKA、CLKBの立上りエッジを利用してピクセルデータ
 が出力されてくるので、CLKA、CLKBの立上りエッジを
 少し遅らせて、記憶用トリガーを生成します。



 記憶用トリガーは、クロックを遅延させるのでシフト
 レジスタを使います。
 CLKA、CLKBは、各々100kHzなので、シフトレジスタを
 動かすクロックは、4倍以上にします。



 MCR_VCマシンは移動メカですから、載せる基板の面積は
 極力小さくしたいので、回路はFPGAの中に入れます。
 FPGAの中に入れるためにVHDLで、回路を定義します。

 SH、CLKA、CLKBのシフトレジスタを定義します。

 SHのシフトレジスタ

  クロックは、FPGA内部にあるシーケンサと同じにします。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSH_SFT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iSH_SFT <= iSH_SFT(0) & iSH ;
    end if ;
  end process ;
  iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;

 CLKA、CLKBのシフトレジスタ

  クロックは、1MHz程度にします。このクロックは
  別に定義して使います。

  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "00" ;
      iCLKB_SFT <= "00" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
      iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
    end if ;
  end process ;

  iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
            '1' when ( iCLKB_SFT = "01" ) else
            '0' ;

  クロックiDCKを1MHzにすると、CLKA、CLKBの立上りエッジ
  から1usの遅延で、幅が1usのトリガーが生成できます。

 内部シーケンサ

  シーケンサは、iSH_TRGを利用して動作を開始します。
  また、iCLKABを利用してピクセルデータを取込みます。
  取込んだピクセルデータは、別途決めるカウント値で
  8ビットのセンサーデータに仕立てます。

  わかりやすい記述にすると、以下。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= 0 ;
      iREGX  <= X"00" ;
      iREG   <= X"00" ;
      iTMP   <= '0' ;
      iSCNT  <= 0 ;
    elsif rising_edge( CLOCK ) then
      case iSTATE is
        -- wait trigger 
        when 0 => if ( iSH_TRG = '1' ) then
                    iSTATE <= 1     ; -- next
                    iREGX  <= X"00" ; -- clear register
                    iSCNT  <= 0     ; -- clear counter
                  else
                    iSTATE <= 0 ; -- stay
                  end if ;
        -- get data
        when 1 => if ( iCLKAB = '1' ) then
                    iSTATE <= 2   ; -- next
                    iTMP   <= iDOS ; -- latch 1 bit
                  else
                    iSTATE <= 1 ; -- stay
                  end if ;
        -- data counter increment
        when 2 => iSTATE <= 3 ; -- next
                  iSCNT  <= iSCNT + 1 ; -- counter increment
        -- store datum to temporary register
        when 3 => iSTATE <= 4 ; -- next
                  -- store
                  if ( iSCNT = S0 or
                       iSCNT = S1 or
                       iSCNT = S2 or
                       iSCNT = S3 or
                       iSCNT = S4 or
                       iSCNT = S5 or
                       iSCNT = S6 or
                       iSCNT = S7    ) then
                    iREGX <= iREGX(6 downto 0) & iTMP ;
                  end if ;
        -- judge (? complete)
        when 4 => if ( iSCNT = SLAST ) then
                    iSTATE <= 6 ; -- return first state
                  else
                    iSTATE <= 5 ; -- loop control
                  end if ;
        -- loop control
        when 5 => iSTATE <= 1 ;
        -- return first state 
        when 6 => iSTATE <= 0 ;
                  iREG   <= iREGX ; -- copy
        -- default
        when others =>
                  iSTATE <= 0 ;
      end case ;
    end if ;
  end process ;

 最終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 ;
    -- divided clock
    DCLK   : out std_logic ;
    -- BCS
    SH     : in  std_logic ;
    CLKA   : in  std_logic ;
    CLKB   : in  std_logic ;
    XOS    : in  std_logic ;
    -- monitor output
    LOUT   : out std_logic_vector(7 downto 0) ;
    -- output
    OST    : out std_logic_vector(7 downto 0) --;
  );
end taa ;

architecture behavioral of taa is
  -- 
  CONSTANT SLAST : integer := 2080 ;
  -- sensing location
  CONSTANT S0 : integer :=  800 ; -- 32+1024+(-256)
  CONSTANT S1 : integer :=  864 ; -- 32+1024+(-192)
  CONSTANT S2 : integer :=  928 ; -- 32+1024+(-128)
  CONSTANT S3 : integer :=  992 ; -- 32+1024+( -64)
  CONSTANT S4 : integer := 1120 ; -- 32+1024+(  64)
  CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
  CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
  CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)
  -- divided clock
  signal iDCNT : std_logic_vector(1 downto 0) ;
  signal iDCK  : std_logic ;
  -- input
  signal iSH   : std_logic ;
  signal iCLKA : std_logic ;
  signal iCLKB : std_logic ;
  signal iDOS  : std_logic ;
  -- shift register
  signal iCLKA_SFT : std_logic_vector(1 downto 0) ;
  signal iCLKB_SFT : std_logic_vector(1 downto 0) ;
  signal iCLKAB    : std_logic ; 
  signal iSH_SFT   : std_logic_vector(1 downto 0) ;
  signal iSH_TRG   : std_logic ;
  -- state machine
  signal iSTATE : integer range 0 to 6 ;
  signal iTMP   : std_logic ;
  signal iREGX  : std_logic_vector(7 downto 0) ;
  signal iREG   : std_logic_vector(7 downto 0) ;
  signal iSCNT  : integer range 0 to SLAST ;
begin
  -- input
  iSH   <= SH   ;
  iCLKA <= CLKA ;
  iCLKB <= CLKB ;
  iDOS  <= XOS  ;

  -- output
  DCLK <= iDCK ;
  OST  <= iREG ;
  LOUT <= not iREG ;

  -- clock divider
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iDCNT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iDCNT <= iDCNT + '1' ;
    end if ;
  end process ;
  iDCK <= '1' when ( iDCNT = "00" ) else '0' ;

  -- clock shifter
  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "00" ;
      iCLKB_SFT <= "00" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
      iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
    end if ;
  end process ;

  -- internal lacth clock trigger
  iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
            '1' when ( iCLKB_SFT = "01" ) else
            '0' ;

  -- synchronizer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSH_SFT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iSH_SFT <= iSH_SFT(0) & iSH ;
    end if ;
  end process ;
  iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;

  -- state machine
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= 0 ;
      iREGX  <= X"00" ;
      iREG   <= X"00" ;
      iTMP   <= '0' ;
      iSCNT  <= 0 ;
    elsif rising_edge( CLOCK ) then
      case iSTATE is
        -- wait trigger 
        when 0 => if ( iSH_TRG = '1' ) then
                    iSTATE <= 1     ; -- next
                    iREGX  <= X"00" ; -- clear register
                    iSCNT  <= 0     ; -- clear counter
                  else
                    iSTATE <= 0 ; -- stay
                  end if ;
        -- get data
        when 1 => if ( iCLKAB = '1' ) then
                    iSTATE <= 2    ; -- next
                    iTMP   <= iDOS ; -- latch 1 bit
                  else
                    iSTATE <= 1 ; -- stay
                  end if ;
        -- data counter increment
        when 2 => iSTATE <= 3 ; -- next
                  iSCNT  <= iSCNT + 1 ; -- counter increment
        -- store datum to temporary register
        when 3 => iSTATE <= 4 ; -- next
                  -- store
                  if ( iSCNT = S0 or
                       iSCNT = S1 or
                       iSCNT = S2 or
                       iSCNT = S3 or
                       iSCNT = S4 or
                       iSCNT = S5 or
                       iSCNT = S6 or
                       iSCNT = S7    ) then
                    iREGX <= iREGX(6 downto 0) & iTMP ;
                  end if ;
        -- judge (? complete)
        when 4 => if ( iSCNT = SLAST ) then
                    iSTATE <= 6 ; -- return first state
                  else
                    iSTATE <= 5 ; -- loop control
                  end if ;
        -- loop control 
        when 5 => iSTATE <= 1 ;
        -- return first state 
        when 6 => iSTATE <= 0 ;
                  iREG   <= iREGX ; -- copy
        -- default
        when others =>
                  iSTATE <= 0 ;
      end case ;
    end if ;
  end process ;

end behavioral;

 CLKA、CLKBは、シフトレジスタで変化をとらえて
 DOSの値を記憶するトリガーを生成します。

 トリガーとして利用するパルスの幅は、シフトレジスタ
 に使うクロックを1MHzとしたとき、1usになります。
 ひとつのトリガーで、2回カウントすると、左によった
 センサー情報になります。

 1usのトリガーに対し、シーケンサは4MHzのクロックで
 動かすため、5クロック後にトリガーの検出をする仕様
 にしてあります。

 トリガー検出→カウンタに+1→センサー値とするか判定→
 終了判定→トリガー検出にもどる

 上のように処理しているので、トリガー検出後
 次のトリガー検出まで5クロック必要です。

 5クロックで1.25usですから、トリガーの1usより
 長くなり、ひとつのトリガーで2回のカウントは
 しないとわかります。

 CLKA、CLKBをシフトレジスタでサンプリングする
 クロックを2MHzにしたときは、以下の回路での
 トリガー生成になります。



 2レジスタを増やし、センサーデータが確定する
 までの時間800nsを確保できるように工夫。
 VHDLコードでは、次のようにします。

  -- clock shifter
  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "00" ;
      iCLKB_SFT <= "00" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
      iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
    end if ;
  end process ;

  -- internal lacth clock trigger
  iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
            '1' when ( iCLKB_SFT = "01" ) else
            '0' ;

 トリガーのパルス幅が0.5usになったので、シーケンサを
 4MHzで動かしても、ひとつのトリガーで2回カウントする
 ことはなくなります。

 Arduinoで移動制御をさせようとすれば、利用できる
 ピン数が少ないので、8ビットのセンサーデータを
 4ビットずつ2回に分けて出力する工夫が必要。

 セレクタを入れ、8ビットの上位、下位の4ビット
 を出力するように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 ; -- 4MHz
    -- divided clock
    DCLK   : out std_logic ; -- 2MHz
    -- BCS
    SH     : in  std_logic ;
    CLKA   : in  std_logic ;
    CLKB   : in  std_logic ;
    DOS    : in  std_logic ;
    -- monitor output
    LOUT   : out std_logic_vector(7 downto 0) ;
    -- output
    RSEL   : in  std_logic ;
    OST    : out std_logic_vector(3 downto 0) --;
  );
end taa ;

architecture behavioral of taa is
  -- 
  CONSTANT SLAST : integer := 2080 ;
  -- sensing location
  CONSTANT S0 : integer :=  800 ; -- 32+1024+(-256)
  CONSTANT S1 : integer :=  864 ; -- 32+1024+(-192)
  CONSTANT S2 : integer :=  928 ; -- 32+1024+(-128)
  CONSTANT S3 : integer :=  992 ; -- 32+1024+( -64)
  CONSTANT S4 : integer := 1120 ; -- 32+1024+(  64)
  CONSTANT S5 : integer := 1184 ; -- 32+1024+( 128)
  CONSTANT S6 : integer := 1248 ; -- 32+1024+( 192)
  CONSTANT S7 : integer := 1312 ; -- 32+1024+( 256)
  -- divided clock
  signal iDCNT : std_logic_vector(1 downto 0) ;
  signal iDCK  : std_logic ;
  -- input
  signal iSH   : std_logic ;
  signal iCLKA : std_logic ;
  signal iCLKB : std_logic ;
  signal iDOS  : std_logic ;
  -- shift register
  signal iCLKA_SFT : std_logic_vector(1 downto 0) ;
  signal iCLKB_SFT : std_logic_vector(1 downto 0) ;
  signal iCLKAB    : std_logic ; 
  signal iSH_SFT   : std_logic_vector(1 downto 0) ;
  signal iSH_TRG   : std_logic ;
  -- state machine
  signal iSTATE : integer range 0 to 6 ;
  signal iTMP   : std_logic ;
  signal iREGX  : std_logic_vector(7 downto 0) ;
  signal iREG   : std_logic_vector(7 downto 0) ;
  signal iSCNT  : integer range 0 to SLAST ;
begin
  -- output
  DCLK <= iDCK ;
  OST  <= iREG(7 downto 4) when ( RSEL = '1' ) else iREG(3 downto 0) ;
  LOUT <= not iREG ;

  -- input
  iSH   <= SH   ;
  iCLKA <= CLKA ;
  iCLKB <= CLKB ;
  iDOS  <= DOS  ;

  -- clock divider
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iDCNT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iDCNT <= iDCNT + '1' ;
    end if ;
  end process ;
  iDCK <= not iDCNT(1) ;

  -- clock shifter
  process (nRESET,iDCK)
  begin
    if ( nRESET = '0' ) then
      iCLKA_SFT <= "00" ;
      iCLKB_SFT <= "00" ;
    elsif rising_edge( iDCK ) then
      iCLKA_SFT <= iCLKA_SFT(0) & iCLKA ;
      iCLKB_SFT <= iCLKB_SFT(0) & iCLKB ;
    end if ;
  end process ;

  -- internal lacth clock trigger
  iCLKAB <= '1' when ( iCLKA_SFT = "01" ) else
            '1' when ( iCLKB_SFT = "01" ) else
            '0' ;

  -- synchronizer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSH_SFT <= "00" ;
    elsif rising_edge( CLOCK ) then
      iSH_SFT <= iSH_SFT(0) & iSH ;
    end if ;
  end process ;
  iSH_TRG <= '1' when ( iSH_SFT = "10" ) else '0' ;

  -- get datum
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iTMP <= '0' ;
    elsif rising_edge( CLOCK ) then
      if ( iCLKAB = '1' ) then
        iTMP <= iDOS ; -- latch 1 bit
      end if ;
    end if ;
  end process ;

  -- state machine
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= 0 ;
      iREGX  <= X"00" ;
      iREG   <= X"00" ;
      iSCNT  <= 0 ;
    elsif rising_edge( CLOCK ) then
      case iSTATE is
        -- wait trigger 
        when 0 => if ( iSH_TRG = '1' ) then
                    iSTATE <= 1 ; -- next
                    iREGX  <= X"00" ; -- clear register
                    iSCNT  <= 0     ; -- clear counter
                  else
                    iSTATE <= 0 ; -- stay
                  end if ;
        -- get datum
        when 1 => if ( iCLKAB = '1' ) then
                    iSTATE <= 2 ; -- next
                  else
                    iSTATE <= 1 ; -- stay
                  end if ;
        -- data counter increment
        when 2 => iSTATE <= 3   ; -- next
                  iSCNT  <= iSCNT + 1 ; -- counter increment
        -- store datum to temporary register
        when 3 => iSTATE <= 4 ; -- next
                  -- store
                  if ( iSCNT = S0 or
                       iSCNT = S1 or
                       iSCNT = S2 or
                       iSCNT = S3 or
                       iSCNT = S4 or
                       iSCNT = S5 or
                       iSCNT = S6 or
                       iSCNT = S7    ) then
                    iREGX <= iREGX(6 downto 0) & iTMP ;
                  end if ;
        -- judge (? complete)
        when 4 => if ( iSCNT = SLAST ) then
                    iSTATE <= 6 ; -- next(return first state)
                  else
                    iSTATE <= 5 ; -- next(loop control)
                  end if ;
        -- loop control
        when 5 => iSTATE <= 1 ;
        -- return first state
        when 6 => iSTATE <= 0 ;
                  iREG   <= iREGX ; -- copy
        -- default
        when others =>
                  iSTATE <= 0 ;
		end case ;
    end if ;
  end process ;

end behavioral;

 利用マクロセル数は、53になりました。

 BCSはセンサーデータの更新を20msに一度とするので
 センサーデータ8ビットを2回に分けてArduinoで
 取得しても問題ありません。

 ラインデータを、どこでサンプリングして、8ビットに
 仕立てるかを計算するのは、AWKスクリプトを使います。

 データは、テキストで次のように書いておきます。

32 1024 64 -4
32 1024 64 -3
32 1024 64 -2
32 1024 64 -1
32 1024 64 1
32 1024 64 2
32 1024 64 3
32 1024 64 4

 AWKスクリプトは、以下。

{
  result = $1 + $2 + ($3 * $4)
  printf("%d => %s\n",result,$0)
}

 テキストの内容を変えると、サンプリング間隔を
 狭めたり、広げたりするのは自由自在です。
 また、等間隔にサンプリングしないことも可能。


目次

inserted by FC2 system