Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] LCD Display VHDL

Status
Not open for further replies.

Guest144

Newbie
Joined
Dec 2, 2020
Messages
3
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
34
Hi, I'm fairly new to VHDL and I'm currently testing to write a letter to an LCD display, however I've had no luck. The measured signals are correct but nothing is being displayed, any idea where I might be going wrong. Any advice is appreciated, the FPGA I'm using is the Nexys A7.

Code:
--================================================================
-- LCD state machine controlling the DisplayTech 162B controller
--================================================================
Library IEEE;
use ieee.std_logic_1164.all;

entity LCD_test is
    generic(
                clk_e : integer := 100e6/1000  --- Amount of bits for the enable signal
        );
    port(
            CLK100MHZ, reset : in std_logic;
            RS,RW              : out std_logic; -- RS: register select RW: Register Write
            E                 : buffer std_logic; -- Enable signal for the LCD
            data              : out std_logic_vector(7 downto 0) -- Data sent to the LCD
        );

end entity;

architecture arch of LCD_test is
type state is (functionset1, functionset2,functionset3,functionset4, display_off, clear, entry_mode,display_on, write_data, returnhome);
signal old_state, new_state: state;

begin
    
    RW <= '0'; -- Read: 1, Write: 0

--=================================================
--        Generating a 500Hz enable signal (E)
--=================================================
    process(CLK100MHZ,reset)
        variable cnt : integer;
    begin
        if reset = '1' then
            cnt := 0;
        elsif rising_edge(CLK100MHZ) then
            cnt := cnt + 1;
            E <= '0';
            if cnt = clk_e then
                  cnt := 0;
                  E <= '1'; --- Creates a clocksignal
            end if; -- Creation of E
        end if; -- Reset   
    end process;


--======================================================
--     State register controlling the transisions
--=====================================================

    process(CLK100MHZ, reset)
    begin
        if reset = '1' then
            old_state <= functionset1;
         elsif E = '1' then   --- Find alternative to rising_edge due to mitigate the risk of asycnhronous designs during synthesis
            old_state <= new_state;
        end if;
    end process;
--==================================================
--  State machine controlling the display
--==================================================
    process(old_state)
    begin
        case old_state is
                 when functionset1 =>
                RS <= '0';
                data <= "00111000"; -- 8-bit interface, 1-line, 5x8 Font
                 new_state <= functionset2;
            when functionset2 =>
                RS <= '0';
                data <= "00111000"; -- 8-bit interface, 1-line, 5x8 Font
                new_state <= functionset3;
            when functionset3 =>
                RS <= '0';
                data <= "00111000"; -- 8-bit interface, 1-line, 5x8 Font
                new_state <= functionset4;
            when functionset4 =>
                RS <= '0';
                data <= "00111000"; -- 8-bit interface, 1-line, 5x8 Font
                new_state <= display_off;
            when display_off =>
                RS <= '0';
                data <= "00001000"; -- Display OFF, Cursor OFF, Blink OFF
                 new_state <= clear;
            when clear =>
                RS <= '0';
                 data <= "00000001"; -- Clear Diplay
                 new_state <= entry_mode;
            when entry_mode =>
                RS <= '0';
                 data <= "00000111"; -- Increment and shift the display to the right
                 new_state <= display_on;
            when display_on =>
                RS <= '0';
                data <= "00001100"; -- Display ON, Cursor OFF, Blink OFF
                 new_state <= write_data;
            when write_data =>
                RS <= '0';
                 data <= "10000101"; -- Letter X
                 new_state <= returnhome;
            when returnhome =>
                RS <= '0';
                data <= "10000000";-- Returns to display again
                 new_state <= write_data;
                end case;
    end process;   
end arch;
 

If the measured signals are correct, then you should be displaying something, right? So, you must have defective hardware.

Or, you’re assection that the “signals are correct” is defective.

Verify that you’re sending the proper data. verify the timing of your signals. Verify your voltages. Adjust your contrast.
 

You have a bunch of code, but where is your testbench? If you don't "test" it before trying it on hardware it is unlikely to work.

