目次
前
次
マルチPLD構想
半年間の大学での非常勤講師の仕事が終了したので
1週間ほどMCR-VCに集中できるようになりました。
大学では、シリアルインタフェースとサーボモータを
VHDLコードで動かしていました。
密着型センサーは、3.3Vで動かせるので、手持ちのCPLDが
2種載っているボードで、MCR-VCマシンを動かしてみます。
2種のCPLDは、次のように利用します。
CoolRunnerIIは、路面センサー(密着方センサー)と
ロータリーエンコーダからの情報で、左右のモータの
DUTY比を計算します。
計算したDUTY比をケーブルで、もう1個のCPLDに転送
します。
XC9572XLは、スタートトリガを貰い、CoolRunnerIIに
報告します。また、与えられた左右のDUTY比でモータ
の回転を制御します。
ハードウエアに近い部分に、XC9572XLを配置します。
XC9572XL内部処理
XC9572XLに接続する回路は、以下です。
PWM波形は、2つのMOSFETドライバフォトカプラに接続します。
スタートトリガーを、プッシュスイッチに接続します。
スイッチを押すと、論理値のLを出力します。
2つのLEDは、次のように使います。
緑 CoolRunnerIIに対し、DUTY比の設定で速度制御ができる
状態にあることを知らせます。同時に、ユーザーに移動
できる状態を提示します。
赤 CoolRunnerIIから、クランク、レーンチェンジの白線を
検出して移動していることを知らせます。
これらの回路は、写真のボードに実装されている部品を
利用します。
スタートトリガーを扱うVHDLコードを考えます。
プッシュスイッチは、チャタリングがあるので、シフトレジスタ
を利用したチャタリング除去回路を定義します。
プッシュスイッチは、負論理で動いているので
正論理に変換します。
iSSTRG <= not SSTRG ;
シフトレジスタを利用して、正論理で立上りエッジを
とらえて、トリガーにします。
process (nRESET,iTCLK)
begin
if ( nRESET = '0' ) then
iSSTRG_SFT <= "000" ;
elsif rising_edge( iTCLK ) then
iSSTRG_SFT <= iSSTRG_SFT(1 downto 0) & iSSTRG ;
end if ;
end process ;
iSETRG <= '1' when ( iSSTRG_SFT = "011" or iSSTRG_SFT = "001" ) else '0' ;
トリガーを利用して、シーケンサを動かします。
Johnsonカウンタを、トリガーを利用し、動かしたり
止めたりするようにします。
2ビットのJohnsonカウンタを利用すると、デコード
が単純になるのと、ハザードを出しません。
VHDLコードは、非常に単純です。
process (nRESET,iTCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
elsif rising_edge( iTCLK ) then
case conv_integer(iSTATE) is
-- wait start trigger
when 0 => if ( iSETRG = '1' ) then
iSTATE <= "01" ;
end if ;
when 1 => iSTATE <= "11" ;
-- wait exit trigger
when 3 => if ( iSETRG = '1' ) then
iSTATE <= "10" ;
end if ;
-- return first state
when 2 => iSTATE <= "01" ;
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
iRUNFLAG <= iSTATE(0) ;
RUNFLAGは、CoolRunnerIIに渡す信号です。
チャタリング除去とシーケンサを回すためのクロックは
100Hz程度であればよく、PWM波形を生成するには10kHz
ほどになります。
個別に定義するのは、面倒なので、クロックジェネレータ
を一つ定義して、使い回します。
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;
PWM波形を生成するには、0〜99を設定するレジスタと
内部カウンタの値と比較するコンパレータがあれば充分
です。
今回は、2チャネルだけなので、専用のコードを定義
して対応します。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pwmx is
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- input
PRATER : in std_logic_vector(6 downto 0) ;
PRATEL : in std_logic_vector(6 downto 0) ;
-- output
POUTR : out std_logic ;
POUTL : out std_logic --;
);
end pwmx ;
architecture Behavioral of pwmx is
signal iPCNT : integer range 0 to 100 ;
signal iPOUT : std_logic_vector(1 downto 0) ;
signal iPRATER : std_logic_vector(6 downto 0) ;
signal iPRATEL : std_logic_vector(6 downto 0) ;
begin
-- output
POUTR <= not iPOUT(0) ;
POUTL <= not iPOUT(1) ;
-- comparetor
iPOUT(0) <= '1' when ( iPCNT < conv_integer( iPRATER ) ) else '0' ;
iPOUT(1) <= '1' when ( iPCNT < conv_integer( iPRATEL ) ) else '0' ;
-- counter
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPCNT <= 0 ;
iPRATER <= "0000000" ;
iPRATEL <= "0000000" ;
elsif rising_edge( CLOCK ) then
if ( iPCNT = 100 ) then
iPCNT <= 0 ;
iPRATER <= PRATER ;
iPRATEL <= PRATEL ;
else
iPCNT <= iPCNT + 1 ;
end if ;
end if ;
end process ;
end Behavioral;
PWM波形のDUTY比を与えると、波形を乱さないように
内部カウンタの値が0のときに、再設定します。
また、出力は負論理としています。
DUTY比は、CoolRunnerから出力されるので、次の仕様で
入力します。
- 0〜99までなので、8ビット中の7ビットを使い指定
- 8ビットの最上位ビットが1だと左モータのDUTY比とする
- 8ビットの最上位ビットが0だと右モータのDUTY比とする
- CoolRunnerの出力するトリガーをキャッチしてDUTY比設定
- シェイクハンドはしない
トリガーのキャッチは、チャタリング除去と同様
シフトレジスタを利用します。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iDTRG_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iDTRG_SFT <= iDTRG_SFT(1 downto 0) & DTRG ;
end if ;
end process ;
iDTRG <= '1' when ( iDTRG_SFT = "011" or iDTRG_SFT = "001" ) else '0' ;
トリガーを利用して、7ビットのDUTY比を内部
レジスタにラッチ(記憶)します。
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPRATER <= "0000000" ;
iPRATEL <= "0000000" ;
elsif falling_edge(CLOCK) then
if ( iDTRG = '1' ) then
-- left duty ratio
if ( DUTY(7) = '1' ) then
iPRATEL <= DUTY(6 downto 0) ;
else
-- right duty ratio
iPRATER <= DUTY(6 downto 0) ;
end if ;
end if ;
end if ;
end process ;
方向設定の指定、白線クロスなどの信号を
含め、全体は以下としました。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mdrivex is
generic (
TOPX : integer := 7 ;
XMAX : integer := 92 ;
TOPT : integer := 6 ;
TMAX : integer := 50 --;
) ;
port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- monitor
MCLK : out std_logic ;
-- interface
RUNFLAG : out std_logic ;
HTURN : out std_logic ;
CWHITE : in std_logic ;
DTRG : in std_logic ;
DUTY : in std_logic_vector(7 downto 0) ;
-- leg system
SSTRG : in std_logic ;
TURN : in std_logic ;
POUTR : out std_logic ;
POUTL : out std_logic ;
GLED : out std_logic ;
RLED : out std_logic --;
) ;
end mdrivex;
architecture Behavioral of mdrivex is
-- 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 ;
-- internal clock
signal iMCLK : std_logic ;
signal iTCLK : std_logic ;
-- PWM
component pwmx is
port(
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- input
PRATER : in std_logic_vector(6 downto 0) ;
PRATEL : in std_logic_vector(6 downto 0) ;
-- output
POUTR : out std_logic ;
POUTL : out std_logic --;
);
end component ;
-- internal register
signal iPRATER : std_logic_vector(6 downto 0) ;
signal iPRATEL : std_logic_vector(6 downto 0) ;
signal iPOUTR : std_logic ;
signal iPOUTL : std_logic ;
signal iTURN : std_logic ;
-- trigger
signal iSSTRG : std_logic ;
signal iSSTRG_SFT : std_logic_vector(2 downto 0) ;
signal iSETRG : std_logic ;
-- state machine
signal iSTATE : std_logic_vector(1 downto 0) ;
signal iRUNFLAG : std_logic ;
-- duty ratio
signal iDTRG : std_logic ;
signal iDTRG_SFT : std_logic_vector(2 downto 0) ;
begin
-- clock generator
CLKX : clkgenx generic map (TOPX,XMAX) port map (nRESET,CLOCK,iMCLK) ;
CLKT : clkgenx generic map (TOPT,TMAX) port map (nRESET,iMCLK,iTCLK) ;
-- PWM
PWM : pwmx port map (nRESET,iMCLK,iPRATER,iPRATEL,iPOUTR,iPOUTL);
-- input
iSSTRG <= not SSTRG ;
iTURN <= not TURN ;
-- output
MCLK <= iTCLK ;
RLED <= not CWHITE ;
GLED <= not iRUNFLAG ;
RUNFLAG <= iRUNFLAG ;
POUTR <= iPOUTR when ( iRUNFLAG = '1' ) else '1' ;
POUTL <= iPOUTL when ( iRUNFLAG = '1' ) else '1' ;
HTURN <= iTURN ;
-- trigger handling
process (nRESET,iTCLK)
begin
if ( nRESET = '0' ) then
iSSTRG_SFT <= "000" ;
elsif rising_edge( iTCLK ) then
iSSTRG_SFT <= iSSTRG_SFT(1 downto 0) & iSSTRG ;
end if ;
end process ;
iSETRG <= '1' when ( iSSTRG_SFT = "011" or iSSTRG_SFT = "001" ) else '0' ;
-- state machine
process (nRESET,iTCLK)
begin
if ( nRESET = '0' ) then
iSTATE <= "00" ;
elsif rising_edge( iTCLK ) then
case conv_integer(iSTATE) is
-- wait start trigger
when 0 => if ( iSETRG = '1' ) then
iSTATE <= "01" ;
end if ;
when 1 => iSTATE <= "11" ;
-- wait exit trigger
when 3 => if ( iSETRG = '1' ) then
iSTATE <= "10" ;
end if ;
-- return first state
when 2 => iSTATE <= "01" ;
when others =>
iSTATE <= "00" ;
end case ;
end if ;
end process ;
iRUNFLAG <= iSTATE(0) ;
-- debouncing
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iDTRG_SFT <= "000" ;
elsif rising_edge(CLOCK) then
iDTRG_SFT <= iDTRG_SFT(1 downto 0) & DTRG ;
end if ;
end process ;
iDTRG <= '1' when ( iDTRG_SFT = "011" or iDTRG_SFT = "001" ) else '0' ;
-- duty ratio trigger
process (nRESET,CLOCK)
begin
if ( nRESET = '0' ) then
iPRATER <= "0000000" ;
iPRATEL <= "0000000" ;
elsif falling_edge(CLOCK) then
if ( iDTRG = '1' ) then
-- left duty ratio
if ( DUTY(7) = '1' ) then
iPRATEL <= DUTY(6 downto 0) ;
else
-- right duty ratio
iPRATER <= DUTY(6 downto 0) ;
end if ;
end if ;
end if ;
end process ;
end Behavioral;
ケーブルでCoolRunnerIIと接続するために
ピンアサインを指定します。
# system
NET "MCLK" LOC = "P44" ;
NET "nRESET" LOC = "P33" ;
NET "CLOCK" LOC = "P1" ;
# group A
NET "POUTR" LOC = P43 ;
NET "POUTL" LOC = P41 ;
NET "SSTRG" LOC = P39 ;
NET "TURN" LOC = P38 ;
NET "GLED" LOC = P37 ;
NET "RLED" LOC = P36 ;
# group B
NET "DUTY<0>" LOC = P34 ;
NET "DUTY<1>" LOC = P32 ;
NET "DUTY<2>" LOC = P31 ;
NET "DUTY<3>" LOC = P30 ;
NET "DUTY<4>" LOC = P29 ;
NET "DUTY<5>" LOC = P28 ;
NET "DUTY<6>" LOC = P27 ;
NET "DUTY<7>" LOC = P23 ;
# group C(7bits)
NET "RUNFLAG" LOC = P22 ;
NET "HTURN" LOC = P21 ;
NET "CWHITE" LOC = P20 ;
CoolRunnerII内部処理
CoolRunnerIIには、次の機能を担当させます。
- センサー取得の路面情報を8個のLEDに表示
- センサー情報から、左右のDUTY比算出
- 算出したDUTY比を他のデバイスに転送
センサー情報から、左右のDUTY比を算出するには
「もし〜ならば...とする」方式で指定します。
センサー情報は、クランク、レーンチェンジでは
普通走行とは異なる解釈しなければならないので
モードを用意して対応します。
モードは、次の4種類にしました。
- NORMAL
- CRANK
- LANE_RIGHT
- LANE_LEFT
センサーから情報入手後、モードを見てDUTY比を
設定するシーケンサを定義します。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iJSTATE <= "000" ;
iJRDUTY <= 0 ; -- right duty
iJLDUTY <= 0 ; -- left duty
iJRDUTYX <= 0 ; -- right duty
iJLDUTYX <= 0 ; -- left duty
iJCNT <= 0 ; -- delay counter
iMODE <= 0 ; -- NORMAL 0 / LANE_RIGHT 1 / LANE_LEFT 2 / CRANK 3
elsif rising_edge(iMCLK) then
if ( iJTRG = '1' ) then
case conv_integer(iJSTATE) is
-- get sensor data
when 0 => iJSTATE <= "001" ;
iSENSOR <= iJSENSOR ;
-- convert
when 1 => iJSTATE <= "011" ;
case conv_integer(iJOUT) is
-- all_black
when 0 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100000 ; -- 1000ms = 100000 x 10us
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 100000 ; -- 1000ms = 100000 x 10us
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
else
iJRDUTYX <= 0 ; -- right duty
iJLDUTYX <= 0 ; -- left duty
end if ;
-- tiny_right
when 1 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 20 ; -- right duty
iJLDUTYX <= 27 ; -- left duty
iJCNT <= 25 ;
else
iJRDUTYX <= 20 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 23 ;
end if ;
-- right
when 2 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 20 ;
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 22 ;
end if ;
-- big_right
when 3 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 30 ; -- left duty
iJCNT <= 15 ;
else
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 30 ; -- left duty
iJCNT <= 17 ;
end if ;
-- center
when 4 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 35 ; -- left duty
iJCNT <= 100000 ;
iMODE <= 0 ; -- change normal
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 35 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100000 ;
iMODE <= 0 ; -- change normal
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 5 ;
else
iJRDUTYX <= 35 ; -- right duty
iJLDUTYX <= 35 ; -- left duty
iJCNT <= 10 ;
end if ;
-- tiny_left
when 5 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 20 ; -- left duty
iJCNT <= 25 ;
else
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 20 ; -- left duty
iJCNT <= 27 ;
end if ;
-- left
when 6 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 10 ; -- left duty
iJCNT <= 20 ;
else
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 23 ;
end if ;
-- big_left
when 7 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 30 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 15 ;
else
iJRDUTYX <= 30 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 17 ;
end if ;
-- right_white
when 8 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 45 ;
iMODE <= 0 ; -- change NORMAL
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 1 ; -- change LANE (right)
end if ;
-- left_white
when 9 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 45 ;
iMODE <= 0 ; -- change NORMAL
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 2 ; -- change LANE (left)
end if ;
-- all_white
when 10 => if ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100 ;
elsif ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 3 ; -- change CRANK
else -- LANE_LEFT / LANE_RIGHT
end if ;
when others => NULL ;
end case ;
-- latch
when 3 => iJSTATE <= "111" ;
iJRDUTY <= iJRDUTYX ; -- right duty
iJLDUTY <= iJLDUTYX ; -- left duty
-- delay
when 7 => if ( iJCNT = 0 ) then
iJSTATE <= "110" ;
else
iJCNT <= iJCNT - 1 ;
end if ;
-- dummy (auto skip)
when 6 => iJSTATE <= "100" ;
-- return first state
when 4 => iJSTATE <= "000" ;
-- default
when others =>
iJSTATE <= "000" ;
end case ;
else
iJRDUTY <= 0 ; -- right duty
iJLDUTY <= 0 ; -- left duty
end if ;
end if ;
end process ;
11個のセンサー情報と4モードの組合せから
DUTY比を設定しています。更に、モードにより
遅延時間を変えています。
DUTY比を設定後、他のブロックへの転送処理は
専用シーケンサを用意して対応します。
専用シーケンサは、Johnsonカウンタを利用して
Blind_RunかJudge_Runを判定しています。
(Blind_Runは、スタート直後の直進を表現します。
Judge_Runは、センサーを利用した走行です。)
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iDSTATE <= "00" ;
iDUTY <= (others => '1') ;
elsif rising_edge(iMCLK) then
case conv_integer(iDSTATE) is
-- get left duty
when 0 => iDSTATE <= "01" ;
if ( iPSTATE = "011" ) then
iDUTY <= '1' & conv_std_logic_vector(iBDUTY,7) ;
else
iDUTY <= '1' & conv_std_logic_vector(iJLDUTY,7) ;
end if ;
-- send trigger
when 1 => iDSTATE <= "11" ;
-- get right duty
when 3 => iDSTATE <= "10" ;
if ( iPSTATE = "011" ) then
iDUTY <= '0' & conv_std_logic_vector(iBDUTY,7) ;
else
iDUTY <= '0' & conv_std_logic_vector(iJRDUTY,7) ;
end if ;
-- send trigger and return first state
when 2 => iDSTATE <= "00" ;
-- default
when others =>
iDSTATE <= "00" ;
end case ;
end if ;
end process ;
iTRG <= '1' when ( iDSTATE = "01" or iDSTATE = "10" ) else '0' ;
センサーから得られる情報は、8ビット=256パターンありますが
すべて利用するのは、面倒なので10パターンに集約させます。
パターンから出力される状態判定値は、次のようにしました。
- すべて黒 all_black
- 少し右 tiny_right
- 右 right
- 大きく右 big_right
- 中央 center
- 少し左 tiny_left
- 左 left
- 大きく左 big_left
- 左半分白 left_white
- 右半分白 right_white
- すべて白 all_white
256パターンから、11個の状態を抽出するコードを定義します。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity judge is
port (
-- input
DIN : in std_logic_vector(7 downto 0) ;
-- output
JOUT : out std_logic_vector(3 downto 0) --;
);
end judge;
architecture Behavioral of judge is
signal iJOUT : std_logic_vector(3 downto 0);
begin
-- output
JOUT <= iJOUT ;
-- convert
iJOUT <= "0000" when ( DIN = X"00" ) else -- all_black
"0001" when ( DIN = X"0C" ) else -- tiny_right
"0010" when ( DIN = X"06" ) else -- right
"0011" when ( DIN = X"0E" ) else -- big_right
"0100" when ( DIN = X"18" ) else -- center
"0100" when ( DIN = X"1C" ) else -- center
"0100" when ( DIN = X"38" ) else -- center
"0101" when ( DIN = X"30" ) else -- tiny_left
"0110" when ( DIN = X"60" ) else -- left
"0111" when ( DIN = X"C0" ) else -- big_left
"1000" when ( DIN = X"E0" ) else -- left_white
"1000" when ( DIN = X"F0" ) else -- left_white
"1000" when ( DIN = X"F8" ) else -- left_white
"1001" when ( DIN = X"07" ) else -- right_white
"1001" when ( DIN = X"0F" ) else -- right_white
"1001" when ( DIN = X"1F" ) else -- right_white
"1010" when ( DIN = X"FF" ) else -- all_white
"1111" ;
end Behavioral;
プリント基板に実装している制御ボードには、センサー情報を
モニタする他に、8個のLEDがあります。この8個のLEDを使い
内部の状態を表示して、テスト、デバッグに役立てます。
8個のLEDを上位、下位の各4ビットずつに状態を割当てます。
下位4ビットは、スタート直後の直進におけるDUTY比を
モニタします。(0消灯、1点灯)
- 1000 DUTY比 5%
- 0100 DUTY比 15%
- 0010 DUTY比 30%
- 0001 DUTY比 50%
- 0000 Blind_Runではない
上位4ビットは、走行モードを表します。(0消灯、1点灯)
- 1000 NORMAL
- 0100 CRANK
- 0010 LANE_RIGHT
- 0001 LANE_LEFT
単純な組合せ回路でよいので、VHDLコードは次のように指定しました。
iSTATUS(3 downto 0) <= "1000" when ( iBSTATE = 1 ) else -- duty 5%
"0100" when ( iBSTATE = 3 ) else -- duty 15%
"0010" when ( iBSTATE = 5 ) else -- duty 30%
"0001" when ( iBSTATE = 7 ) else -- duty 50%
"0000" ;
iSTATUS(7 downto 4) <= "1000" when ( iMODE = 0 ) else -- NORMAL
"0100" when ( iMODE = 3 ) else -- CRANK
"0010" when ( iMODE = 1 ) else -- LANE_RIGHT
"0001" when ( iMODE = 2 ) else -- LANE_LEFT
"0000" ;
CoolRunnerIIは、他のブロックから入力した動作フラグを見て
Blind_RunとJudge_Runのどちらかを実行します。
この動作を監督するタスク(シーケンサ)を定義します。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iPSTATE <= "000" ;
elsif rising_edge(iMCLK) then
case conv_integer(iPSTATE) is
-- wait RFLAG rising edge
when 0 => if ( iRFLAG_R_TRG = '1' ) then
iPSTATE <= "001" ;
end if ;
-- send blind run trigger
when 1 => iPSTATE <= "011" ;
-- wait blind run flag
when 3 => if ( iBFLAG = '1' ) then
iPSTATE <= "111" ;
end if ;
-- send judge run trigger
when 7 => iPSTATE <= "110" ;
-- wait RFLAG falling edge
when 6 => if ( iRFLAG_F_TRG = '1' ) then
iPSTATE <= "100" ;
end if ;
-- return first state
when 4 => iPSTATE <= "000" ;
-- default
when others =>
iPSTATE <= "000" ;
end case ;
end if ;
end process ;
iBTRG <= '1' when ( iPSTATE = "001" ) else '0' ;
iJTRG <= '1' when ( iPSTATE = "111" ) else '0' ;
トリガーとフラグを利用し、2つのシーケンサを動かします。
2つのシーケンサをBlind_Run、Judge_Runとします。
スタート直後の直進(Blind_Run)は、時間で動くように定義しました。
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iBSTATE <= 0 ;
iBCNT <= 0 ;
iBDUTY <= 0 ;
elsif rising_edge(iMCLK) then
case iBSTATE is
-- wait trigger
when 0 => if ( iBTRG = '1' ) then
iBSTATE <= 1 ;
end if ;
-- set duty ratio (5%)
when 1 => iBSTATE <= 2 ;
iBCNT <= 100000 ;
iBDUTY <= 5 ;
-- delay
when 2 => if ( iBCNT = 0 ) then
iBSTATE <= 3 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (15%)
when 3 => iBSTATE <= 4 ;
iBCNT <= 200000 ;
iBDUTY <= 15 ;
-- delay
when 4 => if ( iBCNT = 0 ) then
iBSTATE <= 5 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (30%)
when 5 => iBSTATE <= 6 ;
iBCNT <= 200000 ;
iBDUTY <= 30 ;
-- delay
when 6 => if ( iBCNT = 0 ) then
iBSTATE <= 7 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (50%)
when 7 => iBSTATE <= 0 ;
iBDUTY <= 50 ;
-- default
when others =>
iBSTATE <= 0 ;
end case ;
end if ;
end process ;
iBFLAG <= '1' when ( iBSTATE = 7 ) else '0' ;
まとめると、以下のコードとなります。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mcrx5 is
generic (
TOPX : integer := 4 ;
RMAX : integer := 9 --;
) ;
Port (
-- system
nRESET : in std_logic ;
CLOCK : in std_logic ;
-- sensor inputs
SENSOR : in std_logic_vector(7 downto 0) ;
-- external unit input
RFLAG : in std_logic ; -- run flag from PWM control board
-- OUTPUT
DUTY : out std_logic_vector(7 downto 0) ;
TRG : out std_logic ;
TRGX : out std_logic ;
STATUS : out std_logic_vector(7 downto 0) ;
SOUT : out std_logic_vector(7 downto 0) ;
LED : out std_logic --;
);
end mcrx5;
architecture behavioral of mcrx5 is
-- 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 ;
-- judge
component judge is
port (
-- input
DIN : in std_logic_vector(7 downto 0) ;
-- output
JOUT : out std_logic_vector(3 downto 0) --;
);
end component ;
-- clock
signal iMCLK : std_logic ;
-- parent sequencer
signal iPSTATE : std_logic_vector(2 downto 0) ;
signal iBTRG : std_logic ; -- blind run trigger
signal iBFLAG : std_logic ; -- blind run flag
signal iJTRG : std_logic ; -- judge run trigger
signal iRFLAG_SFT : std_logic_vector(2 downto 0) ;
signal iRFLAG_R_TRG : std_logic ;
signal iRFLAG_F_TRG : std_logic ;
-- blind run sequencer
signal iBSTATE : integer range 0 to 7 ;
signal iBCNT : integer range 0 to 300000 ;
signal iBDUTY : integer range 0 to 63 ;
-- judge run sequencer
signal iJSTATE : std_logic_vector(2 downto 0) ;
signal iJRDUTYX : integer range 0 to 63 ;
signal iJLDUTYX : integer range 0 to 63 ;
signal iJRDUTY : integer range 0 to 63 ;
signal iJLDUTY : integer range 0 to 63 ;
signal iJSENSOR : std_logic_vector(7 downto 0) ;
signal iSENSOR : std_logic_vector(7 downto 0) ;
signal iJOUT : std_logic_vector(3 downto 0) ;
signal iMODE : integer range 0 to 3 ;
signal iJCNT : integer range 0 to 100000 ;
-- update duty
signal iDSTATE : std_logic_vector(1 downto 0) ;
signal iDUTY : std_logic_vector(7 downto 0) ;
signal iTRG : std_logic ;
--
signal iSTATUS : std_logic_vector(7 downto 0) ;
begin
-- clock
CLKX : clkgenx generic map (TOPX,RMAX) port map (nRESET,CLOCK,iMCLK) ;
-- judge
JUDGEX : judge port map (iSENSOR,iJOUT) ;
-- input
iJSENSOR < not SENSOR ;
-- output
DUTY <= iDUTY ;
TRG <= iTRG ;
-- monitor
LED <= not iMCLK ;
SOUT <= not iJSENSOR ;
STATUS <= not iSTATUS ;
TRGX <= iMCLK ;
-- internal status
iSTATUS(3 downto 0) <= "1000" when ( iBSTATE = 1 ) else -- duty 5%
"0100" when ( iBSTATE = 3 ) else -- duty 15%
"0010" when ( iBSTATE = 5 ) else -- duty 30%
"0001" when ( iBSTATE = 7 ) else -- duty 50%
"0000" ;
iSTATUS(7 downto 4) <= "1000" when ( iMODE = 0 ) else -- NORMAL
"0100" when ( iMODE = 3 ) else -- CRANK
"0010" when ( iMODE = 1 ) else -- LANE RIGHT
"0001" when ( iMODE = 2 ) else -- LANE LEFT
"0000" ;
-- trigger shift register
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iRFLAG_SFT <= "000" ;
elsif rising_edge(iMCLK) then
iRFLAG_SFT <= iRFLAG_SFT(1 downto 0) & RFLAG ;
end if ;
end process ;
iRFLAG_R_TRG <= '1' when ( iRFLAG_SFT = "011" or iRFLAG_SFT = "001" ) else '0' ;
iRFLAG_F_TRG <= '1' when ( iRFLAG_SFT = "110" or iRFLAG_SFT = "100" ) else '0' ;
-- parent sequencer
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iPSTATE <= "000" ;
elsif rising_edge(iMCLK) then
case conv_integer(iPSTATE) is
-- wait RFLAG rising edge
when 0 => if ( iRFLAG_R_TRG = '1' ) then
iPSTATE <= "001" ;
end if ;
-- send blind run trigger
when 1 => iPSTATE <= "011" ;
-- wait blind run flag
when 3 => if ( iBFLAG = '1' ) then
iPSTATE <= "111" ;
end if ;
-- send judge run trigger
when 7 => iPSTATE <= "110" ;
-- wait RFLAG falling edge
when 6 => if ( iRFLAG_F_TRG = '1' ) then
iPSTATE <= "100" ;
end if ;
-- return first state
when 4 => iPSTATE <= "000" ;
-- default
when others =>
iPSTATE <= "000" ;
end case ;
end if ;
end process ;
iBTRG <= '1' when ( iPSTATE = "001" ) else '0' ;
iJTRG <= '1' when ( iPSTATE = "111" ) else '0' ;
-- blind run sequencer
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iBSTATE <= 0 ;
iBCNT <= 0 ;
iBDUTY <= 0 ;
elsif rising_edge(iMCLK) then
case iBSTATE is
-- wait trigger
when 0 => if ( iBTRG = '1' ) then
iBSTATE <= 1 ;
end if ;
-- set duty ratio (5%)
when 1 => iBSTATE <= 2 ;
iBCNT <= 100000 ;
iBDUTY <= 5 ;
-- delay
when 2 => if ( iBCNT = 0 ) then
iBSTATE <= 3 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (15%)
when 3 => iBSTATE <= 4 ;
iBCNT <= 200000 ;
iBDUTY <= 15 ;
-- delay
when 4 => if ( iBCNT = 0 ) then
iBSTATE <= 5 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (30%)
when 5 => iBSTATE <= 6 ;
iBCNT <= 200000 ;
iBDUTY <= 30 ;
-- delay
when 6 => if ( iBCNT = 0 ) then
iBSTATE <= 7 ;
else
iBCNT <= iBCNT - 1 ;
end if ;
-- set duty ratio (50%)
when 7 => iBSTATE <= 0 ;
iBDUTY <= 50 ;
-- default
when others =>
iBSTATE <= 0 ;
end case ;
end if ;
end process ;
iBFLAG <= '1' when ( iBSTATE = 7 ) else '0' ;
-- judge run sequencer
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iJSTATE <= "000" ;
iJRDUTY <= 0 ; -- right duty
iJLDUTY <= 0 ; -- left duty
iJRDUTYX <= 0 ; -- right duty
iJLDUTYX <= 0 ; -- left duty
iJCNT <= 0 ; -- delay counter
iMODE <= 0 ; -- NORMAL 0 / LANE_RIGHT 1 / LANE_LEFT 2 / CRANK 3
elsif rising_edge(iMCLK) then
if ( iJTRG = '1' ) then
case conv_integer(iJSTATE) is
-- get sensor data
when 0 => iJSTATE <= "001" ;
iSENSOR <= iJSENSOR ;
-- convert
when 1 => iJSTATE <= "011" ;
case conv_integer(iJOUT) is
-- all_black
when 0 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100000 ; -- 1000ms = 100000 x 10us
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 100000 ; -- 1000ms = 100000 x 10us
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
else
iJRDUTYX <= 0 ; -- right duty
iJLDUTYX <= 0 ; -- left duty
end if ;
-- tiny_right
when 1 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 20 ; -- right duty
iJLDUTYX <= 27 ; -- left duty
iJCNT <= 25 ;
else
iJRDUTYX <= 20 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 23 ;
end if ;
-- right
when 2 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 20 ;
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 25 ; -- left duty
iJCNT <= 22 ;
end if ;
-- big_right
when 3 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 30 ; -- left duty
iJCNT <= 15 ;
else
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 30 ; -- left duty
iJCNT <= 17 ;
end if ;
-- center
when 4 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 35 ; -- left duty
iJCNT <= 100000 ;
iMODE <= 0 ; -- change normal
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 35 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100000 ;
iMODE <= 0 ; -- change normal
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 5 ;
else
iJRDUTYX <= 35 ; -- right duty
iJLDUTYX <= 35 ; -- left duty
iJCNT <= 10 ;
end if ;
-- tiny_left
when 5 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 20 ; -- left duty
iJCNT <= 25 ;
else
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 20 ; -- left duty
iJCNT <= 27 ;
end if ;
-- left
when 6 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 10 ; -- left duty
iJCNT <= 20 ;
else
iJRDUTYX <= 25 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 23 ;
end if ;
-- big_left
when 7 => if ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 30 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 15 ;
else
iJRDUTYX <= 30 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 17 ;
end if ;
-- right_white
when 8 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 45 ;
iMODE <= 0 ; -- change NORMAL
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 1 ; -- change LANE (right)
end if ;
-- left_white
when 9 => if ( iMODE = 2 ) then -- LANE_LEFT
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 1 ) then -- LANE_RIGHT
iJRDUTYX <= 5 ; -- right duty
iJLDUTYX <= 55 ; -- left duty
iJCNT <= 1000 ;
iMODE <= 0 ; -- change NORMAL
elsif ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 55 ; -- right duty
iJLDUTYX <= 5 ; -- left duty
iJCNT <= 45 ;
iMODE <= 0 ; -- change NORMAL
else
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 2 ; -- change LANE (left)
end if ;
-- all_white
when 10 => if ( iMODE = 3 ) then -- CRANK
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 100 ;
elsif ( iMODE = 0 ) then -- NORMAL
iJRDUTYX <= 15 ; -- right duty
iJLDUTYX <= 15 ; -- left duty
iJCNT <= 10000 ;
iMODE <= 3 ; -- change CRANK
else -- LANE_LEFT / LANE_RIGHT
end if ;
when others => NULL ;
end case ;
-- latch
when 3 => iJSTATE <= "111" ;
iJRDUTY <= iJRDUTYX ; -- right duty
iJLDUTY <= iJLDUTYX ; -- left duty
-- delay
when 7 => if ( iJCNT = 0 ) then
iJSTATE <= "110" ;
else
iJCNT <= iJCNT - 1 ;
end if ;
-- dummy (auto skip)
when 6 => iJSTATE <= "100" ;
-- return first state
when 4 => iJSTATE <= "000" ;
-- default
when others =>
iJSTATE <= "000" ;
end case ;
else
iJRDUTY <= 0 ; -- right duty
iJLDUTY <= 0 ; -- left duty
end if ;
end if ;
end process ;
-- update duty
process (nRESET,iMCLK)
begin
if ( nRESET = '0' ) then
iDSTATE <= "00" ;
iDUTY <= (others => '1') ;
elsif rising_edge(iMCLK) then
case conv_integer(iDSTATE) is
-- get left duty
when 0 => iDSTATE <= "01" ;
if ( iPSTATE = "011" ) then
iDUTY <= '1' & conv_std_logic_vector(iBDUTY,7) ;
else
iDUTY <= '1' & conv_std_logic_vector(iJLDUTY,7) ;
end if ;
-- send trigger
when 1 => iDSTATE <= "11" ;
-- get right duty
when 3 => iDSTATE <= "10" ;
if ( iPSTATE = "011" ) then
iDUTY <= '0' & conv_std_logic_vector(iBDUTY,7) ;
else
iDUTY <= '0' & conv_std_logic_vector(iJRDUTY,7) ;
end if ;
-- send trigger and return first state
when 2 => iDSTATE <= "00" ;
-- default
when others =>
iDSTATE <= "00" ;
end case ;
end if ;
end process ;
iTRG <= '1' when ( iDSTATE = "01" or iDSTATE = "10" ) else '0' ;
end behavioral;
ピンアサインを定義します。
# system
NET "CLOCK" LOC = "P38" ;
NET "LED" LOC = "P92" ;
NET "nRESET" LOC = "P143" ;
# output group A (J6)
NET "RFLAG" LOC = "P87" ;
NET "TRGX" LOC = "P80" ;
NET "TRG" LOC = "P81" ;
# output group B (J6)
NET "DUTY<7>" LOC = "P78" ;
NET "DUTY<6>" LOC = "P79" ;
NET "DUTY<5>" LOC = "P76" ;
NET "DUTY<4>" LOC = "P77" ;
NET "DUTY<3>" LOC = "P74" ;
NET "DUTY<2>" LOC = "P75" ;
NET "DUTY<1>" LOC = "P70" ;
NET "DUTY<0>" LOC = "P71" ;
# output group A (J4)
NET "SENSOR<7>" LOC = "P142" ;
NET "SENSOR<6>" LOC = "P140" ;
NET "SENSOR<5>" LOC = "P139" ;
NET "SENSOR<4>" LOC = "P138" ;
NET "SENSOR<3>" LOC = "P137" ;
NET "SENSOR<2>" LOC = "P136" ;
NET "SENSOR<1>" LOC = "P135" ;
NET "SENSOR<0>" LOC = "P134" ;
# output group B (J4)
NET "SOUT<7>" LOC = "P133" ;
NET "SOUT<6>" LOC = "P132" ;
NET "SOUT<5>" LOC = "P131" ;
NET "SOUT<4>" LOC = "P130" ;
NET "SOUT<3>" LOC = "P129" ;
NET "SOUT<2>" LOC = "P128" ;
NET "SOUT<1>" LOC = "P126" ;
NET "SOUT<0>" LOC = "P125" ;
# output group C (J4)
NET "STATUS<7>" LOC = "P124" ;
NET "STATUS<6>" LOC = "P121" ;
NET "STATUS<5>" LOC = "P120" ;
NET "STATUS<4>" LOC = "P119" ;
NET "STATUS<3>" LOC = "P118" ;
NET "STATUS<2>" LOC = "P117" ;
NET "STATUS<1>" LOC = "P116" ;
NET "STATUS<0>" LOC = "P115" ;
ここまでのVHDLコードでは、ロータリーエンコーダの
情報を入力していません。
目次
前
次