目次

センサー制御再考

 路面センサーには、Game Boy Camera、Bar Code Scannerを
 利用してきましたが、太陽光がどのくらいの強度であれば
 ハレーションが起きないのかの解析は中途半端でした。

 太陽光によるハレーションを低減するために、何をすれば
 よいのかを、2つのセンサーの画像データを取得して再度
 解析します。

 Bar Code Scanner(BCS)のデータは、3000ビット程度の
 バイナリーコードなので、手持ちの2kバイトメモリを
 利用し、画像取得と表示ができる回路を設計しました。



 画像取得には、シフトレジスタを利用しています。

 CLKABに同期してデータが出力されてくるので、シフト
 レジスタにデータが8ビット蓄えられると、レジスタに
 転送します。
 8ビットがレジスタに転送されたなら、シーケンサで
 メモリにデータを転送し、アドレスを+1。

 SHが'H'のとき、シフトレジスタ、カウンタ、アドレス
 カウンタをリセットします。

 データをメモリにライトするのか、メモリからデータを
 リードするのかを、トグルスイッチで切替えらます。

 メモリからデータをリードするには、マイコンを利用。

 マイコンは、PC上の端末ソフトからの指示でメモリの
 データを取得し、8ビットごとにPCへ転送します。

 マイコンは、ATtiny2313を利用することに。

 回路図をそのまま半田付けすると、ワイヤーが蕎麦か
 スパゲティ状態になるので、CPLDの中に詰めるだけ
 詰めてみます。

 ブロック図を作成し、CPLDに入れる回路を考えます。



 各ブロックをVHDLコードに置換。

 BUFFER DRIVER
  BCSが出力する信号を受け、内部で扱いやすくします。

  -- input
  iSH     <= SH   ;
  iCLKAB  <= CLKA or CLKB ;
  iDOS    <= DOS  ;

  BCSの信号を同期化して、CPLD内部に入力。

  -- synchronizer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSFT_CLKAB  <= "000" ;
      iSFT_SH     <= "000" ;
    elsif rising_edge(CLOCK) then
      iSFT_CLKAB  <= iSFT_CLKAB(1 downto 0) & iCLKAB ;
      iSFT_SH     <= iSFT_SH(1 downto 0) & iSH ;
    end if ;
  end process ;
  iCLK    <= '1' when ( iSFT_CLKAB = "011" ) else '0' ;
  iSHFTRG <= '1' when ( iSFT_SH    = "100" ) else '0' ;

  iCLKは、シフトレジスタにDOSの値を格納するために利用。
  iSHFTRGは、レジスタ、カウンタのリセット、シーケンサ
  のトリガーに使います。

 SHIFT REGISTER
  シフトレジスタは、iCLKを利用しDOSの値を格納。

  process (nRESET,iCLK)
  begin
    if ( nRESET = '0' ) then
      iDREG <= (others => '0') ;
    elsif rising_edge(iCLK) then
      iDREG <= iDREG(6 downto 0) & iDOS ;
    end if ;
  end process ;

 PIXEL COUNTER
  DOSの値を、いくつ入力したのかをカウントします。
  PIXEL COUNTERなのでiCLKでカウントアップ。
  カウンタのリセットは、SHを利用します。

  process (nRESET,iCLK)
  begin
    if ( nRESET = '0' ) then
      iCNT  <= (others => '0') ;
    elsif rising_edge(iCLK) then
      if ( iSH = '1' ) then
        iCNT <= (others => '0') ;
      elsif ( conv_integer(iCNT) = CNTMAX ) then
        iCNT <= (others => '0') ;
      else
        iCNT <= iCNT + '1' ;
      end if ;
    end if ;
  end process ;

  SRAMにデータを転送するため、アドレスカウンタを
  操作する必要があり、このカウンタのリセットには
  SHを利用します。

  iARSTX <= '1' when ( iSH = '1' ) else '0' ;

 ADDRESS COUNTER
  アドレス用カウンタは、リセットとインクリメントの
  どちらかで制御します。リセットとインクメントには
  BCSとマイコンの2種、4信号となります。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iACNT <= (others => '0') ;
    elsif rising_edge(CLOCK) then
      if ( iARST = '1' and iWENABLE = '0' ) then
        iACNT <= (others => '0') ;
      elsif ( iARSTX = '1' and iWENABLE = '1' ) then
        iACNT <= (others => '0') ;
      elsif ( iAINC = '1' and iWENABLE = '0' ) then
        iACNT <= iACNT + '1' ;
      elsif ( iAINCX = '1' and iWENABLE = '1' ) then
        iACNT <= iACNT + '1' ;
      end if ;
    end if ;
  end process ;

  アドレスインクリメントは、SRAMのライトとリードが
  あるので、切り替えて対応しています。

  マイコンの指示でアドレスリセット、インクリメントが
  必要なので、同期化します。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSFT_WTRG   <= "000" ;
      iADRRST_SFT <= "00"  ;
      iADRINC_SFT <= "00"  ;
    elsif rising_edge(CLOCK) then
      iSFT_WTRG   <= iSFT_WTRG(1 downto 0) & WTRG ;
      iADRRST_SFT <= iADRRST_SFT(0) & iADRRST ;
      iADRINC_SFT <= iADRINC_SFT(0) & iADRINC ;
    end if ;
  end process ;
  iWTRG <= '1' when ( iSFT_WTRG  = "011" ) else '0' ;
  iARST <= '1' when ( iADRRST_SFT = "01" ) else '0' ;
  iAINC <= '1' when ( iADRINC_SFT = "01" ) else '0' ;

 SEQUENCER
  シーケンサは、2つ使います。

  SRAMに対してライト処理が必要なので、シフトレジスタに
  8ビット分のデータが格納されたなら、8ビットレジスタ
  に転送し、SRAMにデータをライトします。その後アドレス
  をインクリメントします。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iWSTATE <= "00" ;
      iREG    <= (others => '0') ;
    elsif rising_edge(CLOCK) then
      case conv_integer( iWSTATE ) is
        -- wait write trigger
        when 0 => if ( iCNT(2 downto 0) = "111" and iWENABLE = '1' ) then
                    iWSTATE <= "01" ;
                    iREG    <= iDREG ;
                  else
                    iWSTATE <= "00" ;
                  end if ;
        -- enable nWE pusle  
        when 1 => iWSTATE <= "11" ;
        -- enable address increment
        when 3 => iWSTATE <= "10" ;
        -- return first state
        when 2 => iWSTATE <= "00" ;
        -- default
        when others =>
                  iWSTATE <= "00" ;
      end case ;
    end if ;
  end process ;
  iAINCX <= '1' when ( iWSTATE = "10" ) else '0' ;

  SRAMにデータを格納するには、ライトパルスが必要なので
  ワンショットのパルスを生成するシーケンサを利用。

  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= "00" ;
    elsif rising_edge(CLOCK) then
      case conv_integer( iSTATE ) is
        -- wait write trigger
        when 0 => if ( iWTRG = '1' ) then
                    iSTATE <= "01" ;
                  else
                    iSTATE <= "00" ;
                  end if ;
        -- wait SH falling edge
        when 1 => if ( iSHFTRG = '1' ) then
                    iSTATE <= "11" ;
                  else
                    iSTATE <= "01" ;
                  end if ;
        -- wait count over
        when 3 => if ( iCNT = CNTLAST ) then
                    iSTATE <= "10" ;
                  else
                    iSTATE <= "11" ;
                  end if ;
        -- return first state
        when 2 => iSTATE <= "00" ;
        -- default
        when others =>
                  iSTATE <= "00" ;
      end case ;
    end if ;
  end process ;
  iWENABLE <= '1' when ( iSTATE = "11" ) else '0' ;

  2つのシーケンサは、マスタークロックで動かします。
  BCSに関係するブロックは200kHzで動作するので、この
  周波数より10倍程度高速なクロックにします。

  シーケンサはハザードを出さないように、2ビットの
  ジョンソンカウンタとしました。

 BUFFER
  出力にバッファを用意しますが、SRAMのデータバスは
  ハイインピーダンスとする場面があります。
  SRAMからリード可能を外部に示すためXWORKを使います。

  ACNT  <= iACNT ;
  Dout  <= iREG when ( iWENABLE = '1' ) else (others => 'Z');
  nWE   <= '0' when ( iWSTATE = "01" ) else '1' ;
  XWORK <= iWENABLE ; 

 ここまでの内容をまとめると、以下。

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

