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.

Unable to understand a LCD module controller code :(

Status
Not open for further replies.

hobbyiclearner

Full Member level 2
Joined
Oct 27, 2014
Messages
142
Helped
2
Reputation
4
Reaction score
2
Trophy points
18
Activity points
1,296
Hello there,

I was going through a LCD controller code in VHDL and was unable to understand a particular section

Code:
...........
WHEN send =>
        busy <= '1';
        IF(clk_count < (50 * freq)) THEN  --do not exit for 50us
           busy <= '1';
           IF(clk_count < freq) THEN      --negative enable
            e <= '0';
           ELSIF(clk_count < (14 * freq)) THEN  --positive enable half-cycle
            e <= '1';
           ELSIF(clk_count < (27 * freq)) THEN  --negative enable half-cycle
            e <= '0';
           END IF;
           clk_count := clk_count + 1;
           state <= send;
        ELSE
          clk_count := 0;
          state <= ready;
        END IF;
..........

The complete code is in at this link


Basically I am able to understand the entire code except for the above mentioned portion of the FSM (the 'send' state). Particularly, what is meant here by negative enable, positive enable half cycle and negative enable half cycle and most importantly why are these required . Also what is the significance of the particular delay values (1 usec, 14 usec and 27 usec respectively).

Any suggestions here will be gratefully acknowledged.


Thanks,
Hobbyiclearner.
 

The constant freq which is set to 50 (in MHz according to the comment) doesn't add up when you calculate the clk_count code. 50 MHz has a 20 ns period and multiplying by 50 gives a 1 us delay, which doesn't match the comment of 50 us. The code at the link has many different values for the wait comments that don't add up. You would need to count 2500 clocks to have 50 us (IMO if you have comments they should be correct or don't include them in the first place). If you use this code I recommend deleting all the comments that mention waiting for some length of time.

The delays have to do with making an enable stobe to load the instructions, see the timing diagram (Fig 9, pg 22) in the HD44780 datasheet. The required timing can be found on pg 49, so the enable pulse high needs to be at least 450 ns, which is definitely within the 13 us (27-14) in the code.
 

OK thanks.... I will go through the HD44780 datasheet. BTW do you know of any other code for LCD controller pls.

Thanks,
Hobbyiclearner

- - - Updated - - -

PLEASE READ THE ABOVE POST AS :


OK thanks. I will go through the HD44780 datasheet.

Also wanted to know why is the enable toggled in these lines (sorry but I do not know the concept of strobing). What can happen if I dont do so. And how is this toggling rate fixed (assuming that its total time period will be atleast 50usec; as per the description here).

BTW do you know of any other (simple) code for LCD controller pls.

Thanks,
Hobbyiclearner
 

IMO if you have comments they should be correct or don't include them in the first place

I'm not sure if similar approach can be applied to FPGAs, but when I program in microcontrollers, I usually add every hardware dependent information parametrized, being situed at the top of the code along with other head parameters ( e.g. numerically defining the above oscillator as 50000000 value in a constant used to calculate other time dependent constants ). This way one would never worry about to review the code which turned more portable to other designs.
 

OK thanks.... I will go through the HD44780 datasheet.
Do this. Read the datasheet the question below about why the enable goes 0-1-0 is shown in the timing diagram.

Also wanted to know why is the enable toggled in these lines (sorry but I do not know the concept of strobing). What can happen if I dont do so. And how is this toggling rate fixed (assuming that its total time period will be atleast 50usec; as per the description here).

BTW do you know of any other (simple) code for LCD controller pls.

Thanks,
Hobbyiclearner
a strobe is some thing that goes from a default state to a active state and returns to the default state in a set period of time. The pulse width of the strobe will generally be fixed but the time between pulses are allowed to vary.In this case the strobe is used to enable the read or the writes of the registers in the display. The whole reason for that snippet of code is to pulse the enable high and space those pulses far enough apart to met the timing requirements of the LCD display.

- - - Updated - - -

I'm not sure if similar approach can be applied to FPGAs, but when I program in microcontrollers, I usually add every hardware dependent information parametrized, being situed at the top of the code along with other head parameters ( e.g. numerically defining the above oscillator as 50000000 value in a constant used to calculate other time dependent constants ). This way one would never worry about to review the code which turned more portable to other designs.
Yes they re called generics and parameters in vhdl and verilog respectively. The code in question used a constant instead, but the major problem was the use of inline comments within the code that don't match the code. That's a big no-no IMO. It's better to have no comments in the code if you aren't going to edit them to be correct.

This all boils down to the quality of code for maintanence.
 

In fact, in the above code, a self descriptive generic instead of a textual comment would avoid that confusion.

Code:
IF ( clk_count < DELAY_50US ) THEN
 

Except the delay isn't 50 us it was 50 * 50 (in MHz) or 20 ns * 50 which is only 1 us, hence the comment is wrong and can change depending on the freq constant used, which in the documentation says you can change to other frequencies if you don't have 50 MHz.

It would have been more appropriate to use 50 * 1 (i.e. 50 * period; -- in us) or something similar so the constant would be period and not freq.

