• 0
Sign in to follow this  
PoorCollegeStudent

Not understanding LCD Display

Question

Hello Digilent community,

   I am currently taking my first digital electronics class and my final project is a calculator written in VHDL using a Basys3 board, 16-key keypad, and a 16x2 LCD display with parallel interface, all provided by Digilent. I took a look at the provided example code from the resource library and I just had some questions about how it works. Now, the example code declares a constant before any of the processes, and this constant is an array of std_logic_vectors so it holds a preloaded message "Hello From Digilent" with the necessary function sets and all that preceding the message. In my case, I have to have a way to display the inputs from the keypad on the display and also display the output on the 2nd line of the display. I have the main logic part of the calculator settled, I just want to know how I can direct these signals from the keypad and the output of the computational module (as in the sum, difference, product, etc.) to the LCD display. I have never played with a display before now and never used VHDL or any type of board like the Basys3 before this class, so I guess I'm still quite novice and don't understand a good part of what the example code is telling me. I do get that the state machine is just cycling through the values in the constant and waits for certain delays to pass through before transitioning between certain states.

Since the values on the display have to be updated live as the user inputs numbers from the keypad and also when there is a value computed, how can I shift from having a constant with a preloaded message to something that can update itself as needed? My idea was keep the idea of the array of std_logic_vectors but only have one value (rather than the 23 or 24 that are preloaded in the current example code) that will update with every key press. I'll have two of these, one for the inputs (to show the numbers and operations on the first line) and one for the output (to show on the second line). I'm thinking maybe I declare a variable within a process that will update and be sensitive to the key presses? 

Also, I tried looking around the reference manual and such but I could not find the function code for how to display information on the 2nd line; so far, stuff only displays on the first line. 

Sorry this is such a bulky post. I have had a lot of questions and my professor hasn't been around much. Also, part of this project is learning to interface with new components, so in my case the keypad and lcd display, and I'm not having much luck without any guidance unfortunately. Thank you for taking the time to read this!

P.S. Attached the example vhd file from Digilent here for easy access in case anyone wants to look!

PmodCLP.vhd

Share this post


Link to post
Share on other sites

32 answers to this question

Recommended Posts

  • 1

HI!

Ignoring the JA and JB ports that are required to talk to the display hardware, what would your module's ideal interface look like?

Would it be a register based interface, where you write data to be displayed, (e.g. digit 0 in register 0):

  addr[4:0],
  din[7:0],
  writeEnable

 or would it be just a minimal interface for each digit you want to show:

  digit0[3:0], decimal0, blank0, 
  digit1[3:0], decimal1, blank1,
  ...
  digitX[3:0], decimalX, blankX

Or would it expose the ASCII nature of the display, allowing you to show almost anything?

  character0[7:0],
  character1[7:0],
  ...
  characterX[7:0]

Or would it be a stream interface:

  din[7:0],
  data_enable,
  first_byte_of_packet

Have you thought how you would manage the cursor (if it is needed at all)?

Once you have that sketched out, the you are in the position of how to implement the innards of the module...

Share this post


Link to post
Share on other sites
  • 1

So if you add a top-level port of "bcd : in std_logic_vector(7 downto 0)" (enough for two digits as a test) then within the module you could do the following:

a. replace "constant" with "signal" on LCD_CMDS

b. Add a couple of assignments to overwrite the desired values in the array.

Add a couple of concurrent assignments:

   LCD_CMDS(15) <= x"3" & bcd_value(7 downto 4);

   LCD_CMDS(14) <= x"3" & bcd_value(3 downto 0);

That will overwrite the "Di" in "Digilent" with two digits.

Once you get this working, you can follow your nose to add the extra digits (e.g. make 'bcd' wider, overwrite extra digits, change the commands being sent to the display so you can access the second line, remove the shift...)

Share this post


Link to post
Share on other sites
  • 1

I've never used the this display, but on other display the second line start on address C0, (http://www.avrfreaks.net/forum/problem-second-line-2x16-lcd

So try this:

Change

                14 => "10"&X"20",           -- blank

To:

                14 => "00"&X"C0",          -- Move to second line

This might also be of use: https://mil.ufl.edu/3744/docs/lcdmanual/commands.html

 

Share this post


Link to post
Share on other sites
  • 1

Oh - ignore the Debug Hub message - it just means that your project does not have a debug code (aka. Embedded Virtual Logic Analyser) in the project.

You will need to tinker with the FSM. My first suggestion would be when the user input changes from what is on the screen, jump to the starting state for the FSM so it re-displays everything.

something along the lines of this early on in the FSM, to capture what is on the screen:

      current_bcd <= bcd;

and have the rest of the fsm display what is in "current_bcd"

Then, in the end state of the FSM:

     if bcd /= current_bcd then
        .... do stuff here to restart the FSM, most likely changing lcd_cmd_ptr
     end if;

That way you should have a reasonably responsive display.

Share this post


Link to post
Share on other sites
  • 1

@PoorCollegeStudent,

