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

pp.447-454

3.3 Vで直接動かせるSC1602BBWB-XA-LB-Gを使った。

テキストはelsif rising_edge(E) thenにしてあるが、Quartus Prime LiteはVHDL2008を全部は実装していないようでoutポートが読めずにエラーが出る。しかたないので假のsignalを設けてelsif rising_edge(E_temp) thenにした。ほかはテキストどおりである。
 

library ieee;
use ieee.std_logic_1164.all;

entity digital_watch_with_LCD_display 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 0)
    );
end entity;

architecture rtl of digital_watch_with_LCD_display 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,

        ClearDisplay, DisplayControl, EntryMode,

        WriteHourT, WriteHourU, WriteColon1,
        WriteMinT , WriteMinU , WriteColon2,
        WriteSecT , WriteSecU , ReturnHome
    );
    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)
    begin
        RS <= '0';
        DB <= "00111000";
        case pr_state is
            when FunctionSet1 => nx_state <= FunctionSet2;
            when FunctionSet2 => nx_state <= FunctionSet3;
            when FunctionSet3 => nx_state <= FunctionSet4;
            when FunctionSet4 => nx_state <= ClearDisplay;
            
            when ClearDisplay   =>         DB <= "00000001"       ; nx_state <= DisplayCOntrol;
            when DisplayControl =>         DB <= "00001100"       ; nx_state <= EntryMode;
            when EntryMode      =>         DB <= "00000110"       ; nx_state <= WriteHourT;
                
            when WriteHourT  => RS <= '1'; DB <= int_to_lcd(hourT); nx_state <= WriteHourU;
            when WriteHourU  => RS <= '1'; DB <= int_to_lcd(hourU); nx_state <= WriteColon1;
            when WriteColon1 => RS <= '1'; DB <= "00111010"       ; nx_state <= WriteMinT;
            when WriteMinT   => RS <= '1'; DB <= int_to_lcd(MinT) ; nx_state <= WriteMinU;
            when WriteMinU   => RS <= '1'; DB <= int_to_lcd(MinU) ; nx_state <= WriteColon2;
            when WriteColon2 => RS <= '1'; DB <= "00111010"       ; nx_state <= WriteSecT;
            when WriteSecT   => RS <= '1'; DB <= int_to_lcd(SecT) ; nx_state <= WriteSecU;
            when WriteSecU   => RS <= '1'; DB <= int_to_lcd(SecU) ; nx_state <= ReturnHome;

            when ReturnHome  =>            DB <= "10000000"       ; nx_state <= WriteHourT;
        end case;
    end process;

end architecture;

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