Your suggested use of a self descriptive constant works well if you know the constant never changes, but in this case the constant is one that is supposed to be modified to match your system clock. All in all this code isn't the best representative of good coding standards.
 
Except the delay isn't 50 us it was 50 * 50 (in MHz) or 20 ns * 50 which is only 1 us, hence the comment is wrong and can change depending on the freq constant used, which in the documentation says you can change to other frequencies if you don't have 50 MHz.

It would have been more appropriate to use 50 * 1 (i.e. 50 * period; -- in us) or something similar so the constant would be period and not freq.

Thanks for the time ads-ee. But I will like to differ here. The literal 'freq' is defined here as an integer constant = 50. So it is 50*50 clock pulses or 2500 clock pulses each of 1 clock pulse duration ie. 20ns duration , ie. total duration is 2500*20ns or 50000 nsec or 50usec. So maybe the comment is OK.

Regarding strobing, I wanted to ask one more thing as well. In the initialize state, the 'e' is alternately getting changed from 1 to 0 to 1 and so on. Is it also done for strobing purpose?

Code:
        WHEN initialize =>
          busy <= '1';
          clk_count := clk_count + 1;
          IF(clk_count < (10 * freq)) THEN       --function set
            lcd_data <= "00111100";      --2-line mode, display on
            --lcd_data <= "00110100";    --1-line mode, display on
            --lcd_data <= "00110000";    --1-line mdoe, display off
            --lcd_data <= "00111000";    --2-line mode, display off
            [B]e <= '1';[/B]
            state <= initialize;
          ELSIF(clk_count < (60 * freq)) THEN    --wait 50 us
            lcd_data <= "00000000";
           [B] e <= '0';[/B]
            state <= initialize;
          ELSIF(clk_count < (70 * freq)) THEN    --display on/off control
            lcd_data <= "00001100";      --display on, cursor off, blink off
            --lcd_data <= "00001101";    --display on, cursor off, blink on
            --lcd_data <= "00001110";    --display on, cursor on, blink off
            --lcd_data <= "00001111";    --display on, cursor on, blink on
            --lcd_data <= "00001000";    --display off, cursor off, blink off
            --lcd_data <= "00001001";    --display off, cursor off, blink on
            --lcd_data <= "00001010";    --display off, cursor on, blink off
            --lcd_data <= "00001011";    --display off, cursor on, blink on            
            [B]e <= '1';[/B]
            state <= initialize;
          ELSIF(clk_count < (120 * freq)) THEN   --wait 50 us
            lcd_data <= "00000000";
           [B] e <= '0'[/B];
            state <= initialize;
          ELSIF(clk_count < (130 * freq)) THEN   --display clear
            lcd_data <= "00000001";
            [B]e <= '1';[/B]
            state <= initialize;
          ELSIF(clk_count < (2130 * freq)) THEN  --wait 2 ms
            lcd_data <= "00000000";
            [B]e <= '0';[/B]
            state <= initialize;
          ELSIF(clk_count < (2140 * freq)) THEN  --entry mode set
            lcd_data <= "00000110";      --increment mode, entire shift off
            --lcd_data <= "00000111";    --increment mode, entire shift on
            --lcd_data <= "00000100";    --decrement mode, entire shift off
            --lcd_data <= "00000101";    --decrement mode, entire shift on
            [B]e <= '1';[/B]
            state <= initialize;
          ELSIF(clk_count < (2200 * freq)) THEN  --wait 60 us
            lcd_data <= "00000000";
            [B]e <= '0';[/B]
            state <= initialize;
          ELSE                                   --initialization complete
            clk_count := 0;
            busy <= '0';
            state <= ready;
          END IF;

Thanks,

Hobbyiclearner.
 
  • Like
Reactions: ads-ee

    ads-ee

    Points: 2
    Helpful Answer Positive Rating
Thanks for the time ads-ee. But I will like to differ here. The literal 'freq' is defined here as an integer constant = 50. So it is 50*50 clock pulses or 2500 clock pulses each of 1 clock pulse duration ie. 20ns duration , ie. total duration is 2500*20ns or 50000 nsec or 50usec. So maybe the comment is OK.

Whoop, I really screwed up my math today! :bang:

Don't know what I was thinking, I still think it would be more clear to specify the delay as 50 * DLY_1US or as andre_teprom suggested DELAY_50US than multiplying up a frequency (i.e. 50 * FREQ_50MHZ). I've obviously proven it's much easier to have a brain f--t and mess up the calculation. :laugh:


The initialization sequence is just another series of writes and reads of the registers in the display so they also need the enable strobes.
 

Whoop,

The initialization sequence is just another series of writes and reads of the registers in the display so they also need the enable strobes.

I had understood the purpose of having an initalization sequence. All I want to know that the enable here also has been toggled from 0 to 1 to 0 and so on. (see the bold texts in the code snippet of my previous post). What is the purpose of doing so.

Hobbyiclearner
 