entity bcstst is
  port (
    -- system
    nRESET : in  std_logic ;
    CLOCK  : in  std_logic ; -- 4MHz
    -- Bar Code Scanner signals
    SH     : in  std_logic ;
    CLKA   : in  std_logic ;
    CLKB   : in  std_logic ;
    DOS    : in  std_logic ;
    -- Trigger
    WTRG   : in  std_logic ; -- write trigger
    ADRRST : in  std_logic ; -- address reset
    ADRINC : in  std_logic ; -- address increment
    XWORK  : out std_logic ;
    --
    nWE    : out std_logic ;
    Dout   : out std_logic_vector(7 downto 0) ;
    ACNT   : out std_logic_vector(8 downto 0) --;
  );
end bcstst ;

architecture behavioral of bcstst is
  --
  CONSTANT CNTMAX  : integer := 3072 ;
  CONSTANT CNTLAST : integer := 2080 ;
  -- Bar Code Scanner signals
  signal iSH     : std_logic ;
  signal iCLKAB  : std_logic ;
  signal iDOS    : std_logic ;
  -- synchronizer
  signal iSFT_CLKAB  : std_logic_vector(2 downto 0) ;
  signal iCLK        : std_logic ;
  signal iSFT_WTRG   : std_logic_vector(2 downto 0) ;
  signal iWTRG       : std_logic ;
  signal iSFT_SH     : std_logic_vector(2 downto 0) ;
  signal iSHFTRG     : std_logic ;
  signal iADRRST     : std_logic ; -- address reset
  signal iADRINC     : std_logic ; -- address increment
  signal iADRRST_SFT : std_logic_vector(1 downto 0) ;
  signal iADRINC_SFT : std_logic_vector(1 downto 0) ;
  signal iARST       : std_logic ;
  signal iAINC       : std_logic ;
  -- internal counter
  signal iCNT   : std_logic_vector(11 downto 0) ;
  signal iACNT  : std_logic_vector( 8 downto 0) ;
  signal iARSTX : std_logic ;
  signal iAINCX : std_logic ;
  signal iDREG  : std_logic_vector( 7 downto 0) ;
  signal iREG   : std_logic_vector( 7 downto 0) ;
  -- state machine
  signal iSTATE   : std_logic_vector(1 downto 0) ;
  signal iWENABLE : std_logic ;
  signal iWSTATE  : std_logic_vector(1 downto 0) ;
