ディジタル時計 / VHDLでキャラクターLCD / 4ビットモード

テキストは8ビットモードで制御しているが今度は4ビットモードを試す。もっとスマートに書けると思うが今のところわからない。

library ieee;
use ieee.std_logic_1164.all;

entity digital_watch_with_LCD_display_4bit is
    generic(
        F_CLK: natural := 48_000_000
    );
    port(
        clk, rst                  : in std_logic;
        set_sec, set_min, set_hour: in std_logic;
        
        E,  RS/*,  RW, LCD_ON, BKLT_ON*/: out std_logic;
        
        DB: out std_logic_vector(7 downto 4)
    );
end entity;

architecture rtl of digital_watch_with_LCD_display_4bit is

    function int_to_lcd(input: natural) return std_logic_vector is
    begin
        case input is
            when 0 => return "00110000";
            when 1 => return "00110001";
            when 2 => return "00110010";
            when 3 => return "00110011";
            when 4 => return "00110100";
            when 5 => return "00110101";
            when 6 => return "00110110";
            when 7 => return "00110111";
            when 8 => return "00111000";
            when 9 => return "00111001";
            when others => return "00111111"; -- '?'マーク
        end case;
    end function;

    type state_type is (
        FunctionSet1, FunctionSet2, FunctionSet3, FunctionSet4, FunctionSet5, FunctionSet6,
        

        ClearDisplay_H, DisplayControl_H, EntryMode_H,
        ClearDisplay_L, DisplayControl_L, EntryMode_L,

        WriteHourT_H,   WriteHourU_H,   WriteColon1_H,
        WriteHourT_L,   WriteHourU_L,   WriteColon1_L,

        WriteMinT_H, WriteMinU_H, WriteColon2_H,
        WriteMinT_L, WriteMinU_L, WriteColon2_L,

        WriteSecT_H, WriteSecU_H,   ReturnHome_H,
        WriteSecT_L, WriteSecU_L,   ReturnHome_L
    );
    signal pr_state, nx_state: state_type;
    
    signal secU, minU, hourU: natural range 0 to 9;     -- 一秒の桁, 一分の桁, 一時の桁
    signal secT, minT       : natural range 0 to 5;     -- 十秒の桁, 十分の桁
    signal hourT            : natural range 0 to 2;     -- 十時の桁
    signal limit            : natural range 0 to F_CLK;
    
    signal E_temp: std_logic;