I had understood the purpose of having an initalization sequence. All I want to know that the enable here also has been toggled from 0 to 1 to 0 and so on. (see the bold texts in the code snippet of my previous post). What is the purpose of doing so.

Hobbyiclearner

I just answered this question in my last post?

The enable strobe has to be HIGH to write or read from the display. So hence it goes 0-1-0 to perform the write or read operation if it just stayed low 0-0-0 then nothing would be written or read.
 
Great. And thanks for the patience (I am new to this strobing concept; thus the requirement of detailed clarification). So if I have understood correctly, to send instructions/ data to the LCD module, its controller module first enables it and then does the needful. In the entire initialization process instructions have to be sent. So why not keep enable high only (instead of toggling it).

Hobbyiclearner.
 

The interface to this module is asynchronous so it needs to know when transactions are started/finished 0-1 (started), 1-0 (finished) depending on the internal implementation it may be using the edges or the level of the enable signal to load an internal register with data on the pins or output the data from the register to the data pins. I haven't done much more than scan the datasheet so if there is anything that specifically states that or implies which it is I didn't notice it.
 

+++NOTE+++

The mentioned code of the lcd_controller was found to be working at clock speed 25 MHz.

Hobbyiclearner.
 

I just have a few more queries on the lcd_controller code pls.

1. In the lcd controller entity declaration, the 'busy' pin is declared as out pin and has been assigned an initial value '1'. First of all why has this been done. Is it to avoid a latch inference or to provide a busy signal during power up to the module driving the lcd.

Code:
ENTITY lcd_controller IS
  PORT(
    clk        : IN    STD_LOGIC;  --system clock
    reset_n    : IN    STD_LOGIC;  --active low reinitializes lcd
    lcd_enable : IN    STD_LOGIC;  --latches data into lcd controller
    lcd_bus    : IN    STD_LOGIC_VECTOR(9 DOWNTO 0);  --data and control signals
    [B]busy       : OUT   STD_LOGIC := '1';[/B]  --lcd controller busy/idle feedback
    rw, rs, e  : OUT   STD_LOGIC;  --read/write, setup/data, and enable for lcd
    lcd_data   : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0)); --data signals for lcd
END lcd_controller;

2. In the else clause of 'power up' state, the lcd_data has been assigned "00110000". Why not simply "00000000".

Code:
WHEN power_up =>
          busy <= '1';
          IF(clk_count < (50000 * freq)) THEN    --wait 50 ms
            clk_count := clk_count + 1;
            state <= power_up;
          ELSE                                   --power-up complete
            clk_count := 0;
            rs <= '0';
            rw <= '0';
            [B]lcd_data <= "00110000";[/B]
            state <= initialize;
          END IF;

3. In the 'send' state of the, why has been busy assigned '1' before the first if statement. Since the controller has to wait for atleast in the send state for 50 usec before going back to ready state, in the first if statement, the busy port has been already assigned logic 1.

Code:
        WHEN send =>
       [B] busy <= '1';[/B]
        IF(clk_count < (50 * freq)) THEN  --do not exit for 50us
           busy <= '1';
           IF(clk_count < freq) THEN      --negative enable
            e <= '0';
           ELSIF(clk_count < (14 * freq)) THEN  --positive enable half-cycle
            e <= '1';
           ELSIF(clk_count < (27 * freq)) THEN  --negative enable half-cycle
            e <= '0';
           END IF;
           clk_count := clk_count + 1;
           state <= send;
        ELSE
          clk_count := 0;
          state <= ready;
        END IF;


Also in general, data can be written to and read from a 2 line character lcd display. When in practical situations, data is read from such displays.

Thanks in advance,

Hobbyiclearner
 

1. Not necessary if reset_n is used appropriately in all modules. With many FPGA, an asynchronous register reset to '1' is also enforcing a '1' power-up level. Specifying it explicitly may avoid a warning.

2. Apparently, the first initialize step sets 8-bit data length. Review the HD44780 command set.

3. For maximum performance, you'll read LCD busy state instead of performing a nominal delay. That's the most common application of reading LCD data. In a noisy environment it can be also useful to check if the display is still holding the expected data and configuration settings.
 
1. Not necessary if reset_n is used appropriately in all modules. With many FPGA, an asynchronous register reset to '1' is also enforcing a '1' power-up level. Specifying it explicitly may avoid a warning.

2. Apparently, the first initialize step sets 8-bit data length. Review the HD44780 command set.

3. For maximum performance, you'll read LCD busy state instead of performing a nominal delay. That's the most common application of reading LCD data. In a noisy environment it can be also useful to check if the display is still holding the expected data and configuration settings.

Thanks for the answers. Apparently you have answered queries 1,2 and the general query. Pls. do answer the 3rd query as well.

Hobbyiclearner.
 
Last edited:

What motivation is required pls?

Thanks,
Hobbyiclearner.

Forum members are not free consultants, we are all volunteers, as such having to work on some random code (perhaps rubbish code) and modify it to do something that we've suggested (that you should implement) is not something you should expect from another forum member. Occasionally you'll find someone with too much time on their hands that might do the work for you, but don't count on it!
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top