目次

最終VHDLコード

 MCR_VCマシンの制御は、ARMとFPGAを利用しています。

 ARMは、カメラのパラメータ設定と移動制御を担当し
 FPGAは、画像処理、モータ制御、ロータリーエンコーダの
 パルスカウントを担当しています。

 FPGAに入れるデジタル回路は、VHDLコードで記述します。

 最終VHDLコードを各ブロックに分けて説明します。



 FPGAが使うクロックは、20MHzです。


分周

 分周には、自作のコンポーネントを利用します。  このコンポーネントのVHDLコードは、以下です。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end clkgenx; architecture Behavioral of clkgenx is signal iSCNT : std_logic_vector(TOPX-1 downto 0); signal iCLK : std_logic ; begin -- output CLKOUT <= iCLK ; -- divider process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iSCNT <= (others => '0') ; iCLK <= '0' ; elsif rising_edge(CLOCK) then if ( conv_integer(iSCNT) = RMAX ) then iSCNT <= (others => '0') ; iCLK <= not iCLK ; else iSCNT <= iSCNT + '1' ; end if ; end if ; end process ; end Behavioral;  コンポーネントを使い、4つのクロックを生成しました。 TOPX : integer := 4 ; XMAX : integer := 10 ; TOPY : integer := 6 ; YMAX : integer := 50; TOPZ : integer := 6 ; ZMAX : integer := 50 ; TOPU : integer := 5 ; UMAX : integer := 25 ; -- component clock generator (20MHz/20 = 1MHz) CLKX : clkgenx generic map (TOPX,XMAX) port map (nRESET,CLOCK,iMCLK); -- component clock generator (1MHz/100 = 10kHz) CLKZ : clkgenx generic map (TOPZ,ZMAX) port map (nRESET,iMCLK,iPWMCLK); -- component clock generator (10kHz/100 = 100Hz) CLKY : clkgenx generic map (TOPY,YMAX) port map (nRESET,iPWMCLK,iSCLK); -- component clock generator (100Hz/50 = 2Hz) CLKU : clkgenx generic map (TOPU,UMAX) port map (nRESET,iSCLK,iICLK);  2Hzクロックは、FPGA基板上にあるLEDを点滅し  FPGAの動作を確認するため、使います。   RLED <= iICLK ;  10kHzクロックは、PWM波形を生成するために利用。  100Hzクロックは、カメラOV7670が出力してくる  垂直同期信号VSYNCを捕捉するために使います。

バスインタフェース

 ARMとFPGAの情報交換は、バスインタフェースを使います。  ARMのGP2、GP3を使います。  GP2を制御、GP3をデータの入出力にアサイン。  GP2は、次のようにビット割当てしました。  GP2の下位4ビットは、FPGA内部レジスタに  割当てしたアドレスを与えます。  ARMからFPGAに値をライトする場合(WE,OE)=(1,0)  としてから、TRGにパルスを与えます。  この状態で、クロックが入るとアドレス、データを  専用レジスタに格納します。 -- BUS interface (latch) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then -- register address iDADR <= (others => '0') ; -- register data iDDAT <= (others => '0') ; elsif rising_edge(CLOCK) then if ( GP2(5 downto 4) = "10" ) then iDADR <= GP2(3 downto 0) ; iDDAT <= GP3 ; end if ; end if ; end process ;  TRGのパルスをとらえるために、シフトレジスタに  よるシンクロナイザを使います。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iTRG_SFT <= "000" ; elsif rising_edge(CLOCK) then iTRG_SFT <= iTRG_SFT(1 downto 0) & GP2(6) ; end if ; end process ; iTRG <= '1' when ( iTRG_SFT = "011" or iTRG_SFT = "001" ) else '0' ;  トリガーがきたなら、値を該当レジスタに保存します。  シーケンサを利用した単純な処理で実現します。 -- BUS interface (write from external circuit) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDSTATE <= "00" ; -- state control -- direction iDIRF <= "00" ; -- forward iDIRR <= "01" ; -- reverse -- duty ratio iDUTYF <= (others => '0') ; iDUTYR <= (others => '0') ; -- target line iTLTOP <= (others => '0') ; iTLMIDDLE <= (others => '0') ; iTLBOTTOM <= (others => '0') ; -- rotate pulse trigger iSETPLS <= '0' ; elsif falling_edge(CLOCK) then case conv_integer(iDSTATE) is -- wait trigger when 0 => if ( iTRG = '1' ) then iDSTATE <= "01" ; else iDSTATE <= "00" ; end if ; -- deliver when 1 => iDSTATE <= "11" ; -- direction forword if ( iDADR = "0000" ) then iDIRF <= iDDAT(1 downto 0) ; end if ; -- forword duty if ( iDADR = "0001" ) then iDUTYF <= iDDAT(6 downto 0) ; end if ; -- direction reverse if ( iDADR = "0010" ) then iDIRR <= iDDAT(1 downto 0) ; end if ; -- reverse duty if ( iDADR = "0011" ) then iDUTYR <= iDDAT(6 downto 0) ; end if ; -- target line TOP if ( iDADR = "1011" ) then iTLTOP <= iDDAT ; end if ; -- target line MIDDLE if ( iDADR = "1100" ) then iTLMIDDLE <= iDDAT ; end if ; -- target line BOTTOM if ( iDADR = "1101" ) then iTLBOTTOM <= iDDAT ; end if ; -- clear distance pulse (upper) if ( iDADR = "1110" ) then iECNTX(15 downto 8) <= iDDAT ; iSETPLS <= '1' ; end if ; -- clear distance pulse (lower) if ( iDADR = "1111" ) then iECNTX( 7 downto 0) <= iDDAT ; iSETPLS <= '1' ; end if ; -- delay when 3 => iDSTATE <= "10" ; iSETPLS <= '0' ; -- return first state when 2 => iDSTATE <= "00" ; -- default when others => iDSTATE <= "00" ; end case ; end if ; end process ;  内部レジスタの内容をリードできないと  ARMの制御が進まないので、リード処理を  定義します。  ARMがFPGAの値リードには、(WE,OE)=(0,1)とします。  WE、OEの値の組合せで、バッファレジスタの値を  GP3に出力します。 GP3 <= iDAT when ( GP2(5 downto 4) = "01" ) else (others => 'Z');  バッファレジスタの値は、RSEL0〜RSEL3の組合せで  指定して、切り替えます。 -- register file -- front direction 0 -- front duty 1 -- rear direction 2 -- rear duty 3 -- TOP distance 4 -- MIDDLE distance 5 -- BOTTOM distance 6 -- camera data 7 -- TOP 8 -- MIDDLE 9 -- BOTTOM 10 -- target line TOP 11 -- target line MIDDLE 12 -- target line BOTTOM 13 -- encoder counter(high) 14 -- encoder counter(low) 15 iDAT <= "000000" & iDIRF when ( GP2(3 downto 0) = "0000" ) else -- front direction '0' & iDUTYF when ( GP2(3 downto 0) = "0001" ) else -- front duty "000000" & iDIRR when ( GP2(3 downto 0) = "0010" ) else -- rear direction '0' & iDUTYR when ( GP2(3 downto 0) = "0011" ) else -- rear duty iLTOPD when ( GP2(3 downto 0) = "0100" ) else -- TOP distance iLMIDDLED when ( GP2(3 downto 0) = "0101" ) else -- MIDDLE distance iLBOTTOMD when ( GP2(3 downto 0) = "0110" ) else -- BOTTOM distance iDOB when ( GP2(3 downto 0) = "0111" ) else -- camera data iLTOP when ( GP2(3 downto 0) = "1000" ) else -- TOP iLMIDDLE when ( GP2(3 downto 0) = "1001" ) else -- MIDDLE iLBOTTOM when ( GP2(3 downto 0) = "1010" ) else -- BOTTOM iTLTOP when ( GP2(3 downto 0) = "1011" ) else -- target line TOP iTLMIDDLE when ( GP2(3 downto 0) = "1100" ) else -- target line MIDDLE iTLBOTTOM when ( GP2(3 downto 0) = "1101" ) else -- target line BOTTOM iECNT(15 downto 8) when ( GP2(3 downto 0) = "1110" ) else -- encoder counter(high) iECNT( 7 downto 0) when ( GP2(3 downto 0) = "1111" ) else -- encoder counter(low) X"FF" ; -- dummy