begin
    limit <=
        F_CLK / 8192 when set_hour else -- set_hourがHiなら時計の進む速度を8192倍にする。
        F_CLK /  256 when set_min  else -- set_min がHiなら時計の進む速度を 256倍になる。
        F_CLK /    8 when set_sec  else -- set_sec をHiなら時計の進む速度を   8倍にする
        F_CLK;
        
    process(clk, rst)
        variable count0: natural range 0 to F_CLK; -- ソースクロックのカウンター
        variable count1: natural range 0 to 10;    -- 一秒桁のカウンター
        variable count2: natural range 0 to  6;    -- 十秒桁のカウンター
        variable count3: natural range 0 to 10;    -- 一分桁のカウンター
        variable count4: natural range 0 to  6;    -- 十分桁のカウンター
        variable count5: natural range 0 to 10;    -- 一時桁のカウンター
        variable count6: natural range 0 to  3;    -- 十時桁のカウンター
    begin
        if rst then
            count0 := 0;
            count1 := 0;
            count2 := 0;
            count3 := 0;
            count4 := 0;
            count5 := 0;
            count6 := 0;
        elsif rising_edge(clk)                           then              count0 := count0 + 1;
            if                               count0=limit then count0 := 0; count1 := count1 + 1;
                if                            count1=10    then count1 := 0; count2 := count2 + 1;
                    if                         count2= 6    then count2 := 0; count3 := count3 + 1;
                        if                      count3=10    then count3 := 0; count4 := count4 + 1;
                            if                   count4= 6    then count4 := 0; count5 := count5 + 1;
                                if (count6/=2 and count5=10) or
                                   (count6 =2 and count5= 4)   then count5 := 0; count6 := count6 + 1;
                                    if             count6= 3    then count6 := 0;
                                    end if;
                                end if;
                            end if;
                        end if;
                    end if;
                end if;
            end if;
        end if;
        secU  <= count1;
        secT  <= count2;
        minU  <= count3;
        minT  <= count4;
        hourU <= count5;
        hourT <= count6;
    end process;

    /*LCD_ON  <= '1';
   BKLT_ON <= '1';
   RW      <= '0';*/

   -- LCDの駆動パルス生成する。
   -- ここではソースクロック48 MHzから500 Hz(周期2 msec)を生成する。
   -- 48000クロックサイクルごとにトグルする。
    process(clk)
        variable count: natural range 0 to F_CLK / 1000;
    begin
        if rising_edge(clk) then
            count := count + 1;
            if count = F_CLK / 1000 then
                count := 0;
                E <= not E;
            end if;
        end if;
    end process;

   -- ステートを保存しておくためのレジスタ
    process(E, rst)
    begin
        E_temp <= E;
        if    rst                 then pr_state <= FunctionSet1;
        elsif rising_edge(E_temp) then pr_state <= nx_state;
        end if;
        E <= E_temp;
    end process;

   -- 次のステートを決めるための組み合わせ論理回路
    process(all)
        variable hourT_temp: std_logic_vector(7 downto 0);
        variable hourU_temp: std_logic_vector(7 downto 0);
        variable minT_temp : std_logic_vector(7 downto 0);
        variable minU_temp : std_logic_vector(7 downto 0);
        variable secT_temp : std_logic_vector(7 downto 0);
        variable secU_temp : std_logic_vector(7 downto 0);
        
    begin
        RS <= '0';
        DB <= "0011";
        case pr_state is
            when FunctionSet1 => nx_state <= FunctionSet2;
            when FunctionSet2 => nx_state <= FunctionSet3;
            when FunctionSet3 => nx_state <= FunctionSet4;
            
            when FunctionSet4 => DB <= "0010"; nx_state <= FunctionSet5;
            when FunctionSet5 => DB <= "0010"; nx_state <= FunctionSet6;
            when FunctionSet6 => DB <= "1000"; nx_state <= ClearDisplay_H;
            
            when ClearDisplay_H   => DB <= "0000"; nx_state <= ClearDisplay_L;
            when ClearDisplay_L   => DB <= "0001"; nx_state <= DisplayCOntrol_H;
            
            when DisplayControl_H => DB <= "0000"; nx_state <= DisplayControl_L;
            when DisplayControl_L => DB <= "1100"; nx_state <= EntryMode_H;
            
            when EntryMode_H      => DB <= "0000"; nx_state <= EntryMode_L;
            when EntryMode_L      => DB <= "0110"; nx_state <= WriteHourT_H;
            
                
            when WriteHourT_H => hourT_temp := int_to_lcd(hourT);
                RS <= '1'; DB <= hourT_temp(7 downto 4); nx_state <= WriteHourT_L;
            when WriteHourT_L =>
                RS <= '1'; DB <= hourT_temp(3 downto 0); nx_state <= WriteHourU_H;

            when WriteHourU_H => hourU_temp := int_to_lcd(hourU);
                RS <= '1'; DB <= hourU_temp(7 downto 4); nx_state <= WriteHourU_L;
            when WriteHourU_L =>
                RS <= '1'; DB <= hourU_temp(3 downto 0); nx_state <= WriteColon1_H;

            when WriteColon1_H => RS <= '1'; DB <= "0011"; nx_state <= WriteColon1_L;
            when WriteColon1_L => RS <= '1'; DB <= "1010"; nx_state <= WriteMinT_H;

            when WriteMinT_H => minT_temp := int_to_lcd(minT);
                RS <= '1'; DB <= minT_temp(7 downto 4); nx_state <= WriteMinT_L;
            when WriteMinT_L =>
                RS <= '1'; DB <= minT_temp(3 downto 0); nx_state <= WriteMinU_H;

            when WriteMinU_H => minU_temp  := int_to_lcd(minU);
                RS <= '1'; DB <= minU_temp(7 downto 4); nx_state <= WriteMinU_L;
            when WriteMinU_L =>
                RS <= '1'; DB <= minU_temp(3 downto 0); nx_state <= WriteColon2_H;

            when WriteColon2_H => RS <= '1'; DB <= "0011"; nx_state <= WriteColon2_L;
            when WriteColon2_L => RS <= '1'; DB <= "1010"; nx_state <= WriteSecT_H;
            
            when WriteSecT_H => secT_temp := int_to_lcd(secT);
                RS <= '1'; DB <= secT_temp(7 downto 4); nx_state <= WriteSecT_L;
            when WriteSecT_L =>
                RS <= '1'; DB <= secT_temp(3 downto 0); nx_state <= WriteSecU_H;
                
            when WriteSecU_H => secU_temp := int_to_lcd(secU);
                RS <= '1'; DB <= secU_temp(7 downto 4); nx_state <= WriteSecU_L;
            when WriteSecU_L =>
                RS <= '1'; DB <= secU_temp(3 downto 0); nx_state <= ReturnHome_H;
                
            when ReturnHome_H => DB <= "1000"; nx_state <= ReturnHome_L;
            when ReturnHome_L => DB <= "0000"; nx_state <= WriteHourT_H;
        end case;
    end process;

end architecture;

f:id:ti-nspire:20210519085058p:plain