(DE0-CV) DE0拡張キットのLCDモジュールを使う (4)

VHDLにする

前回作成したタイミングチャートをVHDLにしました。

サンプルとしてQuartusのプロジェクトも置いておきます。
(Quartus Prime Version 16.1.0 Build 196 10/24/2016 SJ Lite Edition で確認) DE0_CV_LCD.zip - Google ドライブ

library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.std_logic_misc.all;
    use IEEE.std_logic_arith.all;
    use IEEE.std_logic_unsigned.all;

entity DE0_CV_LCD is
    port(

        --//////////// CLOCK //////////
        CLOCK_50         : in     std_logic;
        CLOCK2_50        : in     std_logic;
        CLOCK3_50        : in     std_logic;
        CLOCK4_50        : inout  std_logic;

        --//////////// KEY //////////
        KEY              : in     std_logic_vector( 3 downto 0);
        RESET_N          : in     std_logic;

        --//////////// LED //////////
        LEDR             : out    std_logic_vector( 9 downto 0);

        --//////////// for connecting an LCD //////////
        LCD_D            : out    std_logic_vector( 7 downto 0);
        LCD_RS           : out    std_logic;
        LCD_RW           : out    std_logic;
        LCD_EN           : out    std_logic 
    );
end DE0_CV_LCD;

