最終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は、次のようにビット割当てしました。
- GP27 CENA
- GP26 TRG
- GP25 WE
- GP24 OE
- GP23 RSEL3
- GP22 RSEL2
- GP21 RSEL1
- GP20 RSEL0
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コードの中に、内部動作確認用のモニタ信号を
定義しています。
- STATEX
- ATRGX
- BTRGX
- WTRGX
- MVS
- HPCLK
内部シーケンサのステートとトリガーを、オシロスコープや
周波数カウンタで観測できます。また、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程度で、必要な情報を生成できました。
目次
前
次