PWM波形生成

 モータ回転数は、ARMからFPGAに与えるDUTY比で  変更します。そのために、コンポーネントを利用  します。  コンポーネントは、次のVHDLコードです。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity pwmax is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- input DIR : in std_logic_vector(1 downto 0) ; -- duty ratio DUTY : in std_logic_vector(6 downto 0) ; -- output POUT : out std_logic_vector(1 downto 0) --; ); end pwmax; architecture Behavioral of pwmax is signal iCNT : integer range 0 to 100 ; signal iDUTY : std_logic_vector(6 downto 0) ; signal iOUT : std_logic ; signal iPOUT : std_logic_vector(1 downto 0) ; begin -- output POUT <= iPOUT ; -- internal iPOUT(0) <= iOUT when ( DIR = "01" ) else '0' ; iPOUT(1) <= iOUT when ( DIR = "10" ) else '0' ; iOUT <= '1' when ( iCNT < conv_integer(iDUTY) ) else '0' ; -- counter process(nRESET,CLOCK) begin if ( nRESET = '0' ) then iCNT <= 0 ; iDUTY <= (others => '0') ; elsif rising_edge( CLOCK ) then if ( iCNT = 100 ) then iCNT <= 0 ; iDUTY <= DUTY ; else iCNT <= iCNT + 1 ; end if ; end if; end process; end Behavioral;  コンポーネントのやっていることは、非常に単純です。  与えるパラメータは、DUTY比と出力先指定だけ。  今回のマシンは、前輪、後輪にDCモータを利用しています。  2つのDCモータに与える信号は、次のように生成します。 -- front motor control FRONTX : pwmax port map (nRESET,iPWMCLK,iDIRF,iDUTYF,iFOUT); -- rear motor control REARX : pwmax port map (nRESET,iPWMCLK,iDIRR,iDUTYR,iROUT);  パルス出力は、次のように単純です。 FOUT <= iFOUT ; ROUT <= iROUT ;

エンコーダパルス処理

 マシンシャーシには、ロータリーエンコーダを  接続し、移動距離を計測できるようにしました。  マシンの移動速度は、10m/sまで達していないので  100Hzでロータリーエンコーダから出力されてくる  パルスを同期化して、カウンタをインクリメント  します。  シフトレジスタを利用したシンクロナイザで同期化します。  シフトレジスタを使い、チャタリングを除去しています。 -- encoder counter process (nRESET,iSCLK) begin if ( nRESET = '0' ) then iENPLS_SFT <= "000" ; elsif rising_edge(iSCLK) then iENPLS_SFT <= iENPLS_SFT(1 downto 0) & ENPULSE ; end if ; end process ; iENPLS <= '1' when ( iENPLS_SFT = "011" ) else '0' ;  同期化されたトリガーで、カウンタをインクリメントします。 process (nRESET,iSETPLS,iSCLK,iECNTX) begin if ( nRESET = '0' ) then iECNT <= (others => '0') ; elsif ( iSETPLS = '1' ) then iECNT <= iECNTX ; elsif rising_edge(iSCLK) then if ( iENPLS = '1' ) then iECNT <= iECNT + '1' ; end if ; end if ; end process ;  ロータリーエンコーダから、パルスが来ているか  どうかをFPGA基板上のLEDでモニタします。   GLED <= not ENPULSE ;  エンコーダパルス処理のブロック図は、以下です。

画像データ入力

 画像データは、ARMからのトリガーで動作する  シーケンサを使って入力します。  トリガーが来たら、VSYNCを捕捉後、PCLKとHREFで  合成したトリガーにより、YUVの8ビットデータを  内部レジスタにラッチします。  画像データは、160x120の膨大なサイズになりますが  3ラインだけを選択して、DualPortMemoryに保存して  いきます。  ラッチしたデータが、Y信号であればDualPortMemoryに  保存し、UV信号であればスキップします。  1ピクセルにつき、PCLKが2回来るので、1回目の  PCLKでデータラッチし、2回目のPCLKで保存します。  これらの処理を、シーケンサで定義すると、以下です。 -- camera sequencer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCSTATE <= 0 ; iLCNT <= 0 ; iPCNT <= 0 ; iCDATA <= (others => '0') ; iATRG_SFT <= "00" ; -- maximum iTMAX <= (others => '0') ; iMMAX <= (others => '0') ; iBMAX <= (others => '0') ; -- minimum iTMIN <= (others => '1') ; iMMIN <= (others => '1') ; iBMIN <= (others => '1') ; elsif rising_edge(CLOCK) then case iCSTATE is -- wait trigger from micom when 0 => if ( iCTRG = '1' ) then iCSTATE <= 1 ; -- next else iCSTATE <= 0 ; -- stay end if ; -- wait VSYNC trigger when 1 => if ( iVSTRG = '1' ) then iCSTATE <= 2 ; -- next iCDATA <= (others => '0') ; else iCSTATE <= 1 ; -- stay end if ; -- judge line counter when 2 => if ( iLCNT = LCNT_MAX ) then iCSTATE <= 7 ; -- exit else iCSTATE <= 3 ; -- loop end if ; -- store even data (Y) when 3 => if ( iPTRG = '1' ) then iCSTATE <= 4 ; -- next iCDATA <= CDAT ; iATRG_SFT <= iATRG_SFT(0) & '1' ; else iCSTATE <= 3 ; -- stay end if ; -- skip odd data (U or V) and store data when 4 => if ( iPTRG = '1' ) then iCSTATE <= 5 ; -- next iATRG_SFT <= iATRG_SFT(0) & '1' ; -- TOP line min , max if ( iTARGET_LT = '1' ) then -- min if ( iTMIN > iCDATA ) then iTMIN <= iCDATA ; end if ; -- max if ( iTMAX < iCDATA ) then iTMAX <= iCDATA ; end if ; end if ; -- MIDDLE line min , max if ( iTARGET_LM = '1' ) then -- min if ( iMMIN > iCDATA ) then iMMIN <= iCDATA ; end if ; -- max if ( iMMAX < iCDATA ) then iMMAX <= iCDATA ; end if ; end if ; -- BOTTOM line min , max if ( iTARGET_LB = '1' ) then -- min if ( iBMIN > iCDATA ) then iBMIN <= iCDATA ; end if ; -- max if ( iBMAX < iCDATA ) then iBMAX <= iCDATA ; end if ; end if ; else iCSTATE <= 4 ; -- state : 4 end if ; -- judge pixel counter when 5 => if ( iPCNT = PCNT_MAX ) then iCSTATE <= 6 ; -- state : 6 else iCSTATE <= 3 ; -- state : 3 iPCNT <= iPCNT + 1 ; end if ; iATRG_SFT <= "00" ; -- new line handling when 6 => iCSTATE <= 2 ; -- state : 2 iPCNT <= 0 ; iLCNT <= iLCNT + 1 ; -- clear line counter when 7 => iCSTATE <= 8 ; -- state : 8 -- clear line counter iLCNT <= 0 ; -- return catch trigger when 8 => iCSTATE <= 0 ; -- state : 0 -- default when others => iCSTATE <= 0 ; end case ; end if ; end process ;  データをDualPortMemoryに保存するために、トリガーを  使います。トリガーは、シフトレジスタで実現します。 iATRG_SFT <= iATRG_SFT(0) & '1' ; -- store Y data trigger iATRG <= iATRG_SFT(1) and iATRG_SFT(0) ;  DualPortMemoryのデータ保存用シーケンサは  次のように定義しました。 -- A port sequencer (store Y signal) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iASTATE <= "000" ; iCADRA <= (others => '0') ; elsif rising_edge(CLOCK) then case conv_integer(iASTATE) is -- wait trigger when 0 => if ( iATRG = '1' and iTARGET_LT = '1' ) then iASTATE <= "001" ; -- state : 1 elsif ( iATRG = '1' and iTARGET_LM = '1' ) then iASTATE <= "001" ; -- state : 1 elsif ( iATRG = '1' and iTARGET_LB = '1' ) then iASTATE <= "001" ; -- state : 1 else iASTATE <= "000" ; -- state : 0 end if ; -- transfer data when 1 => iDIA <= iCDATA ; iASTATE <= "011" ; -- state : 3 -- send write trigger when 3 => iASTATE <= "111" ; -- state : 7 -- address increment when 7 => iCADRA <= iCADRA + '1' ; iASTATE <= "110" ; -- state : 6 -- judge when 6 => if ( conv_integer(iCADRA) = QMAX ) then iASTATE <= "100" ; -- state : 4 else iASTATE <= "000" ; -- state : 0 end if ; -- return first state when 4 => iASTATE <= "000" ; -- state : 0 iCADRA <= (others => '0') ; -- default when others => iASTATE <= "000" ; end case ; end if ; end process ; -- camera data store trigger iWEA <= '1' when ( iASTATE = "011" ) else '0' ;  PCLKを扱うシーケンサからのトリガーを受取り  一時レジスタに記憶しているデータを、自分の  中にあるレジスタにコピーします。指定ライン  のピクセルデータであれば、DualPortMemory  にライトパルスを出力します。  このシーケンサを定義するため、次のCのコードで  動作を確認してあります。 void bmachine(void) { switch ( xstate ) { /* wait trigger */ case 0 : if ( itrg == ON ) { xstate = 1 ; } else { xstate = 0 ; } break ; /* initialize */ case 1 : xcnt = 0 ; xstate = 2 ; break ; /* get data */ case 2 : puts("*** get data ***") ; xcnt++ ; xstate = 3 ; break ; /* address increment */ case 3 : xadr++ ; xstate = 4 ; break ; /* compare */ case 4 : puts("+++ compare +++") ; xstate = 5 ; break ; /* store */ case 5 : puts("*** put data ***") ; xstate = 6 ; break ; /* address increment */ case 6 : yadr++ ; xstate = 7 ; break ; /* judge */ case 7 : if ( xcnt == 160 ) { xstate = 8 ; } else { xstate = 2 ; } break ; /* flag increment */ case 8 : xflag++ ; xcnt = 0 ; xstate = 9 ; break ; /* judge */ case 9 : if ( xflag == 3 ) { xstate = 10 ; } else { xstate = 0 ; } break ; /* complete */ case 10 : xstate = 0 ; xflag = 0 ; xadr = 0 ; yadr = 0 ; break ; /* */ default : xstate = 0 ; break ; } /* debug */ printf(" FLAG(%d) state(%2d) sadr(%3d) dadr(%3d) count(%2d)\n",xflag,xstate,xadr,yadr,xcnt); }  シーケンサと等価な動作をしているかを  CUIで確認してから、VHDLコードに変換  しました。  画像データを2値化するために、1ラインごとに  データの最大値、最小値を求め、その相加平均を  計算します。これを閾値として利用します。 iTHVT <= (conv_integer(iTMAX) + conv_integer(iTMIN)) / 2 ; iTHVM <= (conv_integer(iMMAX) + conv_integer(iMMIN)) / 2 ; iTHVB <= (conv_integer(iBMAX) + conv_integer(iBMIN)) / 2 ;