begin
  -- input
  iSH     <= SH   ;
  iCLKAB  <= CLKA or CLKB ;
  iDOS    <= DOS  ;
  iADRRST <= ADRRST ;
  iADRINC <= ADRINC ;

  -- output
  ACNT  <= iACNT ;
  Dout  <= iREG when ( iWENABLE = '1' ) else (others => 'Z');
  nWE   <= '0' when ( iWSTATE = "01" ) else '1' ;
  XWORK <= iWENABLE ; 

  -- synchronizer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSFT_CLKAB  <= "000" ;
      iSFT_WTRG   <= "000" ;
      iSFT_SH     <= "000" ;
      iADRRST_SFT <= "00"  ;
      iADRINC_SFT <= "00"  ;
    elsif rising_edge(CLOCK) then
      iSFT_CLKAB  <= iSFT_CLKAB(1 downto 0) & iCLKAB ;
      iSFT_WTRG   <= iSFT_WTRG(1 downto 0) & WTRG ;
      iSFT_SH     <= iSFT_SH(1 downto 0) & iSH ;
      iADRRST_SFT <= iADRRST_SFT(0) & iADRRST ;
      iADRINC_SFT <= iADRINC_SFT(0) & iADRINC ;
    end if ;
  end process ;
  iCLK    <= '1' when ( iSFT_CLKAB = "011" ) else '0' ;
  iWTRG   <= '1' when ( iSFT_WTRG  = "011" ) else '0' ;
  iSHFTRG <= '1' when ( iSFT_SH    = "100" ) else '0' ;
  iARST   <= '1' when ( iADRRST_SFT = "01" ) else '0' ;
  iAINC   <= '1' when ( iADRINC_SFT = "01" ) else '0' ;

  -- internal counter
  process (nRESET,iCLK)
  begin
    if ( nRESET = '0' ) then
      iCNT  <= (others => '0') ;
      iDREG <= (others => '0') ;
    elsif rising_edge(iCLK) then
      iDREG <= iDREG(6 downto 0) & iDOS ;
      if ( iSH = '1' ) then
        iCNT <= (others => '0') ;
      elsif ( conv_integer(iCNT) = CNTMAX ) then
        iCNT <= (others => '0') ;
      else
        iCNT <= iCNT + '1' ;
      end if ;
    end if ;
  end process ;
  iARSTX <= '1' when ( iSH = '1' ) else '0' ;

  -- address counter
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iACNT <= (others => '0') ;
    elsif rising_edge(CLOCK) then
      if ( iARST = '1' and iWENABLE = '0' ) then
        iACNT <= (others => '0') ;
      elsif ( iARSTX = '1' and iWENABLE = '1' ) then
        iACNT <= (others => '0') ;
      elsif ( iAINC = '1' and iWENABLE = '0' ) then
        iACNT <= iACNT + '1' ;
      elsif ( iAINCX = '1' and iWENABLE = '1' ) then
        iACNT <= iACNT + '1' ;
      end if ;
    end if ;
  end process ;

  -- one shot
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iSTATE <= "00" ;
    elsif rising_edge(CLOCK) then
      case conv_integer( iSTATE ) is
        -- wait write trigger
        when 0 => if ( iWTRG = '1' ) then
                    iSTATE <= "01" ;
                  else
                    iSTATE <= "00" ;
                  end if ;
        -- wait SH falling edge
        when 1 => if ( iSHFTRG = '1' ) then
                    iSTATE <= "11" ;
                  else
                    iSTATE <= "01" ;
                  end if ;
        -- wait count over
        when 3 => if ( iCNT = CNTLAST ) then
                    iSTATE <= "10" ;
                  else
                    iSTATE <= "11" ;
                  end if ;
        -- return first state
        when 2 => iSTATE <= "00" ;
        -- default
        when others =>
                  iSTATE <= "00" ;
      end case ;
    end if ;
  end process ;
  iWENABLE <= '1' when ( iSTATE = "11" ) else '0' ;

  -- write sequencer
  process (nRESET,CLOCK)
  begin
    if ( nRESET = '0' ) then
      iWSTATE <= "00" ;
      iREG    <= (others => '0') ;
    elsif rising_edge(CLOCK) then
      case conv_integer( iWSTATE ) is
        -- wait write trigger
        when 0 => if ( iCNT(2 downto 0) = "111" and iWENABLE = '1' ) then
                    iWSTATE <= "01" ;
                    iREG    <= iDREG ;
                  else
                    iWSTATE <= "00" ;
                  end if ;
        -- enable nWE pusle  
        when 1 => iWSTATE <= "11" ;
        -- enable address increment
        when 3 => iWSTATE <= "10" ;
        -- return first state
        when 2 => iWSTATE <= "00" ;
        -- default
        when others =>
                  iWSTATE <= "00" ;
      end case ;
    end if ;
  end process ;
  iAINCX <= '1' when ( iWSTATE = "10" ) else '0' ;

end behavioral;

 XilinxのXC9572を使うとして、ピンアサインは以下。

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

# BCS
NET "CLKA"   LOC = "P1" ;
NET "SH"     LOC = "P2" ;
NET "DOS"    LOC = "P3" ;
NET "CLKB"   LOC = "P4" ;

# pulse out
NET "WTRG"   LOC = "P6" ;
NET "ADRRST" LOC = "P7" ;
NET "ADRINC" LOC = "P8" ;
NET "XWORK"  LOC = "P9" ;

# BUS address
NET "ACNT<0>" LOC = "P11" ;
NET "ACNT<1>" LOC = "P12" ;
NET "ACNT<2>" LOC = "P13" ;
NET "ACNT<3>" LOC = "P14" ;
NET "ACNT<4>" LOC = "P18" ;
NET "ACNT<5>" LOC = "P19" ;
NET "ACNT<6>" LOC = "P20" ;
NET "ACNT<7>" LOC = "P22" ;
NET "ACNT<8>" LOC = "P24" ;
NET "nWE"     LOC = "P25" ;