Check out the display data ram address command.  On page 16, you can discover that when the chip is in 2-line display mode, the first lines display addresses go from 0x0 to 0x27, and the second lines addresses go from 0x40 to 0x67.  So, if you set the address to 0x40, anything you then write should show up on the second line.

Dan

Share this post


Link to post
Share on other sites
  • 1

@PoorCollegeStudent,

The "H" reference is an indication that the number just given was given in hexadecimal.  That's why, when I posted my comments about it above, I prefixed the number with 0x--the C-language prefix for hexadecimal numbers, which is also accepted by strtoul(), just not used by the author of that specification.

The trick with issuing the command is that you have to change the RS or "Register Select" pin to zero to indicate a command write, versus "1" to indicate that you wish to write to memory.

Dan

Share this post


Link to post
Share on other sites
  • 1

@PoorCollegeStudent,

As I recall, there's a rather complicated start up requirement for the 2-line display.  Could it be that when you ran the example code the display was properly sent through its startup sequence, and your startup sequence ... doesn't cut it yet?

Dan

Share this post


Link to post
Share on other sites
  • 1
Posted (edited)
1 hour ago, PoorCollegeStudent said:

I am now having some trouble. The inputs will display on the LCD but the output will not. I have testbenched every module and they work as expected. There might be something wrong with the keypad interface. I basically need it to only output once per press rather than continuously like it does now, and I have no way of doing this yet.

To make a single-cycle pulse on 'x_rising_edge' when signal 'x' changes from '0' to '1':

signal x_last              : std_logic := '0';
signal x_rising_edge : std_logic := '0';

 

... inside a clocked process....

if x_last = '0' and x = '1' then
   x_rising_edge <= '1';
else
   x_rising_edge <= '0';
end if;
x_last <= x;

 

Edited by hamster

Share this post


Link to post
Share on other sites
  • 1

@PoorCollegeStudent,

You can even put a timer into it as well, forcing only one pulse ever N clock pulses at most:

always @(posedge i_clk)
begin
	pulse <= 1'b0;
	if (btn)
	begin
		pulse <= counter[20];
		counter <= 0;
	end else if (!counter[20])
		counter <= counter + 1'b1;
end

Using this approach, you can set a minimum amount of time between subsequent responses to an input signal.

Dan

Share this post


Link to post
Share on other sites
  • 1

@PoorCollegeStudent,

"will FPGAs act unreliably after being operated/plugged in for a long time"?  Not in my experience.

There can be flaws in your design that will only show up after many hours.  These can be very difficult to debug.

Can I ask you a question, though?  Once you've presented your design and work to your instructure, can you come back and then share with the forum what you did to get your design as far as you got it?  I mean, specifically, how did you go about debugging the design?  I'm curious as to what you did, how you did it, and why you did it--and in general how students in your class handled debugging.

Thanks,

Dan

Share this post


Link to post
Share on other sites
  • 0
8 hours ago, hamster said:

HI!

Ignoring the JA and JB ports that are required to talk to the display hardware, what would your module's ideal interface look like?

Would it be a register based interface, where you write data to be displayed, (e.g. digit 0 in register 0):

  addr[4:0],
  din[7:0],
  writeEnable

 or would it be just a minimal interface for each digit you want to show:

  digit0[3:0], decimal0, blank0, 
  digit1[3:0], decimal1, blank1,
  ...
  digitX[3:0], decimalX, blankX

Or would it expose the ASCII nature of the display, allowing you to show almost anything?

  character0[7:0],
  character1[7:0],
  ...
  characterX[7:0]

Or would it be a stream interface:

  din[7:0],
  data_enable,
  first_byte_of_packet

Have you thought how you would manage the cursor (if it is needed at all)?

Once you have that sketched out, the you are in the position of how to implement the innards of the module...

Hello!

I'm not too sure how you are describing the methods of interfacing, but I get the gist of it I think. I believe my purposes fit the 3rd one where I want to use the ASCII nature of the display. Basically, 4-bit BCD values will come in 8 at a time (for 8 digits to display) and by adding 30 to each one, I can send the proper ASCII value to the display. There won't be a cursor, sorry forgot to mention that. 

I just want to see if maybe I could create a variable that will update for every incoming set of 8 BCD values, which in turn would create 8 new ASCII values to send to the display. I would like the input values to go to the first line of the display, and the computed value to go to the second line of the display, but I'm not sure how to send values to the 2nd line.

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)
18 hours ago, hamster said:

So if you add a top-level port of "bcd : in std_logic_vector(7 downto 0)" (enough for two digits as a test) then within the module you could do the following:

a. replace "constant" with "signal" on LCD_CMDS

b. Add a couple of assignments to overwrite the desired values in the array.

Add a couple of concurrent assignments:

   LCD_CMDS(15) <= x"3" & bcd_value(7 downto 4);

   LCD_CMDS(14) <= x"3" & bcd_value(3 downto 0);

That will overwrite the "Di" in "Digilent" with two digits.

Once you get this working, you can follow your nose to add the extra digits (e.g. make 'bcd' wider, overwrite extra digits, change the commands being sent to the display so you can access the second line, remove the shift...)