You are creating a mess with your "elsif E = '1' then --- Find alternative to rising_edge due to mitigate the risk of asycnhronous designs during synthesis" code. You are creating an asynchronous FSN design by doing this.

You should use the E as an enable and use a rising_edge (CLK100MHZ) so the design stays synchronous.

Also I would expect some sort of protocol for communicating with the display not just sending a series of bytes with no handshake of any kind.

I think you need to read the documentation for the Nexys A7 before trying anything else.
 

Your"state machine controlling the display" is completely asynchronous. You need to make the process sensitive to clock.
 
Your"state machine controlling the display" is completely asynchronous. You need to make the process sensitive to clock.
I was supposed to be synchronous as the FSM is a two process FSM design but they removed the rising_edge(CLK100MHZ) from the synchronous part of the FSM, which they didn't do a good job of explaining why they did that and the comment one the E ='1' line indicates they don't understand the difference between asynchronous and synchronous design.
 

The asynchronous state machine will step through more than one state per E pulse.

The timing of the enable signal is inappropriate to drive a LCD display. You have 10 ns pulse width, but at least several 100 ns are required. Review the display data sheet.
 

The asynchronous state machine will step through more than one state per E pulse.

The timing of the enable signal is inappropriate to drive a LCD display. You have 10 ns pulse width, but at least several 100 ns are required. Review the display data sheet.
I noticed this too. The OP's code makes 'E' active only when cnt equals clk_e and disables it otherwise.

The duration that 'E' remains active could be extended like this:
Code:
    process(CLK100MHZ,reset)
        variable cnt : integer;
    begin
        if reset = '1' then
            cnt := 0;
        elsif rising_edge(CLK100MHZ) then
            If  (cnt = clk_e-1) then
                   cnt := 0;
            else
                   cnt := cnt + 1;
            end if;
            if (cnt = clk_e-1) or (cnt < E_width-1) then --where E_width is the number of clk100MHZ pulses that makes up the duration that 'E' needs to be high, example if E_width = 5 then E will be high for 50ns with your 10ns CLK100MHZ clock.
                  E <= '1'; -- makes E active
            else
                  E <= '0'; -- makes E inactive
            end if; -- Creation of E
        end if; -- Reset 
    end process;
--- Updated ---

You can make both clk_e-1 and E_width-1 constants.
--- Updated ---

I was supposed to be synchronous as the FSM is a two process FSM design but they removed the rising_edge(CLK100MHZ) from the synchronous part of the FSM, which they didn't do a good job of explaining why they did that and the comment one the E ='1' line indicates they don't understand the difference between asynchronous and synchronous design.
Yes it's supposed to be synchronous.

By making the FSM asynchronous, you leave the time that the FSM transitions between states to be determined by propagation delays.
--- Updated ---

Also, your value of clk_e creates unnecessary logic complication. I would prefer to stick to mod-2^n counter counters rather than something inbetween, unless it's unavoidable.
 
Last edited:

Hi guys,

Thanks for the advice regarding the display, I managed to get hold of a display tester from a friend and it seems that the display was broken. I got another one from him to try and still no luck. After consulting the datasheet it seems that there is a problem where the data is held not long enough. The new display required modifications to the code regarding the state machine, my guess is that I need to hold the data for a certain of time for it to be acknowledged. The LCD is the **broken link removed**. Again, I appreciate the advice given as it has helped me understand where some of the errors were occurring.
Code:
--================================================================
-- LCD state machine controlling the LCD controller
--================================================================
Library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity LCD is
    
    port(
            CLK100MHZ, reset : in std_logic;
            RS,RW              : out std_logic; -- RS: register select RW: Register Write
            E                  : out std_logic; -- Enable signal for the LCD
            data              : out std_logic_vector(7 downto 0) -- Data sent to the LCD
        );

end entity;

