目次

2LED方向指示器作成

 自動車やバイクで利用されている、左右にぞれぞれ
 ランプを用意して、点滅することで、方向指示する
 装置をエミュレートしてみます。

 ブレッドボードにLEDと抵抗をのせると、次のようになります。



 回路は、次のように負論理を利用。




 点滅は、論理値の'1'と'0'を交互に出力すればよいので
 マスタークロックを分周したクロックを生成して利用と
 考えます。

 左右のボタンを使うことを考えて、マスタークロック
 から、1kHzと1Hzを生成します。



 分周に利用するVHDLコードは、以下。

  -- divider
  process ( nRESET , CLOCK )
  begin
    if ( nRESET = '0' ) then
      iACNT <= 0 ;
    elsif rising_edge( CLOCK ) then
      if ( iACNT = ACNT_MAX ) then
        iACNT <= 0 ;
      else
        iACNT <= iACNT + 1 ;
      end if ;
    end if ;
  end process ;
  iACLK <= '1' when ( iACNT < ACNT_HALF ) else '0' ;

  process ( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iBCNT <= 0 ;
    elsif rising_edge( iACLK ) then
      if ( iBCNT = BCNT_MAX ) then
        iBCNT <= 0 ;
      else
        iBCNT <= iBCNT + 1 ;
      end if ;
    end if ;
  end process ;
  iBCLK <= '1' when ( iBCNT < BCNT_HALF ) else '0' ;

 分周した1Hzを、LEDに出力するか否かを論理積で
 制御すればよいので、出力を次のように定義。

  Lout <= iBCLK when ( ????? ) else '1' ;
  Rout <= iBCLK when ( ????? ) else '1' ;

 ボタンが押されたことを検出して、左右のLEDに
 クロックを出力するか否か処理を、シーケンサ
 でまとめます。

 シーケンサの状態遷移図を描いて考えます。



 状態がwaitの間に、クロックを出力すればよいので
 状態値で、クロックをLEDに与えるよう工夫します。

  Lout <= iBCLK when ( iSTATE = "11" ) else '1' ;
  Rout <= iBCLK when ( iSTATE = "11" ) else '1' ;

 トリガーを受けてからシーケンサが動作するので
 左ボタン、右ボタンのトリガーを受けて動作する
 と考えれば、次のように、ステートマシンのVHDL
 コードを作成すればよいでしょう。

  process( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iSTATE <= "00" ;
    elsif rising_edge( iACLK ) then
      case conv_integer(iSTATE) is
        -- wait trigger
        when 0 => if ( iLTRG = '1' or iRTRG = '1' ) then
                    iSTATE <= "01" ;
                  else
                    iSTATE <= "00" ;
                  end if ;
        -- enable
        when 1 => iSTATE <= "11" ;
        -- wait
        when 3 => -- ????? -- 
        -- disable and return first state 
        when 2 => iSTATE <= "00" ;
        -- default
        when others => iSTATE <= "00" ;
      end case ;
    end if ;
  end process;

 トリガー生成とwaitをどう実現するかまで
 処理を分けたので、個別に考えていきます。

 ボタンは、次のように正論理で扱います。



 ボタンには、チャタリングがあるので
 シフトレジスタを使って除去。

  iLTRG <= '1' when ( iLSFT = "01" ) else '0' ;
  iRTRG <= '1' when ( iRSFT = "01" ) else '0' ;

  process( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iLSFT <= "00" ;
      iRSFT <= "00" ;
    elsif rising_edge( iACLK ) then
      iLSFT <= iLSFT(0) & LSWT ;
      iRSFT <= iRSFT(0) & RSWT ;
    end if ;
  end process;

 トリガーを片付けたので、waitを考えます。
 ボタンを押したなら、15秒間LEDを点滅させる
 とするします。

 シーケンサは1kHzで動作しているので、1msの
 インターバルをもっています。15秒はmsでの
 換算で15000カウントですから、このカウント
 を、シーケンサに任せます。

 シーケンサの中で、イネーブル処理のときに
 カウンタを設定し、waitでデクリメントして
 いきます。

  process( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iSTATE <= "00" ;
      iWCNT  <= 0 ;
    elsif rising_edge( iACLK ) then
      case conv_integer(iSTATE) is
        -- wait trigger
        when 0 => if ( iLTRG = '1' or iRTRG = '1' ) then
                    iSTATE <= "01" ;
                  else
                    iSTATE <= "00" ;
                  end if ;
        -- enable and set counter
        when 1 => iSTATE <= "11" ;
                  iWCNT  <= 15000 ;
        -- wait and decrement
        when 3 => if ( iWCNT = 0 ) then
                    iSTATE <= "10" ;
                  else
                    iSTATE <= "11" ;
                    iWCNT  <= iWCNT - 1 ;
                  end if ;
        -- disable and return first state 
        when 2 => iSTATE <= "00" ;
        -- default
        when others => iSTATE <= "00" ;
      end case ;
    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 bflash is
  Port (
    -- system 
    nRESET : in  std_logic ;
    CLOCK  : in  std_logic ; -- 4MHz
    -- clock monitor
    ACLK   : out std_logic ;
    BCLK   : out std_logic ;
    -- button inputs
    LSWT   : in  std_logic ;
    RSWT   : in  std_logic ;
    -- output
    Lout   : out std_logic ;
    Rout   : out std_logic --;
  );
end bflash;

architecture Behavioral of bflash is
  --
  CONSTANT ACNT_MAX  : integer :=  3999 ;
  CONSTANT BCNT_MAX  : integer :=   999 ;
  CONSTANT ACNT_HALF : integer :=  2000 ;
  CONSTANT BCNT_HALF : integer :=   500 ;
  CONSTANT LAST      : integer := 15000 ;
  -- divider
  signal iACNT : integer range 0 to ACNT_MAX ;
  signal iACLK : std_logic ;
  signal iBCNT : integer range 0 to BCNT_MAX ;
  signal iBCLK : std_logic ;
  -- synchronizer
  signal iLSFT : std_logic_vector(1 downto 0) ;
  signal iRSFT : std_logic_vector(1 downto 0) ;
  signal iLTRG : std_logic ;
  signal iRTRG : std_logic ;
  -- sequencer
  signal iSTATE : std_logic_vector(1 downto 0) ;
  signal iWCNT  : integer range 0 to LAST ;
begin
  -- output
  Lout <= iBCLK when ( iSTATE = "11" ) else '1' ;
  Rout <= iBCLK when ( iSTATE = "11" ) else '1' ;

  -- monitor
  ACLK <= iACLK ;
  BCLK <= iBCLK ;

  -- divider
  process ( nRESET , CLOCK )
  begin
    if ( nRESET = '0' ) then
      iACNT <= 0 ;
    elsif rising_edge( CLOCK ) then
      if ( iACNT = ACNT_MAX ) then
        iACNT <= 0 ;
      else
        iACNT <= iACNT + 1 ;
      end if ;
    end if ;
  end process ;
  iACLK <= '1' when ( iACNT < ACNT_HALF ) else '0' ;

  process ( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iBCNT <= 0 ;
    elsif rising_edge( iACLK ) then
      if ( iBCNT = BCNT_MAX ) then
        iBCNT <= 0 ;
      else
        iBCNT <= iBCNT + 1 ;
      end if ;
    end if ;
  end process ;
  iBCLK <= '1' when ( iBCNT < BCNT_HALF ) else '0' ;

  -- synchronizer
  iLTRG <= '1' when ( iLSFT = "01" ) else '0' ;
  iRTRG <= '1' when ( iRSFT = "01" ) else '0' ;

  process( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iLSFT <= "00" ;
      iRSFT <= "00" ;
    elsif rising_edge( iACLK ) then
      iLSFT <= iLSFT(0) & LSWT ;
      iRSFT <= iRSFT(0) & RSWT ;
    end if ;
  end process;

  -- sequencer
  process( nRESET , iACLK )
  begin
    if ( nRESET = '0' ) then
      iSTATE <= "00" ;
      iWCNT  <= 0 ;
    elsif rising_edge( iACLK ) then
      case conv_integer(iSTATE) is
        -- wait trigger
        when 0 => if ( iLTRG = '1' or iRTRG = '1' ) then
                    iSTATE <= "01" ;
                  else
                    iSTATE <= "00" ;
                  end if ;
        -- enable and set counter
        when 1 => iSTATE <= "11" ;
                  iWCNT  <= LAST ;
        -- wait and decrement
        when 3 => if ( iWCNT = 0 ) then
                    iSTATE <= "10" ;
                  else
                    iSTATE <= "11" ;
                    iWCNT  <= iWCNT - 1 ;
                  end if ;
        -- disable and return first state 
        when 2 => iSTATE <= "00" ;
        -- default
        when others => 
                  iSTATE <= "00" ;
      end case ;
    end if ;
  end process;

end Behavioral;

 ピンアサインは、以下としました。

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

# monitor
NET "ACLK"   loc = "P1" ;
NET "BCLK"   loc = "P2" ;

# LED
NET "Rout"   loc = "P3" ;
NET "Lout"   loc = "P4" ;

# button
NET "RSWT"   loc = "P8" ;
NET "LSWT"   loc = "P9" ;


目次

inserted by FC2 system