Hello,

I have done part a and was trying to do part B as well. However, I was unable to find the command for accessing the second line. It wasn't in the reference manual and I couldn't find it online? Am I just looking in the wrong places? I feel like ASCII tables would have a value that equates to moving down a line but so far I haven't found one.

Thank you for your help so far by the way. I appreciate it a lot. 

Edited by PoorCollegeStudent

Share this post


Link to post
Share on other sites
  • 0

Hello again,

So a little update if it even matters or if anyone still cares at this point: I still don't know what the command is for accessing the next line, haven't been able to find it still. My confidence in my googling stills has been diminishing exponentially.

I managed to make it to the numbers show one by one only when I press a key, and the characters do not keep repeating on the display anymore. However, there is this long delay of like 4 or 5 seconds between each character, and I have tried adjusting the timing values in the code to even make them really short, but the delay between characters seems to remain at about 4 or 5 seconds. It's actually different each time. Any idea why this might be?

There is a warning about the debug hub not detected at User Scan Chain 1 or 3, and Vivado says to make sure the clock connected to the debug hub (dbg_hub) core is a free running clock and is active -- me being a newbie, I have no idea what this means. I tried looking it up but the forum posts I found were for some very specific projects that don't relate to mine.

Share this post


Link to post
Share on other sites
  • 0

By the way, I will keep updating since the calculator w/ keypad and LCD display seems to be a common, popular project amongst colleges so hopefully one day my notes here will help someone in the future who will face the same struggles I am facing right now.

Share this post


Link to post
Share on other sites
  • 0

Hello @D@n, thank you for your response! Yes I have looked at that datasheet because it's also linked from the Digilent website for the LCD display, but I do not think it says anywhere there how I can start displaying on the second line, just how to enable the 2nd line.

 

@hamster I am going to try what you suggest and see what happens. Thanks for letting me know about the debug hub warning too!

Share this post


Link to post
Share on other sites
  • 0

@D@n Hello,

I had seen that before, but I wasn't aware I could set it using a command. I tried setting a value in my LCD_CMDS signal like 

                4 => "00"&X"40" based on the datasheet but that didn't seem to do anything. It mentions that the second line starts at address 40H, but what does that "H" mean?

Share this post


Link to post
Share on other sites
  • 0

Hello,

Just realized the H probably means hex, but using that line of code doesn't work. I've decided beggars can't be choosers since my demo day is in 2 days. I'm sticking to one line of display. 

My strategy right now will be taking the LCD_CMDS signal and initializing it with the first few original commands and 4 blanks. A press of a key on the keypad will replace these blanks with characters and the LCD will update continuously to display these numbers.

I will create a second signal like LCD_CMDS for the output/computed value, and I'll make an if-statement to assign one of two LCD_CMDS signals to the JB port (JA in the example code) so that the LCD will display either the inputs or the output.

Share this post


Link to post
Share on other sites
  • 0

Hello all,

Update: I have manged to make keys appear one by one with reliable timing. Thank you to everyone here who helped make this possible. It included an extra state in the current example code's fsm that only goes to the stInitDne when a specific key is pressed. The keys that allow this transition of state would be the number keys (i.e. 0 through 9). I also managed to make it stop at 4 digits and clear when an operation button (+, -, *, =) is pressed in order to make room for the next number. I did this by initializing the LCD_CMDS with blanks and used a counter sensitive to the number keys to replace a specific vector in the LCD_CMDS's array with the corresponding ASCII value. I will put a 5th blank before these numbers to make room for a negative sign.

Currently I am trying to debounce the keys because everytime I test it, the third digit (always the third digit) produces some weird character like a capital C or an alpha symbol. I tried implementing a debouncer into the code, but now it recognizes the keys to the right of the ones pressed. In other words, pressing "1" will show "2" on the display or pressing "8" will display "9". If anyone has any suggestions on how to get around this, that would be great, but for now I'll just keep you all posted when I find a solution.

Share this post


Link to post
Share on other sites
  • 0

Hello,

 I have come across a new problem. Black boxes across the first line of the display and nothing seems to work, no buttons presses do anything. I think this is a connectivity issue because it worked fine for a while today, and after coming back from eating it started to do this. I will try another computer.

Share this post


Link to post
Share on other sites
  • 0

Update again: Sorry for the spam to everyone who is probably getting these notifications/emails.

The black bars went away when I ran the original example code that displays "Hello From Digilent" which I guess forced the contrast voltage back or something (not really sure how because I don't see anything for that in the code) but I'm not going to question it because it helped.

Share this post


Link to post
Share on other sites
  • 0

@D@n I did assume that at first too, so I looked through my code for an hour but my startup sequence is the same as the one in the example code, so it seems unlikely now (?) The black-box error started to occur right when I plugged in the display to the ports on the board and turned on the board, so I hadnt even run the program yet. Just weird for me all around. Now it's alright though so I think you're right in that the example code sent it through the startup sequence again in a way that mine doesn't, I just can't see why as of yet.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this