-------------------------------------------------------------------------------- -- LCD.vhd -- LCD driver -- -- 86 (7%), 40.8ns, 24.50MHz (7.21) -- 29 (6%), 34.6ns, 28.90MHz (8.1) -- -- This driver converts instructions from the main control unit into -- instructions that the LCD can understand. -------------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; library work; use work.Constants_Package.all; use work.Control_Package.all; use work.LCD_Package.all; entity LCD is port( clock: in std_logic; reset: in std_logic; -- *** Nov 16, sat: added pin to reset LCD driver -- incoming instructions from Control -- currentState: in std_logic_vector(STATE_WIDTH-1 downto 0); -- the current state that the LCD screen should be set to. -- presently there are 17 states as described in the lab report character: in std_logic_vector(CHAR_WIDTH-1 downto 0); instruction: in std_logic_vector(LCD_INSTRUCTION_WIDTH-1 downto 0); -- the instruction given by the Control Entity to the LCD entity -- 000 - no action -- 001 - print the character signified by the character input vector -- 010 - backup cursor by one, erase the previous character -- 011 - forward cursor by one -- 100 - draw starting screen of current state -- 101 - reset; perform LCD chip powerup sequence -- outgoing instructions to the LCD display DB: out std_logic_vector(LCD_DB_WIDTH-1 downto 0); -- data byte RS: out std_logic; -- register select: 0 - instruction, 1 - data RW: out std_logic; -- read/write select: 0 - writing, 1 -reading E: out std_logic; -- operation enable signal -- the LCDready signal going out to control LCDready: buffer std_logic ); end LCD; architecture behaviour of LCD is signal start_instruction: std_logic; signal instruction_active: std_logic; signal instruction_done : std_logic; signal reset_delaytimer : std_logic; signal delay_zero: std_logic_vector (LCD_DELAY_COUNTER_WIDTH-1 downto 0); signal delay_end: std_logic_vector (LCD_DELAY_COUNTER_WIDTH-1 downto 0); -- The enable (E) pulse must last at least 450ns. constant ENABLE_TIMER_WIDTH: positive := 2; constant DURATION_OF_ENABLE_PULSE: std_logic_vector(ENABLE_TIMER_WIDTH-1 downto 0) := "10"; signal enable_timer: std_logic_vector(ENABLE_TIMER_WIDTH-1 downto 0); signal enable_pulse_active: std_logic; begin delay_zero <= ZERO; delay_end <= LCD_DELAY; -- This next line is for testing, so that we don't wait an eternity --delay_end <= "00000000000000011"; RW <= '0'; -- never need to read from LCD receive: process(clock) begin if rising_edge(clock) then -- all actions are synchronous, and so they need the clock edge if reset = '1' then -- reset driver to a known state LCDready <= '1'; start_instruction <= '0'; E <= '0'; RS <= '0'; DB <= (others => '0'); reset_delaytimer <= '1'; instruction_active <= '0'; elsif instruction_done = '1' then instruction_active <= '0'; LCDready <= '1'; reset_delaytimer <= '1'; DB <= (others => '0'); RS <= '0'; elsif instruction_active = '1' then if enable_pulse_active = '1' then enable_timer <= enable_timer + '1'; if enable_timer = DURATION_OF_ENABLE_PULSE then enable_pulse_active <= '0'; E <= '0'; end if; end if; -- pulse active elsif start_instruction = '1' then -- force a falling edge on E so that the LCD H/W acts on our command E <= '1'; enable_pulse_active <= '1'; instruction_active <= '1'; start_instruction <= '0'; else reset_delaytimer <= '0'; -- Nov 16, sat E <= '0'; -- Nov 17, sat: keep enable normally high if LCDready = '1' then if instruction /= NO_ACTION then LCDready <= '0'; start_instruction <= '1'; case instruction is when PRINT_CHAR => -- output single character RS <= '1'; -- data write command for LCD DB <= character; -- output the character directly when BACKUP_CURSOR => -- back up the cursor RS <= '0'; -- display cursor shift command DB <= DB_BCUR; -- output shift cursor left command when ADVANCE_CURSOR => -- forward the cursor RS <= '0'; DB <= DB_FCUR; -- output shift cursor right command when REFRESH_SCREEN => -- clear screen RS <= '0'; DB <= DB_CLEAR; when CURSOR_HOME => -- home the cursor RS <= '0'; DB <= DB_HOME; when GOTO_LINE2 => -- home the cursor on line 2 RS <= '0'; DB <= DB_LINE2; when LCD_FUNCTION_SET => -- reset the cursor display, assume power on just -- occurred on LCD RS <= '0'; DB <= DB_FUNC; when LCD_DISPLAY_ON => -- reset the cursor display, assume power on just -- occurred on LCD RS <= '0'; DB <= DB_ONDISP; when LCD_ENTRY_MODE => -- reset the cursor display, assume power on just -- occurred on LCD RS <= '0'; DB <= DB_ENTRY; when others => -- same as do nothing RS <= '0'; DB <= (others => '0'); end case; -- instruction else -- NO_ACTION -- do nothing LCDready <= '1'; RS <= '0'; DB <= (others => '0'); end if; end if; -- LCDready = '1' end if; -- reset | instruction_done | else end if; -- rising_edge(clock) end process receive; delay: LCD_DelayTimer port map ( clock => clock, reset => reset_delaytimer, overflow => instruction_done, count_enable => instruction_active, Min_count => delay_zero, Max_count => delay_end ); end behaviour;