Jump to content
  • 0

Not understanding LCD Display


PoorCollegeStudent

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

Link to comment
Share on other sites

Recommended Posts

Hello Digilent Community,

     I am now officially done with my term and I want to share what my experience was like, as per @D@n's question. I would like to attach our final report here (we got an 86 on it, so please understand that it won't be the rest report haha). It was quite rushed since my partner and I were travelling during the last few days before the turn in date and the code in the appendices are quite scuffed. Vivado won't print PDFs in color and I haven't figured out how to fix the sizing or formatting, which is really inconvenient because it looks bad and not easy to read. If anyone has any questions about the code or writing at all, please feel free to private message me or just respond on this forum by tagging me! The idea behind how we wanted it to work, what it actually became, the struggles we had, and our theory on why it did not work again are included in the report along with our VHDL files and some testbench images. If they end up being too small of pictures, just let me know and i can attach the original ones here so it's easier to see. Thank you all for your help and consideration; it means to the world to me now and it meant the world to me before while I was working on this project.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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;

 

Link to comment
Share on other sites

22 minutes ago, hamster said:

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;

 

Oh right I remember this from an in-class exercise we had once. I'll try it out.

Link to comment
Share on other sites

@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

Link to comment
Share on other sites

Also a question, will FPGAs act unreliably after being operated/plugged in for a long time (long as in 10+ hours at a time). My demo is today (Friday) and our project still doesn't work unfortunately. Without having touched anything, I came back from dinner to see that the display only seems to like the keys "2", "7", and "9", but perhaps that could be something to do with the keypad decoder. Might not get great marks on this project, but I had a good time on this discussion post. Thank you all for your time and contributions.

Link to comment
Share on other sites

@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

Link to comment
Share on other sites

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...

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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...)

Link to comment
Share on other sites

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. 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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!

Link to comment
Share on other sites

@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

Link to comment
Share on other sites

@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?

Link to comment
Share on other sites

@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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...