-- -------------------------------------------------------------------------------------
-- -------------- Jádro pro obsluhu displaye 2x16 znaků na desce ML506. ----------------
-- -------------------------------------------------------------------------------------
-- Obsahuje vlastní distribuovanou RAM (32B), do které se uloží znaky pro zobrazení 
-- na LCD. Zobrazení je automatizované, znaky stačí zapsat pouze do RAM. 
-- -------------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
 
entity lcd is
	port
		( 
			-- Interface pro pripojeni k řadiči na ML506
			USER_CLK    : in    std_logic;
         LCD_FPGA_DB	: inout std_logic_vector(3 downto 0);
         LCD_FPGA_E	: out	  std_logic;
         LCD_FPGA_RS	: out	  std_logic;
		   LCD_FPGA_RW	: out	  std_logic;
         -- interface pro RAM
			WRITE_CLK	: in std_logic;							-- hodny pro zapis
			WRITE_ADDR	: in std_logic_vector(4 downto 0);	-- adresa zapisu 
			WE       	: in std_logic;							-- write enable
			DATA_IN		: in std_logic_vector(7 downto 0);	-- vstupni data
			RESET			: in std_logic								-- reset
		);
end lcd;
 
architecture Behavioral of lcd is

-- Distribuovana RAM pro obsah displaye 
type type_message is array (0 to 31) of std_logic_vector(7 downto 0);
signal message: type_message;      

signal lcd_i: std_logic_vector(6 downto 0);
signal ram_out, ram_in: std_logic_vector(7 downto 0);
-- INIT = cca 40ms = 2 na 22
--  = 2ms
--  = 50us
signal time_count, time_count_next: std_logic_vector(26 downto 0);
signal time_count_rst: std_logic;
signal state, next_state: integer range 0 to 24;

signal read_adress, read_adress_next: std_logic_vector(4 downto 0);
signal read_adress_inc, read_adress_rst: std_logic;

signal char_count, char_count_next: std_logic_vector(6 downto 0);
signal char_count_rst, char_count_inc: std_logic;

signal clear_ram: std_logic;
signal clear_adr, write_adr: std_logic_vector(4 downto 0);
begin

	-- pripojeni vyvodu 
	LCD_FPGA_DB	<=	lcd_i(3 downto 0);
	LCD_FPGA_E	<=	lcd_i(6);
	LCD_FPGA_RS	<=	lcd_i(5);
	LCD_FPGA_RW	<=	lcd_i(4);

	------------------------------------------------------------------------
	-- KOMBINACNI CAST DISPLEJE --------------------------------------------
	------------------------------------------------------------------------
	DISPLAY: process (state, time_count, ram_out, read_adress, char_count)
	begin
		next_state <= state;
		time_count_rst <= '0';
		read_adress_rst <= '0';
		char_count_rst <= '0';
		read_adress_inc <= '0';
		char_count_inc <= '0';
		clear_ram <= '0';
		
		case state is
			-- Inicializace --
			-- Počáteční zpoždění 40 ms
			when 0 =>
				lcd_i <= "0000010";
				clear_ram <= '1';
				if time_count = 4000000 then 
					next_state <= 1;
					time_count_rst <= '1';
				end if;
		
			-- Mód 2x16
			when 1 =>	
				lcd_i <= "1000010";
				if time_count = 250 then	
					next_state <= 2;
					time_count_rst <= '1';
				end if;
		
			when 2 =>
				lcd_i <= "0000010";
				if time_count = 250 then	
					next_state <= 3;
					time_count_rst <= '1';
				end if;
			
			when 3 =>
				lcd_i <= "1000010";
				if time_count = 250 then	
					next_state <= 4;
					time_count_rst <= '1';
				end if;
			
			when 4 =>
				lcd_i <= "0000010";
				if time_count = 250 then	
					next_state <= 5;
					time_count_rst <= '1';
				end if;
			
			when 5 =>	
				lcd_i <= "1001000";
				if time_count = 250 then	
					next_state <= 6;
					time_count_rst <= '1';
				end if;
		
			when 6 =>
				lcd_i <= "0001000";
				if time_count = 5000 then	
					next_state <= 7;
					time_count_rst <= '1';
				end if;
		
			-- Počáteční obrazovka, kurzor
			when 7 =>	
				lcd_i<= "1000000";
				if time_count = 250 then	
					next_state <= 8;
					time_count_rst <= '1';
				end if;
		
			when 8 =>	
				lcd_i <= "0000000";
				if time_count = 250 then	
					next_state <= 9;
					time_count_rst <= '1';
				end if;
		
			when 9 =>
				lcd_i <= "1001100";
				if time_count = 250 then	
					next_state <= 10;
					time_count_rst <= '1';
				end if;
		
			when 10 =>	
				lcd_i <= "0001100";
				if time_count = 5000 then	
					next_state <= 11;
					time_count_rst <= '1';
				end if;
		
		-- Smazani obrazovky
			when 11 =>	
				lcd_i <= "1000000";
				if time_count = 250 then	
					next_state <= 12;
					time_count_rst <= '1';
				end if;
		
			when 12 =>
				lcd_i <= "0000000";
				if time_count = 250 then	
					next_state <= 13;
					time_count_rst <= '1';
				end if;
		
			when 13 =>	
				lcd_i <= "1000001";
				if time_count = 250 then	
					next_state <= 14;
					time_count_rst <= '1';
				end if;
			
			when	14	=>	
				lcd_i <= "0000001";
				if time_count = 200000 then	
					next_state <= 15;
					time_count_rst <= '1';
				end if;
