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