architecture RTL of DE0_CV_LCD is
    
    --------------------------------------------------
    -- 初期化開始待機用信号
    signal sWakeUpCounter : std_logic_vector(20 downto 0);
    signal sModuleEnable  : std_logic;
    --------------------------------------------------

    --------------------------------------------------
    -- クロックカウント用信号
    signal sClockCounter : std_logic_vector(17 downto 0);
    signal sMaxCount     : std_logic_vector(sClockCounter'range);
    --------------------------------------------------

    --------------------------------------------------
    -- 状態カウント用信号
    -- kStopStateまでsStateCountをカウントし続ける
    signal sStateCount   : std_logic_vector( 4 downto 0);
    constant kStopState  : std_logic_vector(sStateCount'range) := conv_std_logic_vector(20, sStateCount'length);
    --------------------------------------------------

    
    --------------------------------------------------
    -- sStateCountの値に対応したLCD_RS, LCD_RW, LCD_Dの出力
    -- 2次元配列に格納している値をLCD_RS, LCD_RW, LCD_Dに割り当てる
    
    signal sCommandOfLCD : std_logic_vector( 9 downto 0);
    type tPatternOfLCD     is array( 0 to 19) of std_logic_vector(sCommandOfLCD'range);

    constant kFunstionSet    : std_logic_vector(sCommandOfLCD'range) := "0000111100";
    constant kDisplayInit    : std_logic_vector(sCommandOfLCD'range) := "0000001000";
    constant kDisplayControl : std_logic_vector(sCommandOfLCD'range) := "0000001111";
    constant kDisplayClear   : std_logic_vector(sCommandOfLCD'range) := "0000000001";
    constant kEntryModeSet   : std_logic_vector(sCommandOfLCD'range) := "0000000110";

    -- HELLO WORLD! の値
    constant kH  : std_logic_vector(sCommandOfLCD'range) := "1001001000";
    constant kE  : std_logic_vector(sCommandOfLCD'range) := "1001000101";
    constant kL  : std_logic_vector(sCommandOfLCD'range) := "1001001100";
    constant kO  : std_logic_vector(sCommandOfLCD'range) := "1001001111";
    constant kSp : std_logic_vector(sCommandOfLCD'range) := "1000100000";
    constant kW  : std_logic_vector(sCommandOfLCD'range) := "1001010111";
    constant kR  : std_logic_vector(sCommandOfLCD'range) := "1001010010";
    constant kD  : std_logic_vector(sCommandOfLCD'range) := "1001000100";
    constant kEx : std_logic_vector(sCommandOfLCD'range) := "1000100001";

    constant kInitPattern : tPatternOfLCD :=
                            ( kFunstionSet, kFunstionSet , kFunstionSet , kFunstionSet   ,
                              kDisplayInit, kDisplayClear, kEntryModeSet, kDisplayControl,
                              kH , kE  , kL , kL ,
                              kO , kSp , kW , kO ,
                              kR , kL  , kD , kEx
                            );

    --------------------------------------------------

    --------------------------------------------------
    -- sStateCountの値に対応したEの周期 (t_cycE)
    -- 初期化処理やコマンドによって異なるため、
    -- 2次元配列によってコマンドごとに時間を決めている

    type tWaitPatternOfLCD is array( 0 to 19) of std_logic_vector(sClockCounter'range);

    constant kWait4500us : std_logic_vector(sClockCounter'range) := "110110111011101000";
    constant kWait200us  : std_logic_vector(sClockCounter'range) := "000010011100010000";
    constant kWait40us   : std_logic_vector(sClockCounter'range) := "000000011111010000";

    constant kInitWaitPattern : tWaitPatternOfLCD :=
                                ( kWait4500us , kWait200us  , kWait40us, kWait40us,
                                  kWait40us   , kWait4500us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us
                                );

    --------------------------------------------------
    
    --------------------------------------------------
    -- LCD_RS, LCD_RW開始/終了時間
    constant kRsStart  : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(  0, sClockCounter'length);
    constant kRsEnd    : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(128, sClockCounter'length);
    --------------------------------------------------
    -- LCD_EN開始/終了時間
    constant kEnStart  : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 32, sClockCounter'length);
    constant kEnEnd    : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 96, sClockCounter'length);
    --------------------------------------------------
    -- LCD_D開始/終了時間
    constant kComStart : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 64, sClockCounter'length);
    constant kComEnd   : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(128, sClockCounter'length);
    --------------------------------------------------
    
begin
    
    --------------------------------------------------
    -- 初期化処理待機用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sWakeUpCounter <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            -- sModuleEnable='0'の間、
            -- sWakeUpCounterをカウントアップし続ける
            if (sModuleEnable = '0') then
                sWakeUpCounter <= sWakeUpCounter + '1';
            end if;
        end if;
    end process;
    
    -- sWakeUpCounterがすべて'1'となったらsModuleEnable='1'
    sModuleEnable <= and_reduce(sWakeUpCounter);

    --------------------------------------------------

    --------------------------------------------------
    -- クロックカウント用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sClockCounter <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sModuleEnable = '0' or sStateCount = kStopState) then
                -- 初期化待機中、もしくは状態が既定の値に達したら
                -- カウントアップを止める
                sClockCounter <= (others => '0');
            elsif (sClockCounter = sMaxCount) then
                sClockCounter <= (others => '0');
            else
                sClockCounter <= sClockCounter + '1';
            end if;
        end if;
    end process;
    
    -- 各状態に対応したsClockCounterのMax値を代入
    sMaxCount <= kInitWaitPattern(conv_integer(sStateCount));

    --------------------------------------------------

    --------------------------------------------------
    -- 状態カウント用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sStateCount <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter = sMaxCount) then
                sStateCount <= sStateCount + '1';
            end if;
        end if;
    end process;
    
    --------------------------------------------------

    --------------------------------------------------
    -- LCD_EN, LCD_D, LCD_RS, LCD_RW出力

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_EN <= '0';
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kEnStart and sClockCounter < kEnEnd) then
                LCD_EN <= '1';
            else
                LCD_EN <= '0';
            end if;
        end if;
    end process;

    
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_D <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kComStart and sClockCounter < kComEnd) then
                LCD_D <= kInitPattern(conv_integer(sStateCount))( 7 downto 0);
            else
                LCD_D <= (others => '0');
            end if;
        end if;
    end process;

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_RW <= '0';
            LCD_RS <= '0';
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kRsStart and sClockCounter < kRsEnd) then
                LCD_RW <= kInitPattern(conv_integer(sStateCount))(8);
                LCD_RS <= kInitPattern(conv_integer(sStateCount))(9);
            else
                LCD_RW <= '0';
                LCD_RS <= '0';
            end if;
        end if;
    end process;
    
    --------------------------------------------------

    
end architecture;