--
			when 15 =>	
				lcd_i <= "1000000";
				if time_count = 250 then	
					next_state <= 16;
					time_count_rst <= '1';
				end if;
		
			when 16 =>	
				lcd_i <= "0000000";
				if time_count = 250 then	
					next_state <= 17;
					time_count_rst <= '1';
				end if;
		
			when 17 =>
				lcd_i <= "1000110";
				if time_count = 250 then	
					next_state <= 18;
					time_count_rst <= '1';
				end if;
		
			when 18 =>
				lcd_i <= "0000110";
				char_count_rst <= '1';
				if time_count = 5000 then	
					next_state <= 19;
					time_count_rst <= '1';
				end if;

			-- Konec inicializace, tisk na obrazovku
			when 19 =>
				lcd_i <= "110"&ram_out(7 downto 4);
				if time_count = 250 then	
					next_state <= 20;
					time_count_rst <= '1';
				end if;
		
			when 20 =>
				lcd_i <= "010"&ram_out(7 downto 4);
				if time_count = 250 then	
					next_state <= 21;
					time_count_rst <= '1';
				end if;
		
			when 21 =>	
				lcd_i <= "110"&ram_out(3 downto 0);
				if time_count = 250 then	
					next_state <= 22;
					time_count_rst <= '1';
				end if;
		
			when 22 =>
				lcd_i <= "010"&ram_out(3 downto 0);
				if time_count = 5000 then	
					next_state <= 23;
					time_count_rst <= '1';
					char_count_inc <= '1';
				end if;				
		
			when	23	=>	
				lcd_i <= "010"&ram_out(3 downto 0);
				next_state <= 19;
				time_count_rst <= '1';
				if (char_count < 16) or (char_count > 39) then 
					read_adress_inc <= '1'; 
				end if;
				if char_count = 80 then next_state <= 24; 
				end if;
			
			-- Vypis na obrazovku je dokoncen
			when 24 =>
				next_state <= 24;
				lcd_i <= "0100000";
				char_count_rst <= '1';
				if time_count = 100000000 then 
					next_state <= 19;
					time_count_rst <= '1';
					read_adress_rst <= '1';
				end if;			
		end case;
	end process;
	
	-----------------------------------------------------------------------
	-- SEKVENCNI CAST DISPLEJE --------------------------------------------
	-----------------------------------------------------------------------
	process(USER_CLK)
	begin
		if(USER_CLK'event and USER_CLK = '1')	then
			if RESET = '1' then 
				--state <= 0;
			else 

				state <= next_state;
				time_count <= time_count_next;
				read_adress <= read_adress_next;
				char_count <= char_count_next;
				ram_out <= message(conv_integer(read_adress+1));			
				
			end if;
		end if;
	end process;
	
	-----------------------------------------------------------------------
	-- Pocitadlo pro měření času
	time_count_next <= (others => '0') when time_count_rst = '1' else
							  time_count + 1;
	-----------------------------------------------------------------------
	-- Pocitadlo znaku v pameti displaye (je tam pamet 2x40, asi univerzalni pro vsechny velikosti displaye)
	char_count_next <= (others => '0') when char_count_rst = '1' else
							  char_count when char_count_inc = '0' else
							  char_count + 1;
	-----------------------------------------------------------------------
	-- Pocitadlo znaku v moji RAMce pro zobrazni (2x16 znaku)
	read_adress_next <= (others => '0') when read_adress_rst = '1' else
								read_adress when read_adress_inc = '0' else
								read_adress + 1;
	
	-----------------------------------------------------------------------
	-- ZAPIS DO RAM -------------------------------------------------------
	-----------------------------------------------------------------------
	process (WRITE_CLK)
	begin
		if (WRITE_CLK'event and WRITE_CLK = '1') then
			if (clear_ram = '1') then clear_adr <= clear_adr + 1; end if;
			if (WE = '1' or clear_ram = '1') then
				message(conv_integer(write_adr)) <= ram_in;
			end if;
		end if;
	end process;
	
	write_adr <= WRITE_ADDR when clear_ram = '0' else clear_adr;
	ram_in <= DATA_IN when clear_ram = '0' else x"20";
	-----------------------------------------------------------------------
	
end Behavioral;