1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

[Resolved] LCD lcd interfacing with Elbert V2

Discussion in 'FPGA Boards' started by skyfall_133, Aug 28, 2017.

Thread Status:
Not open for further replies.
  1. skyfall_133

    skyfall_133 New Member

    Joined:
    Aug 28, 2017
    Messages:
    5
    Likes Received:
    0
    Trophy Points:
    1
    Gender:
    Male
    Location:
    Vietnam
  2. rohith

    rohith Administrator Staff Member

    Joined:
    Mar 3, 2016
    Messages:
    66
    Likes Received:
    11
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Bangalore
    Hello @skyfall_133,

    For LCD interface, there are few things to keep in mind:
    1. They are generally powered by 5V, even though the Logic Levels might be 3.3V compliant.
    2. Keep the LCD always in write mode, since reading the LCD when it's powered by 5V will kill the FPGA unless suitable level translator circuit in present in between. Check the LCD wiring used on our Waxwing Boards (page 7): https://docs.numato.com/wp-content/uploads/2016/08/WaxwingBasicDevelopmentBoardSch.pdf Its always in "Write" mode
    3. You might need to adjust the contrast

    Thanks
     
  3. Alexander Lang

    Alexander Lang Member

    Joined:
    May 17, 2015
    Messages:
    81
    Likes Received:
    15
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Manchester, UK
    Can you share a few more details. I also had trouble with the LCD display at first. I was using the Mimas V2 board and not Elbert V2 but I have both so I can hopefully provide some assistance.
    • Which language are you writing in - Verliog or VHDL?
    • How have you connected up the LCD Display - please share some photos?
    As Rohith said the default display available from Numato Labs requires an external supply in order to work and power the backlight. I used 7.5 volts to ensure everything was correctly powered:

    Here are my blog posts on the subject. You may find them helpful:

    http://langster1980.blogspot.co.uk/2016/01/numato-mimas-v2-controlling-16x2-lcd.html

    Cheers

    Alex
     
    skyfall_133 and rohith like this.
  4. skyfall_133

    skyfall_133 New Member

    Joined:
    Aug 28, 2017
    Messages:
    5
    Likes Received:
    0
    Trophy Points:
    1
    Gender:
    Male
    Location:
    Vietnam
    thank @rohith and @Alexander. I read your answer, and i still dont know what i make wrong. This is code i try to test my LCD.
    *Verilog:

    Code:
    `timescale 1ns / 1ps
    
    // 16 X 2 LCD display module demo code
    // Numato Lab
    // http://www.numato.com
    // http://www.numato.cc
    // License : CC BY-SA (http://creativecommons.org/licenses/by-sa/2.0/)
    
    module LCDExpansionModule(
    
      // Assuming 12MHz input clock. My need to adjust the counter below
      // if any other input frequency is used
        input Clk,
      
    // Register Select(rs), Enable(en), Read/Write(rw) and Data Pins of LCD    
        output reg rs,en,rw,                               
        output reg [3:0] LCD_out                           
         );
    
       // Increase the clock frequency
       wire clkout;
       clock_gen  c1 (.CLK_IN(Clk),
                      .RST_IN(1'b0),
                      .CLK_OUT(clkout));
                    
    // Specify the depth of block memory where the data and command are to be saved.
    parameter Length=43;
    
    // Intermediate register used internally                                   
    reg [30:0]counter;                                     
    integer pointer;                                       
    reg count;           
    
    // Memory created to store the required data of 8 bits and two rs and rw signals                                
    wire [9:0]memory[0:Length-1];                
           
    // Implementation :
    // 16 x 2 LCD that is liquid crystal display is used to display Alphanumeric and special Character.
    // LCD here is made to work on 4 bits mode i.e, data/command pins (DB4-DB7), thus reducing the requirement of IO's.
    // For displaying this character on the LCD, initializing LCD is very important.That is done by passing the predefine initialization command to LCD.
    // Here the character which are to be displayed on the LCD are stored in the memory with the initialization command.
    // The first two MSB bit's represent the status on rs and rw register respectively.
    // Whenever rs is '0' then it's a command for LCD and when '1' it's data that should be displayed on LCD.
    // The rw pin is set to '0' for write operation and '1' for read operation.
                                                          
    // Function Set: 4-bit, 2 Line, 5x7 Dots.
    assign     memory[0]={2'b00,8'h28};
          
    // Entry Mode.                     
    assign     memory[2]={2'b00,8'h0E};
    // Display on Cursor on.                            
    assign     memory[1]={2'b00,8'h06};
        
    // Clear Display (also clear DDRAM content).                      
    assign     memory[3]={2'b00,8'h01};
    // The memory location in middle are kept empty so that the LCD is properly initialized and is ready to expect any data that is thrown to it.
    // Space that should appear on LCD.
    assign     memory[18]={2'b10," "};
    
    // Shift the display right.
    assign     memory[19]={2'b00,8'h1C};
    
    // Character that should be displayed on the LCD.
    assign     memory[20]={2'b10,"W"};
    assign     memory[21]={2'b10,"E"};
    assign     memory[22]={2'b10,"L"};
    assign     memory[23]={2'b10,"C"};
    assign     memory[24]={2'b10,"O"};
    assign     memory[25]={2'b10,"M"};
    assign     memory[26]={2'b10,"E"};
    assign     memory[27]={2'b10," "};
    assign     memory[28]={2'b10,"T"};
    assign     memory[29]={2'b10,"O"};
    
    // Shift to second Line of LCD
    assign     memory[30]={2'b00,8'h40};
    
    // Space that should appear on LCD.                            
    assign     memory[31]={2'b10," "};
    
    // Shift the display right
    assign     memory[32]={2'b00,8'h1C};                            
    
    // Character that should be displayed on the LCD.    
    assign     memory[33]={2'b10,"N"};
    assign     memory[34]={2'b10,"U"};
    assign     memory[35]={2'b10,"M"};
    assign     memory[36]={2'b10,"A"};
    assign     memory[37]={2'b10,"T"};
    assign     memory[38]={2'b10,"O"};
    assign     memory[39]={2'b10," "};
    
    assign     memory[40]={2'b10,"L"};
    assign     memory[41]={2'b10,"A"};
    assign     memory[42]={2'b10,"B"};
    
    // Scale down the the clock such that it satisfy the timing characteristics of LCD
    always @(posedge clkout)
    begin
         counter=counter+1'b1;
       
    // Check if all the memory location are covered.
    // Once it's done disable the enable pin has no more data has to be send.   
         if(pointer==Length+1)                                
            en = 'b0;
          else
            en=counter[15];                                              
    end                                                      
    
    // Depending on the edge of the enable(en) signal send the command and data to LCD
    always @(negedge en)                                       
      begin
    
    // MSB bit of a memory location is Register select(rs) signal  
         rs=memory[pointer][9];                               
    
    // Only the write operation is performed so permanently send active low signal to rw pin.   
         rw=1'b0;                                             
       
    // First send the upper 4 bits data/command and then then lower 4 bits data/command.      
       if (!count)
          begin
            LCD_out=memory[pointer][7:4];
             count=count+1'b1;
          end
        else
           begin
              LCD_out=memory[pointer][3:0];
            
    // Once the data/command is send move to next location.          
              pointer=pointer+1;
              count=count+1'b1;
          end                                   
      end                                                          
    endmodule                 
    Code:
    `timescale 1ns / 1ps
    
    module clock_gen(CLK_IN,
                     RST_IN,
                     CLK_OUT,
                     CLKIN_IBUFG_OUT,
                     CLK0_OUT);
    
        input CLK_IN;
        input RST_IN;
       output CLK_OUT;
       output CLKIN_IBUFG_OUT;
       output CLK0_OUT;
     
       wire CLKFB_IN;
       wire CLKFX_BUF;
       wire CLKIN_IBUFG;
       wire CLK0_BUF;
       wire GND_BIT;
     
       assign GND_BIT = 0;
       assign CLKIN_IBUFG_OUT = CLKIN_IBUFG;
       assign CLK0_OUT = CLKFB_IN;
       BUFG  CLKFX_BUFG_INST (.I(CLKFX_BUF),
                             .O(CLK_OUT));
       IBUFG  CLKIN_IBUFG_INST (.I(CLK_IN),
                               .O(CLKIN_IBUFG));
       BUFG  CLK0_BUFG_INST (.I(CLK0_BUF),
                            .O(CLKFB_IN));
       DCM_SP #( .CLK_FEEDBACK("1X"), .CLKDV_DIVIDE(2.0), .CLKFX_DIVIDE(3),
             .CLKFX_MULTIPLY(25), .CLKIN_DIVIDE_BY_2("FALSE"),
             .CLKIN_PERIOD(83.333), .CLKOUT_PHASE_SHIFT("NONE"),
             .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), .DFS_FREQUENCY_MODE("LOW"),
             .DLL_FREQUENCY_MODE("LOW"), .DUTY_CYCLE_CORRECTION("TRUE"),
             .FACTORY_JF(16'hC080), .PHASE_SHIFT(0), .STARTUP_WAIT("FALSE") )
             DCM_SP_INST (.CLKFB(CLKFB_IN),
                           .CLKIN(CLKIN_IBUFG),
                           .DSSEN(GND_BIT),
                           .PSCLK(GND_BIT),
                           .PSEN(GND_BIT),
                           .PSINCDEC(GND_BIT),
                           .RST(RST_IN),
                           .CLKDV(),
                           .CLKFX(CLKFX_BUF),
                           .CLKFX180(),
                           .CLK0(CLK0_BUF),
                           .CLK2X(),
                           .CLK2X180(),
                           .CLK90(),
                           .CLK180(),
                           .CLK270(),
                           .LOCKED(),
                           .PSDONE(),
                           .STATUS());
    endmodule
    *This is how i connect Elbert V2 to my lcd.

    Code:
      NET "en"               LOC = P28  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Enable PIN of LCD
         NET "rs"               LOC = P32  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Register Select PIN of LCD
         NET "rw"               LOC = P31  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Read/Write PIN of LCD
      
      # Four bits LCD Data
         NET "LCD_out[3]"       LOC = P30  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 14 on LCD
         NET "LCD_out[2]"       LOC = P27  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 13 on LCD
         NET "LCD_out[1]"       LOC = P29  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 12 on LCD
         NET "LCD_out[0]"       LOC = P24  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 11 on LCD   
    
    *This is real connection:
    I use pin 3v3 and GND of P to connect to pin A,K of LCD. The power i use for lcd is 5V.
    [​IMG]
    https://drive.google.com/open?id=0B8WdpMCgmg49a3ptWFY4RTRCVDA
     
  5. rohith

    rohith Administrator Staff Member

    Joined:
    Mar 3, 2016
    Messages:
    66
    Likes Received:
    11
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Bangalore
    Hello @skyfall_133

    Assuming the hardware connections and power supply are correct, and also constraints are correct, then the only plausible point to suspect is the LCD display itself. Sometime we have also encountered this due to varying LCDs with differing timing specs available in the market.

    So, in this code below:

    Please try changing counter[15] with `counter[16]` in the code and see if this works or not. It will give more time to LCD to respond.
     
    skyfall_133 likes this.
  6. skyfall_133

    skyfall_133 New Member

    Joined:
    Aug 28, 2017
    Messages:
    5
    Likes Received:
    0
    Trophy Points:
    1
    Gender:
    Male
    Location:
    Vietnam
    I used to try what you said to me. But nothing changed. I stuck on this.
     
  7. Alexander Lang

    Alexander Lang Member

    Joined:
    May 17, 2015
    Messages:
    81
    Likes Received:
    15
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Manchester, UK
    @skyfall_133 - I'm just testing the code on my setup. Once I have something to share I will be in touch.
     
    skyfall_133 likes this.
  8. Alexander Lang

    Alexander Lang Member

    Joined:
    May 17, 2015
    Messages:
    81
    Likes Received:
    15
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Manchester, UK
    Ok...so I have some good news, and some bad news.

    I did get something working! I found that the sample verilog code just does not work. I'm not good at verilog to be honest and I just couldn't get it to work. I checked the UCF file and found that to be correct. I wrote some VHDL Code which worked!

    Here is my VHDL code:

    Code:
    library ieee;
    use ieee.std_logic_1164.all;
    
    -------------------------------------------------------------------------------
    
    entity lcd16x2_ctrl_demo is
      port (
        clk    : in  std_logic;
        lcd_e  : out std_logic;
        lcd_rs : out std_logic;
        lcd_rw : out std_logic;
        lcd_db : out std_logic_vector(7 downto 4));
    
    end entity lcd16x2_ctrl_demo;
    
    -------------------------------------------------------------------------------
    
    architecture behavior of lcd16x2_ctrl_demo is
    
      --
      signal timer : natural range 0 to 100000000 := 0;
      signal switch_lines : std_logic := '0';
      signal line1 : std_logic_vector(127 downto 0);
      signal line2 : std_logic_vector(127 downto 0);
    
      -- component generics
      constant CLK_PERIOD_NS : positive := 10;  -- 100 Mhz
    
      -- component ports
      signal rst          : std_logic;
      signal line1_buffer : std_logic_vector(127 downto 0);
      signal line2_buffer : std_logic_vector(127 downto 0);
    
    begin  -- architecture behavior
    
      -- component instantiation
      DUT : entity work.lcd16x2_ctrl
        generic map (
          CLK_PERIOD_NS => CLK_PERIOD_NS)
        port map (
          clk          => clk,
          rst          => rst,
          lcd_e        => lcd_e,
          lcd_rs       => lcd_rs,
          lcd_rw       => lcd_rw,
          lcd_db       => lcd_db,
          line1_buffer => line1_buffer,
          line2_buffer => line2_buffer);
    
      rst <= '0';
    
      -- see the display's datasheet for the character map
      line1(127 downto 120) <= X"4E";  -- N
      line1(119 downto 112) <= X"75";  -- u
      line1(111 downto 104) <= X"6D";  -- m
      line1(103 downto 96)  <= X"61";  -- a
      line1(95 downto 88)   <= X"74";  -- t
      line1(87 downto 80)   <= X"6F";  -- o
      line1(79 downto 72)   <= X"20";  -- space
      line1(71 downto 64)   <= X"4C";  -- L
      line1(63 downto 56)   <= X"61";  -- a
      line1(55 downto 48)   <= X"62";  -- b
      line1(47 downto 40)   <= X"73";  -- s
      line1(39 downto 32)   <= X"20";  -- space
      line1(31 downto 24)   <= X"46";  -- F
      line1(23 downto 16)   <= X"50";  -- P
      line1(15 downto 8)    <= X"47";  -- G
      line1(7 downto 0)     <= X"41";  -- A
    
      line2(127 downto 120) <= X"56";  -- V
      line2(119 downto 112) <= X"48";  -- H
      line2(111 downto 104) <= X"44";  -- D
      line2(103 downto 96)  <= X"4C";  -- L
      line2(95 downto 88)   <= X"20";  -- space
      line2(87 downto 80)   <= X"4C";  -- L
      line2(79 downto 72)   <= X"43";  -- C
      line2(71 downto 64)   <= X"44";  -- D
      line2(63 downto 56)   <= X"20";  -- space
      line2(55 downto 48)   <= X"45";  -- E
      line2(47 downto 40)   <= X"78";  -- x
      line2(39 downto 32)   <= X"61";  -- a
      line2(31 downto 24)   <= X"6D";  -- m
      line2(23 downto 16)   <= X"70";  -- p
      line2(15 downto 8)    <= X"6C";  -- l
      line2(7 downto 0)     <= X"65";  -- e
    
      line1_buffer <= line2 when switch_lines = '1' else line1;
      line2_buffer <= line1 when switch_lines = '1' else line2;
    
      -- switch lines every second
      process(clk)
      begin
        if rising_edge(clk) then
          if timer = 0 then
            timer <= 100000000;
            switch_lines <= not switch_lines;
          else
            timer <= timer - 1;
          end if;
        end if;
        
      end process;
    end architecture behavior;
    
    Code:
    library ieee;
    use ieee.std_logic_1164.all;
    
    entity lcd16x2_ctrl is
      generic (
        CLK_PERIOD_NS : positive := 20);    -- 50MHz
    
      port (
        clk          : in  std_logic;
        rst          : in  std_logic;
        lcd_e        : out std_logic;
        lcd_rs       : out std_logic;
        lcd_rw       : out std_logic;
        lcd_db       : out std_logic_vector(7 downto 4);
        line1_buffer : in  std_logic_vector(127 downto 0);  -- 16x8bit
        line2_buffer : in  std_logic_vector(127 downto 0));
    
    end entity lcd16x2_ctrl;
    
    architecture rtl of lcd16x2_ctrl is
    
      constant DELAY_15_MS   : positive := 15 * 10**6 / CLK_PERIOD_NS + 1;
      constant DELAY_1640_US : positive := 1640 * 10**3 / CLK_PERIOD_NS + 1;
      constant DELAY_4100_US : positive := 4100 * 10**3 / CLK_PERIOD_NS + 1;
      constant DELAY_100_US  : positive := 100 * 10**3 / CLK_PERIOD_NS + 1;
      constant DELAY_40_US   : positive := 40 * 10**3 / CLK_PERIOD_NS + 1;
    
      constant DELAY_NIBBLE     : positive := 10**3 / CLK_PERIOD_NS + 1;
      constant DELAY_LCD_E      : positive := 230 / CLK_PERIOD_NS + 1;
      constant DELAY_SETUP_HOLD : positive := 40 / CLK_PERIOD_NS + 1;
    
      constant MAX_DELAY : positive := DELAY_15_MS;
    
      -- this record describes one write operation
      type op_t is record
        rs      : std_logic;
        data    : std_logic_vector(7 downto 0);
        delay_h : integer range 0 to MAX_DELAY;
        delay_l : integer range 0 to MAX_DELAY;
      end record op_t;
      constant default_op      : op_t := (rs => '1', data => X"00", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
      constant op_select_line1 : op_t := (rs => '0', data => X"80", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
      constant op_select_line2 : op_t := (rs => '0', data => X"C0", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
    
      -- init + config operations:
      -- write 3 x 0x3 followed by 0x2
      -- function set command
      -- entry mode set command
      -- display on/off command
      -- clear display
      type config_ops_t is array(0 to 5) of op_t;
      constant config_ops : config_ops_t
        := (5 => (rs => '0', data => X"33", delay_h => DELAY_4100_US, delay_l => DELAY_100_US),
            4 => (rs => '0', data => X"32", delay_h => DELAY_40_US, delay_l => DELAY_40_US),
            3 => (rs => '0', data => X"28", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
            2 => (rs => '0', data => X"06", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
            1 => (rs => '0', data => X"0C", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
            0 => (rs => '0', data => X"01", delay_h => DELAY_NIBBLE, delay_l => DELAY_1640_US));
    
      signal this_op : op_t;
    
      type op_state_t is (IDLE,
                          WAIT_SETUP_H,
                          ENABLE_H,
                          WAIT_HOLD_H,
                          WAIT_DELAY_H,
                          WAIT_SETUP_L,
                          ENABLE_L,
                          WAIT_HOLD_L,
                          WAIT_DELAY_L,
                          DONE);
    
      signal op_state      : op_state_t := DONE;
      signal next_op_state : op_state_t;
      signal cnt           : natural range 0 to MAX_DELAY;
      signal next_cnt      : natural range 0 to MAX_DELAY;
    
      type state_t is (RESET,
                       CONFIG,
                       SELECT_LINE1,
                       WRITE_LINE1,
                       SELECT_LINE2,
                       WRITE_LINE2);
    
      signal state      : state_t               := RESET;
      signal next_state : state_t;
      signal ptr        : natural range 0 to 15 := 0;
      signal next_ptr   : natural range 0 to 15;
    
    begin
    
      proc_state : process(state, op_state, ptr, line1_buffer, line2_buffer) is
      begin
        case state is
          when RESET =>
            this_op    <= default_op;
            next_state <= CONFIG;
            next_ptr   <= config_ops_t'high;
          
          when CONFIG =>
            this_op    <= config_ops(ptr);
            next_ptr   <= ptr;
            next_state <= CONFIG;
            if op_state = DONE then
              next_ptr <= ptr - 1;
              if ptr = 0 then
                next_state <= SELECT_LINE1;
              end if;
            end if;
    
          when SELECT_LINE1 =>
            this_op  <= op_select_line1;
            next_ptr <= 15;
            if op_state = DONE then
              next_state <= WRITE_LINE1;
            else
              next_state <= SELECT_LINE1;
            end if;
    
          when WRITE_LINE1 =>
            this_op      <= default_op;
            this_op.data <= line1_buffer(ptr*8 + 7 downto ptr*8);
            next_ptr     <= ptr;
            next_state   <= WRITE_LINE1;
            if op_state = DONE then
              next_ptr <= ptr - 1;
              if ptr = 0 then
                next_state <= SELECT_LINE2;
              end if;
            end if;
    
          when SELECT_LINE2 =>
            this_op  <= op_select_line2;
            next_ptr <= 15;
            if op_state = DONE then
              next_state <= WRITE_LINE2;
            else
              next_state <= SELECT_LINE2;
            end if;
    
          when WRITE_LINE2 =>
            this_op      <= default_op;
            this_op.data <= line2_buffer(ptr*8 + 7 downto ptr*8);
            next_ptr     <= ptr;
            next_state   <= WRITE_LINE2;
            if op_state = DONE then
              next_ptr <= ptr - 1;
              if ptr = 0 then
                next_state <= SELECT_LINE1;
              end if;
            end if;
          
        end case;
      end process proc_state;
    
      reg_state : process(clk)
      begin
        if rising_edge(clk) then
          if rst = '1' then
            state <= RESET;
            ptr   <= 0;
          else
            state <= next_state;
            ptr   <= next_ptr;
          end if;
        end if;
      end process reg_state;
    
      -- we never read from the lcd
      lcd_rw <= '0';
    
      proc_op_state : process(op_state, cnt, this_op) is
      begin
        case op_state is
          when IDLE =>
            lcd_db        <= (others => '0');
            lcd_rs        <= '0';
            lcd_e         <= '0';
            next_op_state <= WAIT_SETUP_H;
            next_cnt      <= DELAY_SETUP_HOLD;
          
          when WAIT_SETUP_H =>
            lcd_db <= this_op.data(7 downto 4);
            lcd_rs <= this_op.rs;
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= ENABLE_H;
              next_cnt      <= DELAY_LCD_E;
            else
              next_op_state <= WAIT_SETUP_H;
              next_cnt      <= cnt - 1;
            end if;
    
          when ENABLE_H =>
            lcd_db <= this_op.data(7 downto 4);
            lcd_rs <= this_op.rs;
            lcd_e  <= '1';
            if cnt = 0 then
              next_op_state <= WAIT_HOLD_H;
              next_cnt      <= DELAY_SETUP_HOLD;
            else
              next_op_state <= ENABLE_H;
              next_cnt      <= cnt - 1;
            end if;
    
          when WAIT_HOLD_H =>
            lcd_db <= this_op.data(7 downto 4);
            lcd_rs <= this_op.rs;
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= WAIT_DELAY_H;
              next_cnt      <= this_op.delay_h;
            else
              next_op_state <= WAIT_HOLD_H;
              next_cnt      <= cnt - 1;
            end if;
    
          when WAIT_DELAY_H =>
            lcd_db <= (others => '0');
            lcd_rs <= '0';
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= WAIT_SETUP_L;
              next_cnt      <= DELAY_SETUP_HOLD;
            else
              next_op_state <= WAIT_DELAY_H;
              next_cnt      <= cnt - 1;
            end if;
    
          when WAIT_SETUP_L =>
            lcd_db <= this_op.data(3 downto 0);
            lcd_rs <= this_op.rs;
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= ENABLE_L;
              next_cnt      <= DELAY_LCD_E;
            else
              next_op_state <= WAIT_SETUP_L;
              next_cnt      <= cnt - 1;
            end if;
    
          when ENABLE_L =>
            lcd_db <= this_op.data(3 downto 0);
            lcd_rs <= this_op.rs;
            lcd_e  <= '1';
            if cnt = 0 then
              next_op_state <= WAIT_HOLD_L;
              next_cnt      <= DELAY_SETUP_HOLD;
            else
              next_op_state <= ENABLE_L;
              next_cnt      <= cnt - 1;
            end if;
    
          when WAIT_HOLD_L =>
            lcd_db <= this_op.data(3 downto 0);
            lcd_rs <= this_op.rs;
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= WAIT_DELAY_L;
              next_cnt      <= this_op.delay_l;
            else
              next_op_state <= WAIT_HOLD_L;
              next_cnt      <= cnt - 1;
            end if;
    
          when WAIT_DELAY_L =>
            lcd_db <= (others => '0');
            lcd_rs <= '0';
            lcd_e  <= '0';
            if cnt = 0 then
              next_op_state <= DONE;
              next_cnt      <= 0;
            else
              next_op_state <= WAIT_DELAY_L;
              next_cnt      <= cnt - 1;
            end if;
    
          when DONE =>
            lcd_db        <= (others => '0');
            lcd_rs        <= '0';
            lcd_e         <= '0';
            next_op_state <= IDLE;
            next_cnt      <= 0;
          
        end case;
      end process proc_op_state;
    
      reg_op_state : process (clk) is
      begin
        if rising_edge(clk) then
          if state = RESET then
            op_state <= IDLE;
          else
            op_state <= next_op_state;
            cnt      <= next_cnt;
          end if;
        end if;
      end process reg_op_state;
    
    end architecture rtl;
    
    
    Code:
    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
    # This file is a .ucf for 16x2 LCD Expansion Modules on Elbert V2                           #
    # To use it in your project :                                                               #
    # * Remove or comment the lines corresponding to unused pins                                #
    # * Rename the used signals according to the your project                                   #
    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
    
       # Clock 12 MHz
           NET "clk"             LOC = P129  | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz;
        
    ####################################################################################################################################################
    #                                               16x2 LCD Expansion Module                                                                          #
    ####################################################################################################     NET "lcd_e"           LOC = P28  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Enable PIN of LCD
         NET "lcd_rs"          LOC = P32  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Register Select PIN of LCD
         NET "lcd_rw"          LOC = P31  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # Read/Write PIN of LCD
      
      # Four bits LCD Data
         NET "lcd_db<7>"       LOC = P24  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 14 on LCD
         NET "lcd_db<6>"       LOC = P29  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 13 on LCD
         NET "lcd_db<5>"       LOC = P27  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 12 on LCD
         NET "lcd_db<4>"       LOC = P30  | IOSTANDARD = LVCMOS33  | DRIVE = 8  | SLEW = FAST;                 # PIN 11 on LCD    
    
    There is a picture of what you should expect to see on the display:

    Please try my code and see if it works with your setup.

    @rohith - Please can you look at the example verilog code and see if you can get it to work.

    Hope this helps - Alex
     

    Attached Files:

    rohith likes this.
  9. rohith

    rohith Administrator Staff Member

    Joined:
    Mar 3, 2016
    Messages:
    66
    Likes Received:
    11
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Bangalore
    Hi @Alexander Lang

    Thanks so much for your help and testing!

    About the sample code, our internal testing department regularly use the the same code during Production & Testing. That said, we have indeed faced problems sometimes with different LCDs, which used to get rectified by modifying the code. So it is probably okay to say that the code is not robust/good enough. Meanwhile I'll ask someone from our team to check that code again.

    @skyfall_133 I would recommend that you give a try to @Alexander Lang's code and see if it works or not.

    Thanks!
     
    skyfall_133 likes this.
  10. skyfall_133

    skyfall_133 New Member

    Joined:
    Aug 28, 2017
    Messages:
    5
    Likes Received:
    0
    Trophy Points:
    1
    Gender:
    Male
    Location:
    Vietnam
    Thank @Alexander Lang, but i didn't succeed when i try your code :(, Did you edit about used frequency of Elbert V2 in your code (12Mhz ). And also i found my error: GND of power did not connect to GND of Kit. After that, i edited like that:
    Code:
    // Check if all the memory location are covered.
    // Once it's done disable the enable pin has no more data has to be send.
         if(pointer==Length+1)                             
            en = 'b0;
          else
            en=counter[16];   // i changed 15--> 16                                           
    end 
    And this is the result, it only indicate two characters.
    [​IMG]
    (https://drive.google.com/file/d/0B8WdpMCgmg49bnJsQkxscWxEaUE/view?usp=sharing)
     
  11. Alexander Lang

    Alexander Lang Member

    Joined:
    May 17, 2015
    Messages:
    81
    Likes Received:
    15
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Manchester, UK
    @skyfall_133

    Sorry for the slow response - I've been busy. I will take another look at this for you and see if I can find out what is wrong. I did test everything on my Elbert V2 so it should have worked. I will hopefully get a chance to do this later on today.
     
  12. Alexander Lang

    Alexander Lang Member

    Joined:
    May 17, 2015
    Messages:
    81
    Likes Received:
    15
    Trophy Points:
    8
    Gender:
    Male
    Location:
    Manchester, UK
    @skyfall_133

    I have tested the VHDL code again and it worked fine with my setup. Please can you upload the BIN file in the attached zip file to your Elbert V2 with your LCD connected and test it again. You should see the message:

    VHDL LCD Example
    Numato Labs FGPA

    The lines should reverse every ten seconds

    Let me know if this works. I could not get the Verilog code to work at all....

    Good luck!

    Alex
     

    Attached Files:

    rohith likes this.
Thread Status:
Not open for further replies.

Share This Page