architecture arch of LCD is
type state is (initialization, write_data, returnhome, enable);
signal old_state, new_state: state;
signal delay             : std_logic_vector(20 downto 0);  --- counter handling the delay
constant delay_15ms     : integer := 1500000;               --- Delay of power on
constant delay_4ms      : integer := 410000;              --- Delay of first function set cmd
constant delay_100us    : integer := 10000;                  --- Delay of second function set command
constant delay_5000ns   : integer := 500;                   --- Enable cycle time
constant delay_1000ns   : integer := 100;                  --- Enable pulse width
constant delay_200ns    : integer := 20;                  --- Set up time for E
signal     commands         : std_logic_vector(2 downto 0);      --- Steps through the initialization commands

begin
    
    process(CLK100MHZ, reset)
    begin
        if reset = '1' then
            delay <= (others => '0');
            commands <= (others => '0');
            E <= '1';
            RS <= '0';
            RW <= '0';
            data <= (others => '0');
            old_state <= initialization;
            new_state <= initialization;
        elsif rising_edge(CLK100MHZ) then
             delay <= delay + 1;
             E <= '1';
            case old_state is
                 when initialization =>
                       RS <= '0';
                       RW <= '0';
                       if(delay >= delay_15ms) AND (commands = "000") then  --- Power up delay
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00110000"; -- Function set command
                           old_state <= enable;
                          new_state <= initialization;
                       elsif (delay >= delay_4ms) AND(commands = "001") then
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00110000"; -- Function set command
                           old_state <= enable;
                          new_state <= initialization;       
                          elsif (delay >= delay_100us) AND(commands = "010") then
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00110000"; -- Function set command
                           old_state <= enable;
                          new_state <= initialization;   
                        elsif (commands = "011") then --- function set for interface   
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00110100"; -- 8-bit interface, 1-line, 5x10 Font
                           old_state <= enable;
                          new_state <= initialization;

                        elsif (commands = "100") then --- Display off   
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00001000"; -- Display OFF command
                           old_state <= enable;
                          new_state <= initialization;

                        elsif (commands = "101") then ---  Clear Display
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00000001"; -- 8-bit interface, 1-line, 5x8 Font
                           old_state <= enable;
                          new_state <= initialization;

                        elsif (commands = "110") then --- Entry Mode Set
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00000111"; -- Increment and shift to the left
                           old_state <= enable;
                          new_state <= initialization;

                        elsif (commands = "111") then --- Display ON
                          delay <= (others => '0');
                          commands <= commands + 1;
                            data <= "00001111"; -- Display ON, Cursor ON, Blink ON
                           old_state <= enable;
                          new_state <= write_data;
                        end if;
                when write_data =>
                      delay <= (others => '0');
                      RW <= '0';
                      RS <= '1';
                       data <= "10000101"; -- Letter X
                      old_state <= enable;
                       new_state <= returnhome;
                when returnhome =>
                      delay <= (others => '0');
                      RW <= '0';
                      RS <= '0';
                      data <= "00000010";-- Returns to address "00H" to display again
                      old_state <= enable;
                       new_state <= write_data;
                when enable =>
                     if delay < delay_5000ns then
                        if (delay >= delay_200ns) AND (delay <= delay_1000ns) then
                            E <= '0';
                        end if;
                     else
                         delay <= (others => '0');
                         RW <= '0';
                         old_state <= new_state;
                     end if;
                end case;
            end if;
    end process;   
end arch;
 

Now you have both the old_state and the new_state both in the same edge triggered process. Make up your mind if you want a one process FSM or a incorrectly written two process FSM (your original posted code)!


Here is a nice explanation of the differences between a one process and two process FSM along with simulation waveforms to show how they behave differently. The writer does a good job of explaining them.
https://vhdlwhiz.com/n-process-state-machine/

Your approach suffers from both not having learned how to code basic VHDL structures like FSMs (and perhaps even multiplexers, registers, decoders, etc) and not having blocked out the overall structure of the design before writing VHDL code.

The FSM should have been a directly translated from timing diagrams of the LCD control signals and the clock cycles needed to meet both the setup and hold times of the signals to/from the LCD. You shouldn't have written any code for this until you did the up front design work first.
 

    Guest144

    Points: 2
    Helpful Answer Positive Rating
After reading through the link you gave me I managed to display something on the Display. Thank you so much for your help.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top