# BUS data
NET "Dout<7>" LOC = "P44" ;
NET "Dout<6>" LOC = "P43" ;
NET "Dout<5>" LOC = "P42" ;
NET "Dout<4>" LOC = "P40" ;
NET "Dout<3>" LOC = "P38" ;
NET "Dout<2>" LOC = "P37" ;
NET "Dout<1>" LOC = "P36" ;
NET "Dout<0>" LOC = "P35" ;

 ここまでVHDLコードを作成し、もう一度BCSからの
 データをSRAMに入力することを見直しました。

 BCSのデータは、マイコンからの指示でワンショット
 ライトで、SRAMに転送します。

 SRAMがライト処理に応じるのは、nCSがイネーブルに
 なっているときなので、次のようなシーケンスで処理
 します。
  1. マイコンからのトリガー待ち、トリガーが着たら2へ
  2. BCSのSHのfalling edge待ち、エッジが着たら3へ
  3. BCSのSHのrising edge待ち、エッジが着たら4へ
  4. 1へ戻る
 BCSのSHで、SRAMのnCSを制御します。  タイミングチャートで見ると、以下となります。  SRAMへのデータ転送を終了するのは、512バイト分を  ライト終了時でもよいので、シーケンサの定義は以下。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge(CLOCK) then case conv_integer( iSTATE ) is -- wait write trigger when 0 => if ( iTRG = '1' ) then iSTATE <= "01" ; else iSTATE <= "00" ; end if ; -- wait SH falling edge when 1 => if ( iSHFTRG = '1' ) then iSTATE <= "11" ; else iSTATE <= "01" ; end if ; -- wait SH rising edge when 3 => if ( iSHRTRG = '1' or conv_integer(iACNT) = ADRMAX ) then iSTATE <= "10" ; else iSTATE <= "11" ; end if ; -- return first state when 2 => iSTATE <= "00" ; -- default when others => iSTATE <= "00" ; end case ; end if ; end process ; iCS <= '1' when ( iSTATE = "11" ) else '0' ;  カウンタ値とSHのrising edgeのチェックで、データ入力  後確実に動作を終了できます。  VHDLコードにまとめると以下。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity bcstst is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- 1MHz -- Bar Code Scanner signals SH : in std_logic ; CLKA : in std_logic ; CLKB : in std_logic ; DOS : in std_logic ; -- Trigger TRG : in std_logic ; -- write trigger ADRRST : in std_logic ; -- address reset ADRINC : in std_logic ; -- address increment XCS : in std_logic ; -- chip select control -- nWE : out std_logic ; nCS : out std_logic ; Dout : out std_logic_vector(7 downto 0) ; ACNT : out std_logic_vector(8 downto 0) --; ); end bcstst ; architecture behavioral of bcstst is -- CONSTANT CNTMAX : integer := 3072 ; CONSTANT ADRMAX : integer := 384 ; -- BCS synchronizer signal iCLKA_SFT : std_logic_vector(2 downto 0) ; signal iCLKB_SFT : std_logic_vector(2 downto 0) ; signal iCLKA_TRG : std_logic ; signal iCLKB_TRG : std_logic ; -- internal latch counter signal iCNTA : integer range 0 to 10 ; signal iCF : std_logic ; signal iSFT_WTRG : std_logic_vector(2 downto 0) ; signal iTRG : std_logic ; signal iSFT_SH : std_logic_vector(2 downto 0) ; signal iSHFTRG : std_logic ; -- Memory synchronizer signal iADRRST_SFT : std_logic_vector(1 downto 0) ; signal iADRINC_SFT : std_logic_vector(1 downto 0) ; signal iXCS : std_logic ; signal iARST : std_logic ; signal iAINC : std_logic ; -- internal counter signal iCNT : std_logic_vector(11 downto 0) ; signal iSTORE_ENA : std_logic ; signal iACNT : integer range 0 to 511 ; signal iAINCX : std_logic ; signal iDREG : std_logic_vector( 7 downto 0) ; signal iREG : std_logic_vector( 7 downto 0) ; -- state machine signal iSTATE : std_logic_vector(1 downto 0) ; signal iCS : std_logic ; signal iWSTA : std_logic_vector(1 downto 0) ; begin -- input iXCS <= XCS ; -- output ACNT <= conv_std_logic_vector(iACNT,9) ; Dout <= iREG when ( iCS = '1' ) else (others => 'Z'); nWE <= '0' when ( iWSTA = "11" ) else '1' ; nCS <= not (iCS or XCS); -- synchronizer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCLKA_SFT <= "000" ; iCLKB_SFT <= "000" ; iSFT_WTRG <= "000" ; iSFT_SH <= "000" ; iADRRST_SFT <= "00" ; iADRINC_SFT <= "00" ; elsif rising_edge(CLOCK) then iCLKA_SFT <= iCLKA_SFT(1 downto 0) & CLKA ; iCLKB_SFT <= iCLKB_SFT(1 downto 0) & CLKB ; iSFT_SH <= iSFT_SH(1 downto 0) & SH ; iSFT_WTRG <= iSFT_WTRG(1 downto 0) & TRG ; iADRRST_SFT <= iADRRST_SFT(0) & ADRRST ; iADRINC_SFT <= iADRINC_SFT(0) & ADRINC ; end if ; end process ; iCLKA_TRG <= '1' when ( iCLKA_SFT = "011" ) else '0' ; iCLKB_TRG <= '1' when ( iCLKB_SFT = "011" ) else '0' ; iSHFTRG <= '1' when ( iSFT_SH = "100" ) else '0' ; iTRG <= '1' when ( iSFT_WTRG = "011" ) else '0' ; iARST <= '1' when ( iADRRST_SFT = "01" ) else '0' ; iAINC <= '1' when ( iADRINC_SFT = "01" ) else '0' ; -- internal counter to store sensor data process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNT <= X"000" ; elsif rising_edge(CLOCK) then if ( SH = '1' ) then iCNT <= X"000" ; elsif ( iCNT = CNTMAX ) then iCNT <= X"000" ; elsif ( iCLKA_TRG = '1' ) then iCNT <= iCNT + 1 ; elsif ( iCLKB_TRG = '1' ) then iCNT <= iCNT + 1 ; end if ; end if ; end process ; iSTORE_ENA <= '1' when ( iCNT(3 downto 0) = "1000" ) else '0' ; -- generate latch trigger process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNTA <= 0 ; elsif rising_edge(CLOCK) then if ( iCLKA_TRG = '1' or iCLKB_TRG = '1' ) then iCNTA <= 0 ; else iCNTA <= iCNTA + 1 ; end if ; end if ; end process ; iCF <= '1' when ( iCNTA > 4 ) else '0' ; -- data latch process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDREG <= X"00" ; elsif rising_edge(CLOCK) then if ( iCNTA = 3 ) then iDREG <= iDREG(6 downto 0) & DOS ; end if ; end if ; end process ; -- address counter process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iACNT <= 0 ; elsif rising_edge(CLOCK) then -- micro computer control if ( iXCS = '1' ) then -- reset if ( iARST = '1' ) then iACNT <= 0 ; end if ; -- increment if ( iAINC = '1' ) then iACNT <= iACNT + 1 ; end if ; end if ; -- internal control if ( iSTATE(0) = '1' ) then -- reset if ( SH = '1' ) then iACNT <= 0 ; end if ; -- increment if ( iAINCX = '1' ) then iACNT <= iACNT + 1 ; end if ; end if ; end if ; end process ; -- one shot get bar code data process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSTATE <= "00" ; elsif rising_edge(CLOCK) then case conv_integer( iSTATE ) is -- wait write trigger when 0 => if ( iTRG = '1' ) then iSTATE <= "01" ; else iSTATE <= "00" ; end if ; -- wait SH falling edge when 1 => if ( iSHFTRG = '1' ) then iSTATE <= "11" ; else iSTATE <= "01" ; end if ; -- wait complete when 3 => if ( iACNT = ADRMAX ) then iSTATE <= "10" ; else iSTATE <= "11" ; end if ; -- return first state when 2 => iSTATE <= "00" ; -- default when others => iSTATE <= "00" ; end case ; end if ; end process ; iCS <= '1' when ( iSTATE = "11" ) else '0' ; -- write sequencer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iWSTA <= "00" ; iREG <= X"00" ; elsif rising_edge(CLOCK) then case conv_integer( iWSTA ) is -- judge mode when 0 => if ( iCS = '1' ) then iWSTA <= "01" ; else iWSTA <= "00" ; end if ; -- copy data when 1 => if ( iSTORE_ENA = '1' and iCF = '1' ) then iWSTA <= "11" ; iREG <= iDREG ; else iWSTA <= "01" ; end if ; -- enable nWE pulse when 3 => iWSTA <= "10" ; -- enable address increment and return first state when 2 => iWSTA <= "00" ; -- default when others => iWSTA <= "00" ; end case ; end if ; end process ; iAINCX <= '1' when ( iWSTA = "10" ) else '0' ; end behavioral;  利用ピンが増えたのと、一部信号名を変更した  ので、UCFファイルは次のように変更します。 # system NET "CLOCK" LOC = "P5" ; NET "nRESET" LOC = "P39" ; # BCS NET "SH" LOC = "P1" ; NET "CLKA" LOC = "P2" ; NET "CLKB" LOC = "P3" ; NET "DOS" LOC = "P4" ; # pulse out NET "TRG" LOC = "P6" ; NET "ADRRST" LOC = "P7" ; NET "ADRINC" LOC = "P8" ; NET "XCS" LOC = "P9" ; # BUS address NET "ACNT<0>" LOC = "P11" ; NET "ACNT<1>" LOC = "P12" ; NET "ACNT<2>" LOC = "P13" ; NET "ACNT<3>" LOC = "P14" ; NET "ACNT<4>" LOC = "P18" ; NET "ACNT<5>" LOC = "P19" ; NET "ACNT<6>" LOC = "P20" ; NET "ACNT<7>" LOC = "P22" ; NET "ACNT<8>" LOC = "P24" ; NET "nWE" LOC = "P25" ; NET "nCS" LOC = "P26" ; # BUS data NET "Dout<7>" LOC = "P44" ; NET "Dout<6>" LOC = "P43" ; NET "Dout<5>" LOC = "P42" ; NET "Dout<4>" LOC = "P40" ; NET "Dout<3>" LOC = "P38" ; NET "Dout<2>" LOC = "P37" ; NET "Dout<1>" LOC = "P36" ; NET "Dout<0>" LOC = "P35" ;  SRAMに保存したデータを、PersonalComputerで閲覧できる  ようにします。  BCSのデータをSRAMへ保存するのは、CPLDのシーケンサに任せますが  SRAMに格納されたデータを取出して表示するには、マイコンを利用  します。  PersonalComputer、マイコン(ATtiny2313)、CPLDの接続は、以下。  回路は、以下のように半田付けしました。  (LEDは、信号モニタに使います。)  マイコンは、CPLDに指示を与え、SRAMにBCSからの情報を格納。  また、SRAMからBCSの情報を引き出します。  マイコン(ATtiny2313)のポートは、次のように利用します。  ポートB 8ビットバス  ポートD CPLD、SRAMの制御 PD6 XCS PD5 AINC PD4 ARST PD3 nOE PD2 TRG  PersonalComputerとマイコンで通信するためのプロトコルを決めます。  データ転送速度 9600bps  同期方式    調歩同期  データ長    8ビット  ストップビット 1ビット  パリティ    なし  フロー制御   なし  コマンドは、以下の3種としました。  ソースコードは、以下。 #include <avr/io.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #include <avr/sfr_defs.h> typedef unsigned char UBYTE ; typedef unsigned short UWORD ; #define OFF 0 #define ON OFF+1 #define XCS 6 #define AINC 5 #define ARST 4 #define nOE 3 #define TRG 2 volatile UBYTE uflag ; volatile UBYTE sbuf[8] ; volatile UBYTE sindex ; volatile UBYTE cmd ; #define NO 0 #define YES NO+1 #define MASK01 0x01 #define MASK03 0x03 #define MASKFF 0xff #define FOSC 4000000 #define BAUD 9600 #define MYUBRR (FOSC/16/BAUD)-1 #define LAST 512 const prog_char msg_h[] PROGMEM = "? help" ; const prog_char msg_g[] PROGMEM = "G get code" ; const prog_char msg_s[] PROGMEM = "S show memory" ; const prog_char msg_t[] PROGMEM = "T test I/O" ; const prog_char msg_r[] PROGMEM = "R rst adr" ; const prog_char msg_i[] PROGMEM = "I inc adr" ; /*--------------------------------*/ /* Insert user functions protoype */ /*--------------------------------*/ void user_initialize(void); void rs_putchar(UBYTE x); void rs_puts(UBYTE *x); void crlf(void); UBYTE get_hex(UBYTE x); void show_help(void); void set_cs(UBYTE x); void inc_adr(void); void rst_adr(void); void set_oe(UBYTE x); void put_trg(void); void binary_display(UBYTE x); void sdelay(void); /*------*/ /* main */ /*------*/ int main(void) { UWORD loop ; UBYTE tmp ; UBYTE xch ; /* initialize port and variables */ user_initialize(); /* enable interrupt */ sei(); /* opening message */ rs_puts((UBYTE *)"Hello !"); crlf(); /* endless loop */ while ( ON ) { /* command interpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* new line */ crlf() ; /* get command */ cmd = *(sbuf+0) ; /* help */ if ( cmd == '?' ) { show_help(); } /* get information */ if ( cmd == 'G' ) { put_trg() ; } /* show memory context */ if ( cmd == 'S' ) { /* enable memory CS */ set_cs(ON); /* enable memory nOE */ set_oe(ON); /* reset address */ rst_adr(); /* loop */ for ( loop = 0 ; loop < LAST ; loop++ ) { /* delay */ sdelay(); /* get data from PORTB */ tmp = PINB ; /* show */ binary_display( tmp ) ; /* address increment */ inc_adr(); /* new line */ if ( (loop % 8) == 7 ) { crlf() ; } } /* disable memory nOE */ set_oe(OFF); /* disable memory CS */ set_cs(OFF); } /* reset address */ if ( cmd == 'R' ) { rst_adr(); } /* address increment */ if ( cmd == 'I' ) { inc_adr();} /* test I/O */ if ( cmd == 'T' ) { /* get logical vaule */ tmp = get_hex( *(sbuf+1) ); /* get channel */ xch = *(sbuf+2) ; /* XCS control */ if ( xch == 'C' ) { PORTD &= ~(1 << XCS) ; if ( tmp ) { PORTD |= (1 << XCS) ; } } /* address increment control */ if ( xch == 'I' ) { PORTD &= ~(1 << AINC) ; if ( tmp ) { PORTD |= (1 << AINC) ; } } /* address reset control */ if ( xch == 'R' ) { PORTD &= ~(1 << ARST) ; if ( tmp ) { PORTD |= (1 << ARST) ; } } /* OE control */ if ( xch == 'O' ) { PORTD &= ~(1 << nOE) ; if ( tmp ) { PORTD |= (1 << nOE) ; } } /* TRIGGER control */ if ( xch == 'T' ) { PORTD &= ~(1 << TRG) ; if ( tmp ) { PORTD |= (1 << TRG) ; } } } } } /* dummy */ return 0 ; } /*-----------------------*/ /* Insert user functions */ /*-----------------------*/ void user_initialize(void) { /* PORT B */ PORTB = 0b11111111 ; /* 11111111 */ DDRB = 0b00000000 ; /* iiiiiiii */ /* PORT D */ PORTD = 0b00001001 ; /* 00000001 */ DDRD = 0b11111110 ; /* oooooooi */ /* clear flags */ uflag = OFF ; /* clear index */ sindex = 0 ; /* initialize UART */ { /* set Baud Rate Registers */ UBRRL = MYUBRR ; /* Enable receive interrupt , receive module and transmit module */ UCSRB = (1 << RXCIE) | (1 << RXEN) | (1 << TXEN) ; } } void rs_putchar(UBYTE x) { while ( !(UCSRA & (1 << UDRE)) ) {} UDR = x ; } void rs_puts(UBYTE *x) { while ( *x != '\0' ) { rs_putchar( *x ) ; x++ ; } } void crlf(void) { rs_putchar('\r'); rs_putchar('\n'); } UBYTE get_hex(UBYTE x) { UBYTE result ; /* default */ result = 0 ; /* conversion */ 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 show_help(void) { char msg[16]; strcpy_P(msg,msg_h); rs_puts((UBYTE *)msg); crlf(); strcpy_P(msg,msg_g); rs_puts((UBYTE *)msg); crlf(); strcpy_P(msg,msg_s); rs_puts((UBYTE *)msg); crlf(); strcpy_P(msg,msg_t); rs_puts((UBYTE *)msg); crlf(); strcpy_P(msg,msg_r); rs_puts((UBYTE *)msg); crlf(); strcpy_P(msg,msg_i); rs_puts((UBYTE *)msg); crlf(); } /* UART receive interrupt */ ISR(USART_RX_vect) { volatile UBYTE ch ; /* get 1 charactoer */ ch = UDR ; /* store */ *(sbuf+sindex) = ch ; /* increment */ sindex++ ; /* judge */ if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } void set_cs(UBYTE x) { if ( x ) { PORTD |= (1 << XCS) ; } else { PORTD &= ~(1 << XCS) ; } } void inc_adr(void) { /* set */ PORTD |= (1 << AINC) ; /* clear */ PORTD &= ~(1 << AINC) ; } void rst_adr(void) { /* set */ PORTD |= (1 << ARST) ; /* clear */ PORTD &= ~(1 << ARST) ; } void set_oe(UBYTE x) { if ( x ) { PORTD &= ~(1 << nOE) ; } else { PORTD |= (1 << nOE) ; } } void put_trg(void) { /* set */ PORTD |= (1 << TRG) ; /* clear */ PORTD &= ~(1 << TRG) ; } void binary_display(UBYTE x) { int i ; UBYTE xtmp ; for ( i = 7 ; i > -1 ; i-- ) { xtmp = (x >> i) & 0x01 ; xtmp += '0' ; rs_putchar(xtmp); } } void sdelay(void) { UBYTE loop ; for ( loop = 0 ; loop < 32 ; loop++ ) { } }  TeraTermで動作を確認します。  最初に、CPLDとSRAMに関する信号ピンの電圧出力を  チェックしました。  コマンド'T'に続けて、'0'か'1'で出力論理値を指定し  続けて、'C'、'I'、'R'、'O'、'T'のどれかを入力します。  各ピンの割当ては、以下としました。  'C' チップセレクト  'I' アドレスインクリメント  'R' アドレスリセット  'O' アウトプットイネーブル  'T' トリガー  BCSの動作をエミュレーションするためArduinoを  利用した簡単なスケッチを作成して利用しました。 #include <MsTimer2.h> #define OFF 0 #define ON OFF+1 #define SH_VALUE 1 #define CLKA_VALUE 2 #define CLKB_VALUE 4 #define DOS_VALUE 8 byte uflag ; byte eflag ; byte tmp ; word xdos ; word ydos ; byte xcnt ; word scnt ; char sbuf[8]; byte sindex ; char cmd ; 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("E set code") ; crlf(); rs_puts("e show code") ; 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 ; } void binary_display(word x) { int i ; for ( i = 15 ; i > -1 ; i-- ) { rs_putchar( '0' + ((x >> i) & 1) ); } } /* initialilze */ void setup() { /* initialize serial port */ Serial.begin(9600); sindex = 0 ; /* pin configuration */ PORTC = 0x00 ; DDRC = 0xff ; PORTD = 0x01 ; DDRD = 0xfe ; /* clear flags */ uflag = OFF ; eflag = OFF ; /* others */ scnt = 0 ; xcnt = 0 ; xdos = 0x00ff ; ydos = 0x00ff ; /* 2ms period */ MsTimer2::set(2,update_trigger); /* enable */ MsTimer2::start(); } /* endless loop */ void loop() { /* update */ if ( eflag == ON ) { /* clear flag */ tmp = 0 ; /* SH */ if ( scnt < 5 ) { tmp |= SH_VALUE ; } /* CLKA */ if ( scnt & 1 ) { tmp |= CLKA_VALUE ; } /* CLKB */ if ( !(scnt & 1) ) { tmp |= CLKB_VALUE ; } /* DOS */ if ( scnt > 7 ) { if ( xdos & 0x8000 ) { tmp |= DOS_VALUE ; } } /* impress */ PORTC = tmp ; /* shift */ xdos <<= 1 ; xcnt++ ; if ( xcnt == 16 ) { xcnt = 0 ; xdos = ydos ; } } /* command interpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* new line */ crlf(); /* get command */ cmd = *(sbuf+0) ; /* help */ if ( cmd == '?' ) { show_help() ; } /* update code */ if ( cmd == 'E' ) { ydos = 0 ; ydos |= get_hex( *(sbuf+1) ) ; ydos <<= 4 ; ydos |= get_hex( *(sbuf+2) ) ; ydos <<= 4 ; ydos |= get_hex( *(sbuf+3) ) ; ydos <<= 4 ; ydos |= get_hex( *(sbuf+4) ) ; } /* show code */ if ( cmd == 'e' ) { binary_display(ydos); crlf(); } } } /* 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 ; } } } /* MtTimer2 interrupt handler */ void update_trigger() { /* update */ scnt++ ; if ( scnt == 3000 ) { scnt = 0 ; } /* event flag */ eflag = ON ; }  やっていることは、SH、CLKA、CLKB、DOSの信号を  ポートCから出力するだけ。タイマー割込みで2ms  のタイミングを作り、タイミングチャートに相当の  値を合成しています。  端末ソフトを使い、バーコードに相当するデータを  16ビットで指定できます。  端末ソフトTeraTermを利用して、現在の16ビットの  パターン確認と再設定操作は、以下。  パターン確認は2進数で、再設定は入力量を  減らす目的で16進数を利用します。  Arduinoスケッチで、BCSもどきの動作をさせて、SRAMの内容が  どうなっているのかを調べます。  AVRのファームウエアは、CUIでほぼバグを潰してあるので  CPLDの中にある回路が正しいのかをチェックします。  BCSのタイミングチャートをみると、CLKA、CLKBで取得した  32ビット(4バイト)は、ゴミとされているので、5バイト  目から解析していきます。2048ビットは、256バイトに相当  するため、300バイト分のデータを取出して、解析すれば  充分です。  300バイトのデータを逐次取出して解析するのは、面倒なので  ハードウエアを利用して楽をします。  BCSはコース面をバーコードと同じとみなして処理するので  白から黒、黒から白と変化する位置を見つけ出せばよいと  します。2進数では01、10となっている位置を探出すのが  基本です。  コース面は、黒、白の面積が多いので、1ブロックを16ビット  した複数ブロックに分割。  すべて黒、すべて白、それ以外を判定後、それ以外のブロック  を利用して、2進数で01、10の位置を見つけだす方式を採用。  16ビットがすべて黒、すべて白、それ以外を判断するのは  ハードウエアであれば、非常に単純です。  16ビット入力2ビット出力のデコーダと考えると、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 ( -- input Din : in std_logic_vector(15 downto 0) ; -- output Result : out std_logic_vector(1 downto 0) --; ); end taa ; architecture behavioral of taa is -- values CONSTANT NOTSAME : std_logic_vector(1 downto 0) := "00" ; CONSTANT ALLBLACK : std_logic_vector(1 downto 0) := "01" ; CONSTANT ALLWHITE : std_logic_vector(1 downto 0) := "10" ; -- input signal iDin : std_logic_vector(15 downto 0) ; -- output signal iResult : std_logic_vector(1 downto 0) ; begin -- output Result <= iResult ; -- input iDin <= Din ; -- judge iResult <= ALLBLACK when ( iDin = X"0000" ) else ALLWHITE when ( iDin = X"FFFF" ) else NOTSAME ; end behavioral;  上のVHDLコードをCPLDに入れると、10マクロセル程度でした。  16ビットのデータを、すべて黒、白、それ以外で判断すると  2048ビットある場合、128回の判定になります。FPGAの中に  128分の回路を入れたとしても、1280マクロセル程度なので  128ブロックの判定に1usもかならないでしょう。  コース面の情報は、中央付近が綺麗に取得できるので  中央の1024ビットと限定すると、64ブロック分の判定  回路を並べるだけになります。  1ブロックを、すべて黒、すべて白、それ以外を2進数  2ビットで10、01、00で表現すると、64ブロックでは  128ビットのデータ処理になります。  16ブロックを4エリアごとに分けて、該当エリアが  すべて黒、すべて白、それ以外の状態かを判定する  ハードウエアは、次のVHDLで表現できます。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tab is port ( -- input Din : in std_logic_vector(31 downto 0) ; -- output Dout : out std_logic_vector(2 downto 0) --; ); end tab ; architecture behavioral of tab is -- output signal iDout : std_logic_vector(2 downto 0) ; -- internal output signal iAB : std_logic ; signal iAW : std_logic ; -- internal all black signal iABR : std_logic_vector(3 downto 0) ; -- internal all white signal iAWR : std_logic_vector(3 downto 0) ; begin -- output Dout <= iDout ; -- internal output iDout(0) <= iAB ; iDout(1) <= iAW ; iDout(2) <= (not iAB) and (not iAW) ; -- all black iAB <= iABR(0) and iABR(1) and iABR(2) and iABR(3) ; iABR(0) <= (not Din(31)) and Din(30) and (not Din(29)) and Din(28) and (not Din(27)) and Din(26) and (not Din(25)) and Din(24) ; iABR(1) <= (not Din(23)) and Din(22) and (not Din(21)) and Din(20) and (not Din(19)) and Din(18) and (not Din(17)) and Din(16) ; iABR(2) <= (not Din(15)) and Din(14) and (not Din(13)) and Din(12) and (not Din(11)) and Din(10) and (not Din( 9)) and Din( 8) ; iABR(3) <= (not Din( 7)) and Din( 6) and (not Din( 5)) and Din( 4) and (not Din( 3)) and Din( 2) and (not Din( 1)) and Din( 0) ; -- all white iAW <= iAWR(0) and iAWR(1) and iAWR(2) and iAWR(3) ; iAWR(0) <= Din(31) and (not Din(30)) and Din(29) and (not Din(28)) and Din(27) and (not Din(26)) and Din(25) and (not Din(24)) ; iAWR(1) <= Din(23) and (not Din(22)) and Din(21) and (not Din(20)) and Din(19) and (not Din(18)) and Din(17) and (not Din(16)) ; iAWR(2) <= Din(15) and (not Din(14)) and Din(13) and (not Din(12)) and Din(11) and (not Din(10)) and Din( 9) and (not Din( 8)) ; iAWR(3) <= Din( 7) and (not Din( 6)) and Din( 5) and (not Din( 4)) and Din( 3) and (not Din( 2)) and Din( 1) and (not Din( 0)) ; end behavioral;  4エリアに区切り、すべて黒、すべて白、それ以外の  判断ができた後、コース面の黒から白、白から黒へと  変化する正確な位置を求めるようにします。  高速走行しているときは、正確な位置を求めていては  制御が追いつかないことがあるので、大まか判断処理  と、より精度の高い判断を選択できるようになります。  大まかな判断の場合、推定処理が必要になりますが  推定に必要な時間を稼ぎ出すためのデコード処理を  短くしたいという要求には答えられます。

目次

inserted by FC2 system