2値化処理

 2値化は、画像処理データを保存したときに  計算で求めた閾値を利用します。  画像データと閾値を比較し、画像データが大きいと  1として、2値化データ保存用DualPortMemoryに  格納します。  この処理に、次のシーケンサを使います。 -- B port sequencer (store binary code) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iBSTATE <= "0000" ; -- address iCADRB <= (others => '0') ; iBADRA <= (others => '0') ; -- data iBDAT <= (others => '0') ; iBDIA <= (others => '0') ; -- flag iBFLAG <= 0 ; elsif rising_edge(CLOCK) then case conv_integer(iBSTATE) is -- wait trigger when 0 => if ( iBTRG = '1' ) then iBSTATE <= "0001" ; -- next else iBSTATE <= "0000" ; -- stay end if ; -- initialize when 1 => iBSTATE <= "0010" ; -- next iBCNT <= 0 ; -- get data when 2 => iBSTATE <= "0011" ; -- next iBDAT <= iDOB ; iBCNT <= iBCNT + 1 ; -- increment counter -- address increment when 3 => iBSTATE <= "0100" ; -- next iCADRB <= iCADRB + 1 ; iBDIA <= X"00" ; -- compare when 4 => iBSTATE <= "0101" ; -- next -- TOP if ( iBFLAG = 0 ) then if ( conv_integer(iBDAT) > iTHVT ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- MIDDLE if ( iBFLAG = 1 ) then if ( conv_integer(iBDAT) > iTHVM ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- BOTTOM if ( iBFLAG = 2 ) then if ( conv_integer(iBDAT) > iTHVB ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- store when 5 => iBSTATE <= "0110" ; -- next -- address increment when 6 => iBSTATE <= "0111" ; -- next iBADRA <= iBADRA + '1' ; -- judge when 7 => if ( iBCNT = PCNT_MAX ) then iBSTATE <= "1000" ; -- next else iBSTATE <= "0010" ; -- loop end if ; -- flag increment when 8 => iBSTATE <= "1001" ; -- next iBFLAG <= iBFLAG + 1 ; -- update iBCNT <= 0 ; -- judge when 9 => if ( iBFLAG = 3 ) then iBSTATE <= "1010" ; -- next else iBSTATE <= "0000" ; -- loop end if ; -- complete when 10 => iBSTATE <= "0000" ; -- first state iBFLAG <= 0 ; -- clear flag -- initialize address iCADRB <= (others => '0') ; iBADRA <= (others => '0') ; -- default when others => iBSTATE <= "0000" ; end case ; end if ; end process ; -- binary data store trigger iBWEA <= '1' when ( iBSTATE = "0101" ) else '0' ; -- generate parameters trigger iWTRG <= '1' when ( iBSTATE = "1010" ) else '0' ;  カメラとのインタフェースを担当するシーケンサから  指定ラインの次のラインであることを通知してもらい  ます。指定ラインの次になったときには、画像データ  閾値が確定しているので、比較と保存をシーケンサで  面倒をみます。  2つのシーケンサが同時に動くので、ラインカウンタ  変化で、処理中断がないよう、計数フラグを使います。  DualPortMemoryに2値化データをライトするために  トリガーiBWEAを使います。  全データの2値化が終われば、センサーデータに  関係する情報を生成します。

センサーデータ生成

 センサーデータは、センターライン中央位置と  その幅を計算し、2情報で表現します。  センサー関係情報を生成するシーケンサは  次のように定義しました。 process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iWSTATE <= "0000" ; -- address iBADRB <= (others => '0') ; -- data iWNOW <= 0 ; iWNEXT <= 0 ; -- result iLTOP <= (others => '1') ; iLMIDDLE <= (others => '1') ; iLBOTTOM <= (others => '1') ; iLTOPD <= (others => '1') ; iLMIDDLED <= (others => '1') ; iLBOTTOMD <= (others => '1') ; -- counter iWCNT <= 0 ; -- Zero -> Plus iWZP <= 255 ; -- Plus -> Zero iWPZ <= 255 ; elsif rising_edge(CLOCK) then case conv_integer(iWSTATE) is -- wait trigger when 0 => if ( iWTRG = '1' ) then iWSTATE <= "0001" ; -- next -- address iBADRB <= (others => '0') ; -- address iWFLAG <= 0 ; else iWSTATE <= "0000" ; -- stay end if ; -- initialize when 1 => iWSTATE <= "0010" ; -- next -- counter iWCNT <= 0 ; -- Zero -> Plus iWZP <= 255 ; -- Plus -> Zero iWPZ <= 255 ; -- iNOW when 2 => iWSTATE <= "0011" ; -- data iWNOW <= conv_integer(iBDOB) ; -- counter iWCNT <= iWCNT + 1 ; -- address increment when 3 => iWSTATE <= "0100" ; -- next -- address iBADRB <= iBADRB + '1' ; -- iNEXT when 4 => iWSTATE <= "0101" ; -- next -- data iWNEXT <= conv_integer(iBDOB) ; -- counter iWCNT <= iWCNT + 1 ; -- address increment when 5 => iWSTATE <= "0110" ; -- next -- address iBADRB <= iBADRB + '1' ; -- compare when 6 => iWSTATE <= "0111" ; -- next -- Zero -> Plus if ( iWNOW = 0 and iWNEXT = 1 ) then iWZP <= iWCNT - 2 ; end if ; -- Plus -> Zero if ( iWNOW = 0 and iWNEXT = 1 ) then iWPZ <= iWCNT - 2 ; end if ; -- copy when 7 => iWSTATE <= "1000" ; -- next iWNOW <= iWNEXT ; -- judge when 8 => if ( iWCNT = 160 ) then iWSTATE <= "1001" ; -- next else iWSTATE <= "0100" ; -- loop end if ; -- calculate when 9 => iWSTATE <= "1010" ; -- next -- all WHITE or all BLACK if ( iWZP = 255 and iWPZ = 255 ) then -- distance iLTOPD <= (others => '0') ; iLMIDDLED <= (others => '0') ; iLBOTTOMD <= (others => '0') ; -- location if ( iWNOW = 1 ) then -- TOP if ( iWFLAG = 0 ) then iLTOP <= conv_std_logic_vector(160,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then iLMIDDLE <= conv_std_logic_vector(160,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then iLBOTTOM <= conv_std_logic_vector(160,8) ; end if ; else -- TOP if ( iWFLAG = 0 ) then iLTOP <= (others => '0') ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then iLMIDDLE <= (others => '0') ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then iLBOTTOM <= (others => '0') ; end if ; end if ; end if ; -- left WHITE if ( iWZP = 255 and iWPZ < 160 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= (others => '0') ; -- distance iLTOPD <= conv_std_logic_vector(iWPZ,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= (others => '0') ; -- distance iLMIDDLED <= conv_std_logic_vector(iWPZ,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= (others => '0') ; -- distance iLBOTTOMD <= conv_std_logic_vector(iWPZ,8) ; end if ; end if ; -- right WHITE if ( iWZP < 160 and iWPZ = 255 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= conv_std_logic_vector(160,8) ; -- distance iLTOPD <= conv_std_logic_vector(iWZP,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= conv_std_logic_vector(160,8) ; -- distance iLMIDDLED <= conv_std_logic_vector(iWZP,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= conv_std_logic_vector(160,8) ; -- distance iLBOTTOMD <= conv_std_logic_vector(iWZP,8) ; end if ; end if ; -- if ( iWZP < 160 and iWPZ < 160 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLTOPD <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLTOPD <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLMIDDLED <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLMIDDLED <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLBOTTOMD <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLBOTTOMD <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; end if ; -- flag increment when 10 => iWSTATE <= "1011" ; -- next iWFLAG <= iWFLAG + 1 ; -- flag increment when 11 => if ( iWFLAG = 3 ) then iWSTATE <= "0000" ; -- first iWFLAG <= 0 ; else iWSTATE <= "0001" ; -- loop end if ; -- default when others => iWSTATE <= "0000" ; end case ; end if ; end process ;  上のシーケンサと等価な動作をする、Cのコードで  CUIに文字列を出力して確認してあります。 void smachine(void) { switch ( xstate ) { /* wait trigger */ case 0 : if ( itrg == ON ) { xstate = 1 ; xflag = 0 ; xadr = 0 ; } else { xstate = 0 ; } break ; /* initialize */ case 1 : xcnt = 0 ; izp = 255 ; ipz = 255 ; xstate = 2 ; break ; /* iNOW copy */ case 2 : puts("*** iNOW ***") ; xcnt++ ; xstate = 3 ; break ; /* address increment */ case 3 : xadr++ ; xstate = 4 ; break ; /* iNEXT copy */ case 4 : puts("*** iNEXT ***") ; xcnt++ ; xstate = 5 ; break ; /* address increment */ case 5 : xadr++ ; xstate = 6 ; break ; /* compare */ case 6 : puts("+++ compare +++") ; xstate = 7 ; break ; /* copy */ case 7 : puts("****** iNOW <= iNEXT ******") ; xstate = 8 ; break ; /* judge */ case 8 : if ( xcnt == 160 ) { xstate = 9 ; } else { xstate = 4 ; } break ; /* calculate */ case 9 : puts("+++ calculate +++"); xstate = 10 ; break ; /* countable flag increment */ case 10 : xflag++ ; xstate = 11 ; break ; /* judge */ case 11 : if ( xflag == 3 ) { xstate = 0 ; xflag = 0 ; xadr = 0 ; xcnt = 0 ; } else { xstate = 1 ; } break ; /* */ default : xstate = 0 ; break ; } /* debug */ printf(" FLAG(%d) state(%2d) address(%3d) count(%3d)\n",xflag,xstate,xadr,xcnt); }  2値化したデータから、ピクセルデータが  0→1、1→0になる変化点を求めます。  変換点の位置の相加平均を求め、その1/2を  センターライン中央位置とします。また差  を幅としておきます。  この2情報をセンサーデータとしてARMに  渡します。  この処理で求めた値だけでは、すべて0、すべて1  のようなとき、路面のセンサー情報として不適切と  なることが発生します。そこで、次の場合分けで  対応しました。  2つの変換点が、ともに255である。  幅は0になるが、2値化データを見て  0ならば、それはすべて黒と判断。  このとき中央位置は0とする。  1ならば、それはすべて白と判断。  このとき中央位置は160とする。  ピクセルデータがすべて黒は、路面上に使うべきセンター  ラインがないことを意味します。すべて白はクランク用の  マーカーを見つけたことになります。  ピクセルデータが0→1になることがなく  1→0になることはある。  左の変化点が255で、右の変化点が0〜159と  なる場合。左片側白になり、レーンチェンジ  の左マーカーになります。  中央位置は0、幅を右の変化点の値とします。  レーンチェンジのマーカーは、右の場合も  あるので、その場合を考えます。  ピクセルデータが1→0になることがなく  0→1になることはある。  右の変化点が255で、左の変化点が0〜159と  なる場合。右片側白になり、レーンチェンジ  の右マーカーになります。  中央位置は160、幅を左の変化点の値とします。

全VHDLコード

 これまでの内容をまとめると、次の  VHDLコードとなります。 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; Library UNISIM; use UNISIM.vcomponents.all; entity mcrvcxy is generic ( TOPX : integer := 4 ; XMAX : integer := 10 ; TOPY : integer := 6 ; YMAX : integer := 50; TOPZ : integer := 6 ; ZMAX : integer := 50 ; TOPU : integer := 5 ; UMAX : integer := 25 ; LCNT_MAX : integer := 60 ; PCNT_MAX : integer := 160 ; QMAX : integer := 480 --; 160pixel x 3line ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- camera interface VS : in std_logic ; HREF : in std_logic ; PCLK : in std_logic ; CDAT : in std_logic_vector(7 downto 0) ; -- run output GLED : out std_logic ; -- rotary encoder pulse monitor RLED : out std_logic ; -- run -- MOTOR control FOUT : out std_logic_vector(1 downto 0) ; ROUT : out std_logic_vector(1 downto 0) ; -- encoder ENPULSE : in std_logic ; -- monitor STATEX : out std_logic_vector(2 downto 0) ; ATRGX : out std_logic ; BTRGX : out std_logic ; WTRGX : out std_logic ; MVS : out std_logic ; HPCLK : out std_logic ; -- BUS interface control signals GP2 : in std_logic_vector(7 downto 0) ; -- CENA -- TRG -- WE -- OE -- LSEL3 -- LSEL2 -- LSEL1 -- LSEL0 -- BUS interface GP3 : inout std_logic_vector(7 downto 0) --; ); end mcrvcxy; architecture Behavioral of mcrvcxy is -- component clock generator component clkgenx is generic ( TOPX : integer ; RMAX : integer --; ); port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- output CLKOUT : out std_logic -- ; ); end component ; -- component PWM component pwmax is port ( -- system nRESET : in std_logic ; CLOCK : in std_logic ; -- input DIR : in std_logic_vector(1 downto 0) ; -- duty ratio DUTY : in std_logic_vector(6 downto 0) ; -- output POUT : out std_logic_vector(1 downto 0) --; ); end component; -- clock signal iPWMCLK : std_logic ; signal iMCLK : std_logic ; signal iSCLK : std_logic ; signal iICLK : std_logic ; signal iPCLK : std_logic ; -- camera sequencer signal iCSTATE : integer range 0 to 8 ; signal iCSTATEX : std_logic_vector(2 downto 0) ; signal iLCNT : integer range 0 to LCNT_MAX ; signal iPCNT : integer range 0 to PCNT_MAX ; signal iCDATA : std_logic_vector(7 downto 0) ; signal iCTRG : std_logic ; signal iVSTRG : std_logic ; signal iCTRG_SFT : std_logic_vector(2 downto 0) ; signal iPTRG : std_logic ; signal iVS_SFT : std_logic_vector(1 downto 0) ; signal iPCLK_SFT : std_logic_vector(2 downto 0) ; signal iTARGET_LT : std_logic ; signal iTARGET_LM : std_logic ; signal iTARGET_LB : std_logic ; signal iTMAX : std_logic_vector(7 downto 0) ; signal iTMIN : std_logic_vector(7 downto 0) ; signal iMMAX : std_logic_vector(7 downto 0) ; signal iMMIN : std_logic_vector(7 downto 0) ; signal iBMAX : std_logic_vector(7 downto 0) ; signal iBMIN : std_logic_vector(7 downto 0) ; signal iTHVT : integer range 0 to 255 ; signal iTHVM : integer range 0 to 255 ; signal iTHVB : integer range 0 to 255 ; -- A port sequencer signal iATRG_SFT : std_logic_vector(1 downto 0) ; signal iATRG : std_logic ; signal iASTATE : std_logic_vector(2 downto 0) ; -- camera buffer memory signal iCADRA : std_logic_vector(10 downto 0) ; signal iCADRB : std_logic_vector(10 downto 0) ; signal iDIA : std_logic_vector( 7 downto 0) ; signal iDOB : std_logic_vector( 7 downto 0) ; signal iWEA : std_logic ; signal iBTRG : std_logic ; -- buffer memory signal iBADRA : std_logic_vector(10 downto 0) ; signal iBADRB : std_logic_vector(10 downto 0) ; signal iBDIA : std_logic_vector( 7 downto 0) ; signal iBDOB : std_logic_vector( 7 downto 0) ; signal iBWEA : std_logic ; -- store binary data sequencer signal iBCNT : integer range 0 to PCNT_MAX ; signal iBDAT : std_logic_vector(7 downto 0) ; signal iBFLAG : integer range 0 to 3 ; signal iBSTATE : std_logic_vector(3 downto 0) ; -- last sequencer signal iWSTATE : std_logic_vector(3 downto 0) ; signal iWTRG : std_logic ; signal iWCNT : integer range 0 to PCNT_MAX ; signal iWNOW : integer range 0 to 255 ; signal iWNEXT : integer range 0 to 255 ; signal iWFLAG : integer range 0 to 3 ; signal iWZP : integer range 0 to 255 ; signal iWPZ : integer range 0 to 255 ; -- MOTOR control signal iFOUT : std_logic_vector(1 downto 0) ; signal iROUT : std_logic_vector(1 downto 0) ; -- BUS interface signal iDIRR : std_logic_vector(1 downto 0) ; signal iDIRF : std_logic_vector(1 downto 0) ; signal iDUTYF : std_logic_vector(6 downto 0) ; signal iDUTYR : std_logic_vector(6 downto 0) ; signal iDSTATE : std_logic_vector(1 downto 0) ; signal iDADR : std_logic_vector(3 downto 0) ; signal iDDAT : std_logic_vector(7 downto 0) ; -- trigger signal iTRG : std_logic ; signal iTRG_SFT : std_logic_vector(2 downto 0) ; -- sensor register file (location) signal iDAT : std_logic_vector(7 downto 0) ; signal iLTOP : std_logic_vector(7 downto 0) ; signal iLMIDDLE : std_logic_vector(7 downto 0) ; signal iLBOTTOM : std_logic_vector(7 downto 0) ; -- sensor register file (distance) signal iLTOPD : std_logic_vector(7 downto 0) ; signal iLMIDDLED : std_logic_vector(7 downto 0) ; signal iLBOTTOMD : std_logic_vector(7 downto 0) ; -- target line register file signal iTLTOP : std_logic_vector(7 downto 0) ; signal iTLMIDDLE : std_logic_vector(7 downto 0) ; signal iTLBOTTOM : std_logic_vector(7 downto 0) ; -- encoder counter signal iECNT : std_logic_vector(15 downto 0) ; signal iECNTX : std_logic_vector(15 downto 0) ; signal iENPLS_SFT : std_logic_vector( 2 downto 0) ; signal iENPLS : std_logic ; signal iSETPLS : std_logic ; begin -- component clock generator (20MHz/20 = 1MHz) CLKX : clkgenx generic map (TOPX,XMAX) port map (nRESET,CLOCK,iMCLK); -- component clock generator (1MHz/100 = 10kHz) CLKZ : clkgenx generic map (TOPZ,ZMAX) port map (nRESET,iMCLK,iPWMCLK); -- component clock generator (10kHz/100 = 100Hz) CLKY : clkgenx generic map (TOPY,YMAX) port map (nRESET,iPWMCLK,iSCLK); -- component clock generator (100Hz/50 = 2Hz) CLKU : clkgenx generic map (TOPU,UMAX) port map (nRESET,iSCLK,iICLK); -- front motor control FRONTX : pwmax port map (nRESET,iPWMCLK,iDIRF,iDUTYF,iFOUT); -- rear motor control REARX : pwmax port map (nRESET,iPWMCLK,iDIRR,iDUTYR,iROUT); -- RAMB16_S9_S9: 2k x 8 + 1 Parity bit Dual-Port RAM -- graphic data buffer RAMB16_S9_S9_inst : RAMB16_S9_S9 generic map ( INIT_A => X"000", -- Value of output RAM registers on Port A at startup INIT_B => X"000", -- Value of output RAM registers on Port B at startup SRVAL_A => X"000", -- Port A ouput value upon SSR assertion SRVAL_B => X"000", -- Port B ouput value upon SSR assertion WRITE_MODE_A => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE WRITE_MODE_B => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE SIM_COLLISION_CHECK => "ALL", -- "NONE", "WARNING", "GENERATE_X_ONLY", "ALL" -- The following INIT_xx declarations specify the initial contents of the RAM -- Address 0 to 511 INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 512 to 1023 INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1024 to 1535 INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1536 to 2047 INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000", -- The next set of INITP_xx are for the parity bits -- Address 0 to 511 INITP_00 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_01 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 512 to 1023 INITP_02 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_03 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1024 to 1535 INITP_04 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_05 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1536 to 2047 INITP_06 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_07 => X"0000000000000000000000000000000000000000000000000000000000000000") port map ( DOA => open, -- Port A 8-bit Data Output DOB => iDOB, -- Port B 8-bit Data Output DOPA => open, -- Port A 1-bit Parity Output DOPB => open, -- Port B 1-bit Parity Output ADDRA => iCADRA, -- Port A 11-bit Address Input ADDRB => iCADRB, -- Port B 11-bit Address Input CLKA => CLOCK, -- Port A Clock CLKB => CLOCK, -- Port B Clock DIA => iDIA, -- Port A 8-bit Data Input DIB => X"FF", -- Port B 8-bit Data Input DIPA => "1", -- Port A 1-bit parity Input DIPB => "1", -- Port-B 1-bit parity Input ENA => '1', -- Port A RAM Enable Input ENB => '0', -- Port B RAM Enable Input SSRA => '0', -- Port A Synchronous Set/Reset Input SSRB => '0', -- Port B Synchronous Set/Reset Input WEA => iWEA, -- Port A Write Enable Input WEB => '0' -- Port B Write Enable Input ); -- RAMB16_S9_S9: 2k x 8 + 1 Parity bit Dual-Port RAM -- sensor buffer XBUFX : RAMB16_S9_S9 generic map ( INIT_A => X"000", -- Value of output RAM registers on Port A at startup INIT_B => X"000", -- Value of output RAM registers on Port B at startup SRVAL_A => X"000", -- Port A ouput value upon SSR assertion SRVAL_B => X"000", -- Port B ouput value upon SSR assertion WRITE_MODE_A => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE WRITE_MODE_B => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE SIM_COLLISION_CHECK => "ALL", -- "NONE", "WARNING", "GENERATE_X_ONLY", "ALL" -- The following INIT_xx declarations specify the initial contents of the RAM -- Address 0 to 511 INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 512 to 1023 INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1024 to 1535 INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1536 to 2047 INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000", INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000", -- The next set of INITP_xx are for the parity bits -- Address 0 to 511 INITP_00 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_01 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 512 to 1023 INITP_02 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_03 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1024 to 1535 INITP_04 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_05 => X"0000000000000000000000000000000000000000000000000000000000000000", -- Address 1536 to 2047 INITP_06 => X"0000000000000000000000000000000000000000000000000000000000000000", INITP_07 => X"0000000000000000000000000000000000000000000000000000000000000000") port map ( DOA => open, -- Port A 8-bit Data Output DOB => iBDOB, -- Port B 8-bit Data Output DOPA => open, -- Port A 1-bit Parity Output DOPB => open, -- Port B 1-bit Parity Output ADDRA => iBADRA, -- Port A 11-bit Address Input ADDRB => iBADRB, -- Port B 11-bit Address Input CLKA => CLOCK, -- Port A Clock CLKB => CLOCK, -- Port B Clock DIA => iBDIA, -- Port A 8-bit Data Input DIB => X"FF", -- Port B 8-bit Data Input DIPA => "1", -- Port A 1-bit parity Input DIPB => "1", -- Port-B 1-bit parity Input ENA => '1', -- Port A RAM Enable Input ENB => '0', -- Port B RAM Enable Input SSRA => '0', -- Port A Synchronous Set/Reset Input SSRB => '0', -- Port B Synchronous Set/Reset Input WEA => iBWEA, -- Port A Write Enable Input WEB => '0' -- Port B Write Enable Input ); -- monitor output GLED <= not ENPULSE ; RLED <= iICLK ; MVS <= not iVSTRG ; HPCLK <= not iPCLK ; STATEX <= not iCSTATEX ; ATRGX <= not iATRG ; BTRGX <= not iBTRG ; WTRGX <= not iWTRG ; -- trigger process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iTRG_SFT <= "000" ; iCTRG_SFT <= "000" ; elsif rising_edge(CLOCK) then iTRG_SFT <= iTRG_SFT(1 downto 0) & GP2(6) ; iCTRG_SFT <= iCTRG_SFT(1 downto 0) & GP2(7) ; end if ; end process ; iTRG <= '1' when ( iTRG_SFT = "011" or iTRG_SFT = "001" ) else '0' ; iCTRG <= '1' when ( iCTRG_SFT = "011" or iTRG_SFT = "001" ) else '0' ; -- BUS interface (latch) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then -- register address iDADR <= (others => '0') ; -- register data iDDAT <= (others => '0') ; elsif rising_edge(CLOCK) then if ( GP2(5 downto 4) = "10" ) then iDADR <= GP2(3 downto 0) ; iDDAT <= GP3 ; end if ; end if ; end process ; -- BUS interface (write from external circuit) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iDSTATE <= "00" ; -- state control -- direction iDIRF <= "00" ; -- forward iDIRR <= "01" ; -- reverse -- duty ratio iDUTYF <= (others => '0') ; iDUTYR <= (others => '0') ; -- target line iTLTOP <= (others => '0') ; iTLMIDDLE <= (others => '0') ; iTLBOTTOM <= (others => '0') ; -- rotate pulse trigger iSETPLS <= '0' ; elsif falling_edge(CLOCK) then case conv_integer(iDSTATE) is -- wait trigger when 0 => if ( iTRG = '1' ) then iDSTATE <= "01" ; else iDSTATE <= "00" ; end if ; -- deliver when 1 => iDSTATE <= "11" ; -- direction forword if ( iDADR = "0000" ) then iDIRF <= iDDAT(1 downto 0) ; end if ; -- forword duty if ( iDADR = "0001" ) then iDUTYF <= iDDAT(6 downto 0) ; end if ; -- direction reverse if ( iDADR = "0010" ) then iDIRR <= iDDAT(1 downto 0) ; end if ; -- reverse duty if ( iDADR = "0011" ) then iDUTYR <= iDDAT(6 downto 0) ; end if ; -- target line TOP if ( iDADR = "1011" ) then iTLTOP <= iDDAT ; end if ; -- target line MIDDLE if ( iDADR = "1100" ) then iTLMIDDLE <= iDDAT ; end if ; -- target line BOTTOM if ( iDADR = "1101" ) then iTLBOTTOM <= iDDAT ; end if ; -- clear distance pulse (upper) if ( iDADR = "1110" ) then iECNTX(15 downto 8) <= iDDAT ; iSETPLS <= '1' ; end if ; -- clear distance pulse (lower) if ( iDADR = "1111" ) then iECNTX( 7 downto 0) <= iDDAT ; iSETPLS <= '1' ; end if ; -- delay when 3 => iDSTATE <= "10" ; iSETPLS <= '0' ; -- return first state when 2 => iDSTATE <= "00" ; -- default when others => iDSTATE <= "00" ; end case ; end if ; end process ; -- motor control output FOUT <= iFOUT ; ROUT <= iROUT ; -- BUS interface data GP3 <= iDAT when ( GP2(5 downto 4) = "01" ) else (others => 'Z'); -- register file -- front direction 0 -- front duty 1 -- rear direction 2 -- rear duty 3 -- TOP distance 4 -- MIDDLE distance 5 -- BOTTOM distance 6 -- camera data 7 -- TOP 8 -- MIDDLE 9 -- BOTTOM 10 -- target line TOP 11 -- target line MIDDLE 12 -- target line BOTTOM 13 -- encoder counter(high) 14 -- encoder counter(low) 15 iDAT <= "000000" & iDIRF when ( GP2(3 downto 0) = "0000" ) else -- front direction '0' & iDUTYF when ( GP2(3 downto 0) = "0001" ) else -- front duty "000000" & iDIRR when ( GP2(3 downto 0) = "0010" ) else -- rear direction '0' & iDUTYR when ( GP2(3 downto 0) = "0011" ) else -- rear duty iLTOPD when ( GP2(3 downto 0) = "0100" ) else -- TOP distance iLMIDDLED when ( GP2(3 downto 0) = "0101" ) else -- MIDDLE distance iLBOTTOMD when ( GP2(3 downto 0) = "0110" ) else -- BOTTOM distance iDOB when ( GP2(3 downto 0) = "0111" ) else -- camera data iLTOP when ( GP2(3 downto 0) = "1000" ) else -- TOP iLMIDDLE when ( GP2(3 downto 0) = "1001" ) else -- MIDDLE iLBOTTOM when ( GP2(3 downto 0) = "1010" ) else -- BOTTOM iTLTOP when ( GP2(3 downto 0) = "1011" ) else -- target line TOP iTLMIDDLE when ( GP2(3 downto 0) = "1100" ) else -- target line MIDDLE iTLBOTTOM when ( GP2(3 downto 0) = "1101" ) else -- target line BOTTOM iECNT(15 downto 8) when ( GP2(3 downto 0) = "1110" ) else -- encoder counter(high) iECNT( 7 downto 0) when ( GP2(3 downto 0) = "1111" ) else -- encoder counter(low) X"FF" ; -- dummy -- pixel clock iPCLK <= HREF and PCLK ; -- synchronizer (100Hz sampling ) process (iSCLK) begin if rising_edge(iSCLK) then iVS_SFT <= iVS_SFT(0) & VS ; end if ; end process ; iVSTRG <= (not iVS_SFT(1)) and iVS_SFT(0) ; -- synchronizer (20MHz sampling ) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iPCLK_SFT <= "000" ; elsif rising_edge(CLOCK) then iPCLK_SFT <= iPCLK_SFT(1 downto 0) & iPCLK ; end if ; end process ; iPTRG <= '1' when ( iPCLK_SFT = "011" or iPCLK_SFT = "001" ) else '0' ; -- camera sequencer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iCSTATE <= 0 ; iLCNT <= 0 ; iPCNT <= 0 ; iCDATA <= (others => '0') ; iATRG_SFT <= "00" ; -- maximum iTMAX <= (others => '0') ; iMMAX <= (others => '0') ; iBMAX <= (others => '0') ; -- minimum iTMIN <= (others => '1') ; iMMIN <= (others => '1') ; iBMIN <= (others => '1') ; elsif rising_edge(CLOCK) then case iCSTATE is -- wait trigger from micom when 0 => if ( iCTRG = '1' ) then iCSTATE <= 1 ; -- next else iCSTATE <= 0 ; -- stay end if ; -- wait VSYNC trigger when 1 => if ( iVSTRG = '1' ) then iCSTATE <= 2 ; -- next iCDATA <= (others => '0') ; else iCSTATE <= 1 ; -- stay end if ; -- judge line counter when 2 => if ( iLCNT = LCNT_MAX ) then iCSTATE <= 7 ; -- exit else iCSTATE <= 3 ; -- loop end if ; -- store even data (Y) when 3 => if ( iPTRG = '1' ) then iCSTATE <= 4 ; -- next iCDATA <= CDAT ; iATRG_SFT <= iATRG_SFT(0) & '1' ; else iCSTATE <= 3 ; -- stay end if ; -- skip odd data (U or V) and store data when 4 => if ( iPTRG = '1' ) then iCSTATE <= 5 ; -- next iATRG_SFT <= iATRG_SFT(0) & '1' ; -- TOP line min , max if ( iTARGET_LT = '1' ) then -- min if ( iTMIN > iCDATA ) then iTMIN <= iCDATA ; end if ; -- max if ( iTMAX < iCDATA ) then iTMAX <= iCDATA ; end if ; end if ; -- MIDDLE line min , max if ( iTARGET_LM = '1' ) then -- min if ( iMMIN > iCDATA ) then iMMIN <= iCDATA ; end if ; -- max if ( iMMAX < iCDATA ) then iMMAX <= iCDATA ; end if ; end if ; -- BOTTOM line min , max if ( iTARGET_LB = '1' ) then -- min if ( iBMIN > iCDATA ) then iBMIN <= iCDATA ; end if ; -- max if ( iBMAX < iCDATA ) then iBMAX <= iCDATA ; end if ; end if ; else iCSTATE <= 4 ; -- state : 4 end if ; -- judge pixel counter when 5 => if ( iPCNT = PCNT_MAX ) then iCSTATE <= 6 ; -- state : 6 else iCSTATE <= 3 ; -- state : 3 iPCNT <= iPCNT + 1 ; end if ; iATRG_SFT <= "00" ; -- new line handling when 6 => iCSTATE <= 2 ; -- state : 2 iPCNT <= 0 ; iLCNT <= iLCNT + 1 ; -- clear line counter when 7 => iCSTATE <= 8 ; -- state : 8 -- clear line counter iLCNT <= 0 ; -- return catch trigger when 8 => iCSTATE <= 0 ; -- state : 0 -- default when others => iCSTATE <= 0 ; end case ; end if ; end process ; -- monitor iCSTATEX <= conv_std_logic_vector(iCSTATE,3) ; -- store Y data trigger iATRG <= iATRG_SFT(1) and iATRG_SFT(0) ; -- binary conversion iBTRG <= '1' when ( iCSTATE = 7 ) else '0' ; iTHVT <= (conv_integer(iTMAX) + conv_integer(iTMIN)) / 2 ; iTHVM <= (conv_integer(iMMAX) + conv_integer(iMMIN)) / 2 ; iTHVB <= (conv_integer(iBMAX) + conv_integer(iBMIN)) / 2 ; -- target line flag iTARGET_LT <= '1' when ( iLCNT = conv_integer(iTLTOP) ) else '0' ; iTARGET_LM <= '1' when ( iLCNT = conv_integer(iTLMIDDLE) ) else '0' ; iTARGET_LB <= '1' when ( iLCNT = conv_integer(iTLBOTTOM) ) else '0' ; -- A port sequencer (store Y signal) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iASTATE <= "000" ; iCADRA <= (others => '0') ; elsif rising_edge(CLOCK) then case conv_integer(iASTATE) is -- wait trigger when 0 => if ( iATRG = '1' and iTARGET_LT = '1' ) then iASTATE <= "001" ; -- state : 1 elsif ( iATRG = '1' and iTARGET_LM = '1' ) then iASTATE <= "001" ; -- state : 1 elsif ( iATRG = '1' and iTARGET_LB = '1' ) then iASTATE <= "001" ; -- state : 1 else iASTATE <= "000" ; -- state : 0 end if ; -- transfer data when 1 => iDIA <= iCDATA ; iASTATE <= "011" ; -- state : 3 -- send write trigger when 3 => iASTATE <= "111" ; -- state : 7 -- address increment when 7 => iCADRA <= iCADRA + '1' ; iASTATE <= "110" ; -- state : 6 -- judge when 6 => if ( conv_integer(iCADRA) = QMAX ) then iASTATE <= "100" ; -- state : 4 else iASTATE <= "000" ; -- state : 0 end if ; -- return first state when 4 => iASTATE <= "000" ; -- state : 0 iCADRA <= (others => '0') ; -- default when others => iASTATE <= "000" ; end case ; end if ; end process ; -- camera data store trigger iWEA <= '1' when ( iASTATE = "011" ) else '0' ; -- B port sequencer (store binary code) process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iBSTATE <= "0000" ; -- address iCADRB <= (others => '0') ; iBADRA <= (others => '0') ; -- data iBDAT <= (others => '0') ; iBDIA <= (others => '0') ; -- flag iBFLAG <= 0 ; elsif rising_edge(CLOCK) then case conv_integer(iBSTATE) is -- wait trigger when 0 => if ( iBTRG = '1' ) then iBSTATE <= "0001" ; -- next else iBSTATE <= "0000" ; -- stay end if ; -- initialize when 1 => iBSTATE <= "0010" ; -- next iBCNT <= 0 ; -- get data when 2 => iBSTATE <= "0011" ; -- next iBDAT <= iDOB ; iBCNT <= iBCNT + 1 ; -- increment counter -- address increment when 3 => iBSTATE <= "0100" ; -- next iCADRB <= iCADRB + 1 ; iBDIA <= X"00" ; -- compare when 4 => iBSTATE <= "0101" ; -- next -- TOP if ( iBFLAG = 0 ) then if ( conv_integer(iBDAT) > iTHVT ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- MIDDLE if ( iBFLAG = 1 ) then if ( conv_integer(iBDAT) > iTHVM ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- BOTTOM if ( iBFLAG = 2 ) then if ( conv_integer(iBDAT) > iTHVB ) then iBDIA <= X"01" ; else iBDIA <= X"00" ; end if ; end if ; -- store when 5 => iBSTATE <= "0110" ; -- next -- address increment when 6 => iBSTATE <= "0111" ; -- next iBADRA <= iBADRA + '1' ; -- judge when 7 => if ( iBCNT = PCNT_MAX ) then iBSTATE <= "1000" ; -- next else iBSTATE <= "0010" ; -- loop end if ; -- flag increment when 8 => iBSTATE <= "1001" ; -- next iBFLAG <= iBFLAG + 1 ; -- update iBCNT <= 0 ; -- judge when 9 => if ( iBFLAG = 3 ) then iBSTATE <= "1010" ; -- next else iBSTATE <= "0000" ; -- loop end if ; -- complete when 10 => iBSTATE <= "0000" ; -- first state iBFLAG <= 0 ; -- clear flag -- initialize address iCADRB <= (others => '0') ; iBADRA <= (others => '0') ; -- default when others => iBSTATE <= "0000" ; end case ; end if ; end process ; -- binary data store trigger iBWEA <= '1' when ( iBSTATE = "0101" ) else '0' ; -- generate parameters trigger iWTRG <= '1' when ( iBSTATE = "1010" ) else '0' ; -- last sequencer process (nRESET,CLOCK) begin if ( nRESET = '0' ) then iWSTATE <= "0000" ; -- address iBADRB <= (others => '0') ; -- data iWNOW <= 0 ; iWNEXT <= 0 ; -- result iLTOP <= (others => '1') ; iLMIDDLE <= (others => '1') ; iLBOTTOM <= (others => '1') ; iLTOPD <= (others => '1') ; iLMIDDLED <= (others => '1') ; iLBOTTOMD <= (others => '1') ; -- counter iWCNT <= 0 ; -- Zero -> Plus iWZP <= 255 ; -- Plus -> Zero iWPZ <= 255 ; elsif rising_edge(CLOCK) then case conv_integer(iWSTATE) is -- wait trigger when 0 => if ( iWTRG = '1' ) then iWSTATE <= "0001" ; -- next -- address iBADRB <= (others => '0') ; -- address iWFLAG <= 0 ; else iWSTATE <= "0000" ; -- stay end if ; -- initialize when 1 => iWSTATE <= "0010" ; -- next -- counter iWCNT <= 0 ; -- Zero -> Plus iWZP <= 255 ; -- Plus -> Zero iWPZ <= 255 ; -- iNOW when 2 => iWSTATE <= "0011" ; -- data iWNOW <= conv_integer(iBDOB) ; -- counter iWCNT <= iWCNT + 1 ; -- address increment when 3 => iWSTATE <= "0100" ; -- next -- address iBADRB <= iBADRB + '1' ; -- iNEXT when 4 => iWSTATE <= "0101" ; -- next -- data iWNEXT <= conv_integer(iBDOB) ; -- counter iWCNT <= iWCNT + 1 ; -- address increment when 5 => iWSTATE <= "0110" ; -- next -- address iBADRB <= iBADRB + '1' ; -- compare when 6 => iWSTATE <= "0111" ; -- next -- Zero -> Plus if ( iWNOW = 0 and iWNEXT = 1 ) then iWZP <= iWCNT - 2 ; end if ; -- Plus -> Zero if ( iWNOW = 0 and iWNEXT = 1 ) then iWPZ <= iWCNT - 2 ; end if ; -- copy when 7 => iWSTATE <= "1000" ; -- next iWNOW <= iWNEXT ; -- judge when 8 => if ( iWCNT = 160 ) then iWSTATE <= "1001" ; -- next else iWSTATE <= "0100" ; -- loop end if ; -- calculate when 9 => iWSTATE <= "1010" ; -- next -- all WHITE or all BLACK if ( iWZP = 255 and iWPZ = 255 ) then -- distance iLTOPD <= (others => '0') ; iLMIDDLED <= (others => '0') ; iLBOTTOMD <= (others => '0') ; -- location if ( iWNOW = 1 ) then -- TOP if ( iWFLAG = 0 ) then iLTOP <= conv_std_logic_vector(160,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then iLMIDDLE <= conv_std_logic_vector(160,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then iLBOTTOM <= conv_std_logic_vector(160,8) ; end if ; else -- TOP if ( iWFLAG = 0 ) then iLTOP <= (others => '0') ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then iLMIDDLE <= (others => '0') ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then iLBOTTOM <= (others => '0') ; end if ; end if ; end if ; -- left WHITE if ( iWZP = 255 and iWPZ < 160 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= (others => '0') ; -- distance iLTOPD <= conv_std_logic_vector(iWPZ,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= (others => '0') ; -- distance iLMIDDLED <= conv_std_logic_vector(iWPZ,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= (others => '0') ; -- distance iLBOTTOMD <= conv_std_logic_vector(iWPZ,8) ; end if ; end if ; -- right WHITE if ( iWZP < 160 and iWPZ = 255 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= conv_std_logic_vector(160,8) ; -- distance iLTOPD <= conv_std_logic_vector(iWZP,8) ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= conv_std_logic_vector(160,8) ; -- distance iLMIDDLED <= conv_std_logic_vector(iWZP,8) ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= conv_std_logic_vector(160,8) ; -- distance iLBOTTOMD <= conv_std_logic_vector(iWZP,8) ; end if ; end if ; -- if ( iWZP < 160 and iWPZ < 160 ) then -- TOP if ( iWFLAG = 0 ) then -- location iLTOP <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLTOPD <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLTOPD <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; -- MIDDLE if ( iWFLAG = 1 ) then -- location iLMIDDLE <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLMIDDLED <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLMIDDLED <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; -- BOTTOM if ( iWFLAG = 2 ) then -- location iLBOTTOM <= conv_std_logic_vector((iWZP + iWPZ) / 2,8) ; -- distance if ( iWZP < iWPZ ) then iLBOTTOMD <= conv_std_logic_vector(iWPZ - iWZP,8) ; else iLBOTTOMD <= conv_std_logic_vector(iWZP - iWPZ,8) ; end if ; end if ; end if ; -- flag increment when 10 => iWSTATE <= "1011" ; -- next iWFLAG <= iWFLAG + 1 ; -- flag increment when 11 => if ( iWFLAG = 3 ) then iWSTATE <= "0000" ; -- first iWFLAG <= 0 ; else iWSTATE <= "0001" ; -- loop end if ; -- default when others => iWSTATE <= "0000" ; end case ; end if ; end process ; -- encoder counter process (nRESET,iSCLK) begin if ( nRESET = '0' ) then iENPLS_SFT <= "000" ; elsif rising_edge(iSCLK) then iENPLS_SFT <= iENPLS_SFT(1 downto 0) & ENPULSE ; end if ; end process ; iENPLS <= '1' when ( iENPLS_SFT = "011" ) else '0' ; process (nRESET,iSETPLS,iSCLK,iECNTX) begin if ( nRESET = '0' ) then iECNT <= (others => '0') ; elsif ( iSETPLS = '1' ) then iECNT <= iECNTX ; elsif rising_edge(iSCLK) then if ( iENPLS = '1' ) then iECNT <= iECNT + '1' ; end if ; end if ; end process ; end Behavioral;  VHDLコードの中に、内部動作確認用のモニタ信号を  定義しています。  内部シーケンサのステートとトリガーを、オシロスコープや  周波数カウンタで観測できます。また、OV7670の出力信号を  モニタしています。  これらの信号をFPGA外に出すと、内部動作が設計  通りかを、確認できました。  今回はオシロスコープを使はず、8ビットのLED基板  とマルチメータの周波数カウンタで動作を確認でき  ました。

ピンアサイン

 VHDLコードに対応させるピンアサインは  次のように定義しています。 NET "CLOCK" LOC = "P36" ; NET "nRESET" LOC = "P50" ; # G0 (motor control) NET "FOUT<0>" LOC = "P1" | DRIVE = 8 | SLEW = SLOW ; NET "FOUT<1>" LOC = "P2" | DRIVE = 8 | SLEW = SLOW ; NET "ROUT<0>" LOC = "P4" | DRIVE = 8 | SLEW = SLOW ; NET "ROUT<1>" LOC = "P5" | DRIVE = 8 | SLEW = SLOW ; NET "ENPULSE" LOC = "P11" ; # G1 (camera data) NET "CDAT<0>" LOC = "P13" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<1>" LOC = "P14" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<2>" LOC = "P15" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<3>" LOC = "P16" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<4>" LOC = "P17" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<5>" LOC = "P21" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<6>" LOC = "P22" | DRIVE = 8 | SLEW = SLOW ; NET "CDAT<7>" LOC = "P23" | DRIVE = 8 | SLEW = SLOW ; # group G2 (monitor signal output) NET "STATEX<0>" LOC = "P27" | DRIVE = 8 | SLEW = SLOW ; NET "STATEX<1>" LOC = "P28" | DRIVE = 8 | SLEW = SLOW ; NET "STATEX<2>" LOC = "P30" | DRIVE = 8 | SLEW = SLOW ; NET "ATRGX" LOC = "P32" | DRIVE = 8 | SLEW = SLOW ; NET "BTRGX" LOC = "P34" | DRIVE = 8 | SLEW = SLOW ; NET "WTRGX" LOC = "P35" | DRIVE = 8 | SLEW = SLOW ; NET "HPCLK" LOC = "P53" | DRIVE = 8 | SLEW = SLOW ; NET "MVS" LOC = "P54" | DRIVE = 8 | SLEW = SLOW ; # Bank 4 (2.5V) G3 NET "GLED" LOC = "P47" | DRIVE = 8 | SLEW = SLOW ; NET "RLED" LOC = "P49" | DRIVE = 8 | SLEW = SLOW ; # G3 (BUS data) NET "GP3<0>" LOC = "P55" ; NET "GP3<1>" LOC = "P59" ; NET "GP3<2>" LOC = "P60" ; NET "GP3<3>" LOC = "P61" ; NET "GP3<4>" LOC = "P62" ; NET "GP3<5>" LOC = "P63" ; NET "GP3<6>" LOC = "P64" ; NET "GP3<7>" LOC = "P65" ; # G4 NET "GP2<0>" LOC = "P67" ; NET "GP2<1>" LOC = "P68" ; NET "GP2<2>" LOC = "P71" ; NET "GP2<3>" LOC = "P72" ; NET "GP2<4>" LOC = "P74" ; NET "GP2<5>" LOC = "P75" ; NET "GP2<6>" LOC = "P79" ; NET "GP2<7>" LOC = "P80" ; # G5 (camera synchronous signal) NET "PCLK" LOC = "P81" ; NET "HREF" LOC = "P85" ; NET "VS" LOC = "P86" ;

デバッグ履歴

 画像処理回路は、最初からうまく動いたわけ  ではありません。OV7670仕様の思違い、内部  シーケンサの動作不良を、信号をモニターし  デバッグしました。その履歴を備忘録として  記しておきます。 垂直同期信号  OV7670は、VGAフォーマットで画像データを  出力できます。フレームレートは30なので  VSYNCは、30Hz程度になります。  OV7670内部レジスタのパラメータ設定で、VSYNCを  30Hzから下げられます。周波数が下がっているかを  確認するため、VSYNCを入力すると同時にバッファ  を入れて、外部で周波数を観測できるようにしました。  OV7670は電源電圧が3Vなので、利用している周波数  カウンタで信号を捕まえられるか、心配でした。  FPGA内にバッファを用意し、3.3V電源で信号の振幅を  嵩上げすることになり、周波数カウンタを動かす電圧  レベルを確保できました。  ファームウエアで設定するパラメータにより、VSYNCの  周波数は12.5Hzとしました。 ピクセルクロック  OV7670には、20MHzのクロックを与えています。  ピクセルクロックはPCLKとして定義され、この  クロックに同期して、データが出力されます。  PCLKは、OV7670内部のPLLを利用し、動作クロックを  てい倍すると、変えられます。デフォルト処理でどの  程度の周波数になるかを、モニタできるように信号を  バッファリングして出力します。  有効データの出力中は、HREFの論理値で示されるので  この信号とANDをとった後、FPGAの外に出しました。  デフォルトでは、5MHz近くとなりました。  FPGAは20MHz動作なので、PCLKとHREFの合成信号を  トリガーとして利用できると判断できました。  シフトレジスタでrising_edgeを捕らえて、トリガー  にします。このトリガーを、シーケンサに与えます。 画像格納シーケンサ  画像データをFPGA内のメモリへ保存するには  シーケンサを使います。シーケンサの動作を  確認できれば、メモリへの保存は実現できて  いると判断できます。  シーケンサは、カウンタを使って実現するので  カウンタ値を、FPGA外部に引出して、観測可能  とします。  シーケンサを使う場合、ハザードがでないように  しないと、誤動作につながります。画像データを  メモリに保存する動作を担当するシーケンサには  ジョンソンカウンタベースにします。  データをメモリに保存する回路は、ジョンソンカウンタ  で動かし、シーケンサをバイナリーカウンタベースとし  記述しました。  シーケンサをバイナリーカウンタベースとするのは  レジスタ使用効率を高くして、利用するゲート数を  減らすためです。 2値化シーケンサ  最初、画像データ保存後、次ラインのデータを  扱うときに、2値化の処理を入れていました。  データの保存と処理を同時に実行すると、2値化の  シーケンサがまったく動きません。  シーケンサ起動用トリガーが出ていませんでした。  調べると、データ保存した次のラインであることを  示すデコード信号とトリガーの合成で作った指示を  2値化シーケンサが受け取っていません。  2つのシーケンサでトリガーをやり取りするので  片側がクロックに同期し、トリガーを出すならば  1クロック遅れて、他方のシーケンサが受け取り  ます。シーケンサのクロックは同一でも、内部の  配置により、スキューが発生するため、トリガー  を捕らえられないと判断しました。  ターゲットラインのデータを全部メモリに保存後  トリガーを与える仕様に変更しても、問題は発生  しません。この仕様に変更し、シーケンサの動作  を確認できました。 センサー情報生成シーケンサ  2値化ができていれば、センサー情報の生成は  各ラインデータの変化点(0→1か1→0)を  探すことが中心になります。  2値化が終了した時点で、センサー情報生成の  シーケンサにトリガーを与えて、動かせばよい  と判断しました。  シーケンサを使うとき、トリガーはひとつにして  一気に動かすのが定石です。その定石に従うこと  として、2値化用シーケンサに他のシーケンサに  トリガーを出力するステートを入れて対応です。 画像処理タイミング  画像処理は、ファームウエアで1度起動すると  リセットボタンを押すまで、自動継続する仕様  としました。これだと、必要なセンサー情報を  生成できません。FPGAが勝手に画像処理しない  ように、ファームウエアからセンサー情報生成  のトリガーを与えるようにしました。  ファームウエアからのトリガーで、センサー情報を  生成するためには、画像格納シーケンサへトリガー  を与え、2値化、センサー情報生成は、FPGA内部の  回路に任せます。  FPGA内部には、3シーケンサがあるので、画像格納  シーケンサが、2値化シーケンサを起動します。  2値化シーケンサはセンサー情報生成シーケンサを  起動します。ドミノ倒し方式で、必要な情報を生成  する仕様で、ファームウエアがトリガーを与えた後  50ms程度で、必要な情報を生成できました。
目次

inserted by FC2 system