This post discusses the same subject but goes into more detail and relies on VHDL code instead of Verilog.

There are a great many 16x2 LCD displays available to electronic design engineers. Nearly all of them are controlled by the Hitachi HD44780 control IC which is built into the display module. The wikipedia entry on the control system is here:

The pins used on LCD display have been standardised to make it simple for engineers to use these displays in their electronic circuits. The nuances of controlling a display are quite complex and beyond the scope of what I'm prepared to discuss here. The displays are controlled by sending signals from a microcontroller which implements a state machine in order to send data to be displayed to the display and read back from the display.

If people are interested in exactly how to control an LCD display with switches then here is a tutorial for you:

In order to use an FPGA to achieve control of a display we need to implement a similar state machine. This has been written many times before and a particular good paper explaining the implementation of the state machine is here:

Luckily we don't have to write our own state machine as I found a really good implementation of one at OpenCores. OpenCores is an excellent FPGA design and development resource and if anyone is serious about learning and using FPGA technology then they should check it out!

Download the project files and extract them to somewhere sensible on your Hard disk and then load up Xilinx WebISE and lets get started!

We need to create a new project - use the settings shown in the picture below:

They are all correct for the Numato Labs Mimas V2 Development board. Click OK when ready.

Next right click on the Hierarchy window in the main project window of WebISE and choose 'add Copy of Source' - then navigate to the folder(s) where the OpenCores project is located and find and then add the file:

lcd16x2_ctrl.vhd

Click 'OK' when asked to confirm adding the file to the project.

Then repeat the process to add lcd16x2_ctrl_demo.vhd

Click 'OK' when asked to confirm adding the file to the project.

The repeat the process on more time to add the UCF file called 'pin_locations.ucf'

The main project window should now look like this:

What needs to be done now is to update the UCF file with the appropriate connections for the Mimas V2 Development board. Load up the UCF file in the internal text editor in WebISE:

Then save the project - always a good idea. Now its time to 'compile' the code! Implement a top module by clicking on the green arrrow:

Now generate a bitstream file:

Once that is complete generate a 'Bit File' and then upload that to the Mimas V2 Development board via your preferred method - by Python Script or by using the supplied uploader. I use the uploader.

Ensure you have a power supply connected to the Vext input of the Expansion PCB (which is capable of supplying at least 7 Volts. Then plug the expansion header into the P6 header socket. When you connect it up you should be presented with the following image:

Cool beans!

At this point I should probably explain some of the code and how it works. We added three files to the project in order to get things working.

lcd16x2_ctrl.vhd - The state machine for controlling the LCD display

pin_locations.UCF - The UCF file which tells the compiler

lcd16x2_ctrl_demo.vhd - a file to demonstrate the display in operation.

The code was written by a very clever German gentleman named Daniel Drescherand full credit goes to him for an excellent implementation of an VHDL Hitachi HD44780 display driver.

I'm not going to go into much detail about the lcd16x2_ctrl.vhd file except to say that it provides the method for controlling the pins on the display to go about getting the response required whether reading whether the display has correctly displayed something or writing data to the display. Most people are only interested in write operation however the read operation can be useful for fault finding.

The file pin_locations.ucf tell Xilinx WebISE which pins on the FPGA are going to be used. In the example above we used the pins associated with the P6 connector. If another connector is needed then refer to the Mimas V2 instruction manual and schematic and update this file as necessary.

The 16x2_ctrl_demo.vhd file is the file of most interest. It allows the designer to set the data to be sent and displayed on the 16x2 screen.

The above lines of code tell the compiler to include some standard vhdl libraries and then implements an entity statement - tells the compiler the pin names we are going to use and what type of pin they are either input or output. The pin names point towards their function.

clk - The free running clock to assist with the timings to control the display

lcd_e - the LCD enable pin

lcd_rs - the LCD register select pin

lcd_rw - the LCD read/write register pin

lcd_db - 4 pins used to control the data pins on the LCD display in 4 bit operation

The above lines are from the architecture statement. It defines the behaviour of the FPGA. In this case it will drive the 16x2 LCD display. It does this by implementing some internal signals (variables).

The Signal timer is a timer which counts from zero to ten million. This will be used to ensure that the timings for the reading and writing to the display are correct,

The signal switch lines is a variable to tell the FPGA to swap the top and bottom lines on the 16x2 LCD display.

The signals line1 and line2 are logic vectors to send the characters to be displayed to the 16x2 LCD display.

Next a constant clock period is set at 100 MHz or ten nano seconds and the clock is positive edge triggered.

Finally some further signals are created including a reset signal and some logic buffers for the characters to be sent to the 16x2 Display.

The above lines begin the actual architecture statement. The method of controlling the display is instantiated from the code written in lcd16x2_ctrl.vhd. The clock period is set to match and the pins are mapped to ensure data is passed correctly between the two files. Finally the reset pin is set low to ensure the display is ready to receive data.

The above lines actually are the characters that will be sent to the display. The more observant will notice that the values used are hexadecimal values for ascii characters. If it was required to change the characters displayed on screen we would need to change these hexadecimal values to whatever we wanted.

The above lines is the actual process the display will follow. If the clock is positive and the timer is at zero then the timer is set to a hundred million. This equates to one second as 100,000,000 counts of a 100 MHz clock is one second. The characters on the top and bottom lines are swapped. Then the timer is decremented and the process ends.

If we wanted to change the characters being displayed we need to change the section shown here:

-- see the display's datasheet for the character map

line1(127 downto 120) <= X"20";

line1(119 downto 112) <= X"20";

line1(111 downto 104) <= X"48"; -- H

line1(103 downto 96) <= X"65"; -- e

line1(95 downto 88) <= X"6c"; -- l

line1(87 downto 80) <= X"6c"; -- l

line1(79 downto 72) <= X"6f"; -- o

line1(71 downto 64) <= X"20";

line1(63 downto 56) <= X"57"; -- W

line1(55 downto 48) <= X"6f"; -- o

line1(47 downto 40) <= X"72"; -- r

line1(39 downto 32) <= X"6c"; -- l

line1(31 downto 24) <= X"64"; -- d

line1(23 downto 16) <= X"21"; -- !

line1(15 downto 8) <= X"20";

line1(7 downto 0) <= X"20";

line2(127 downto 120) <= X"30";

line2(119 downto 112) <= X"31";

line2(111 downto 104) <= X"32";

line2(103 downto 96) <= X"33";

line2(95 downto 88) <= X"34";

line2(87 downto 80) <= X"35";

line2(79 downto 72) <= X"36";

line2(71 downto 64) <= X"37";

line2(63 downto 56) <= X"38";

line2(55 downto 48) <= X"39";

line2(47 downto 40) <= X"3a";

line2(39 downto 32) <= X"3b";

line2(31 downto 24) <= X"3c";

line2(23 downto 16) <= X"3d";

line2(15 downto 8) <= X"3e";

line2(7 downto 0) <= X"3f";

It really helps to have an ascii character map with the hexadecimal values:

The manual provides information on the LCD display has been constructed and how it should be used and connected to an FGPA development board such as the Elbert V2 or Mimas V2 FPGA development boards. The pin connections are below:

16x2 Expansion LCD Pin Connections

The schematic Diagram is also provided in the manual but for completeness it is present below:

The display has been physically designed to connect to one of the spare input header sockets on the Elbert or Mimas V2 FPGA development boards. It could be connected to any FPGA development board or Micro-controller however as long as the pin connections are known.

LCD Display Connected to the P6 Header socket on the Mimas V2

There is some example code available for testing and learning how to use the LCD display available from Numato's website. It provides an example project to get users started.

Download the example project and uncompress it to a folder of your choice.

The project has been written in verilog which I'm not too familiar with. I am slowly learning VHDL for when I use FPGAS however verilog is similar and I know the code works as intended. If we load the project up in Xilinx WebISE we are presented with the following:

The first thing required is to change the project settings to relate to the FPGA device on the Mimas V2 development board. At the moment it is set for use with an Elbert V2 development board. Right click on the project hierachy box and select "Design Properties":

Then change all of the information in the windows that opens to match the picture below:

Mimas V2 Design Properties for LCD Example project

Next we need to update the UCF file for the one relating to the Mimas V2 FPGA development board. At the moment its written to reflect the pin connections on the Elbert V2 board. We need to change it to reflect the Mimas V2 board (which is what we are using for this example).

Right click on the UCF file and remove it from the project:

Click on 'Yes' when the confirmation window is displayed.

Now right click on the project hierachy box and select "Add Source"and then navigate to the UCF folder where the project was unzipped on your hard disk. Select the UCF file called MimasV2.ucf and click 'Open' and then click 'Ok' on the confirmation windows that appear.

Now double click on the newly added UCF file to open in for editing in Xilinx WebISE:

Here is where things get interesting. I looked at the pin labels on P6 of the Mimas V2 Development board and the pin labels on the LCD expansion PCB. I found that Row 1 of the Development board is connected to Row 2 of the LCD expansion PCB and that Row 2 of the Development board was connected to Row 1 of the LCD Expansion PCB. Because these are reversed the supplied UCF file is unfortunately not correct. I verified this with a multimeter (and a lot of patience) and re-wrote the UCF file to create the correct connections:

Now it's time to compile the project and generate a bit file ready to upload to the Mimas V2 Development board. Click on the green Arrow in the middle of the screen to compile the project:

Once that is complete generate a 'Bit File' and then upload that to the Mimas V2 Development board via your preferred method - by Python Script or by using the supplied uploader. I use the uploader.

Ensure you have a power supply connected to the Vext input of the Expansion PCB (which is capable of supplying at least 7 Volts. When you connect it up you should be presented with the following images:

I know they are upside down but it does prove the display is working!

If you wish to change the message that is displayed on the LCD then the following lines of code will be of interest:

// 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 LCDassign 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"};
The first line shifts the display right by three characters. As the display has availability for 16 characters in each line this places the first character to be displayed at position 4 on the first line.

The next ten lines assign the memory register with characters to be displayed. If you wanted to these can be changed to whichever ascii character required. Change the characters in the quotation marks but remember that a space must be " " and not "". Using "" causes three horizontal lines to be displayed. I changed the code to that displayed below:

// Shift the display right.assign memory[19]={2'b00,8'h1C}; // Character that should be displayed on the LCD.assign memory[20]={2'b10,"V"};assign memory[21]={2'b10,"E"};assign memory[22]={2'b10,"R"};assign memory[23]={2'b10,"I"};assign memory[24]={2'b10,"L"};assign memory[25]={2'b10,"O"};assign memory[26]={2'b10,"G"};assign memory[27]={2'b10," "};assign memory[28]={2'b10,"I"};assign memory[29]={2'b10,"S"};// Shift to second Line of LCDassign 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,"H"};assign memory[34]={2'b10,"A"};assign memory[35]={2'b10,"R"};assign memory[36]={2'b10,"D"};assign memory[37]={2'b10," "};assign memory[38]={2'b10,"T"};assign memory[39]={2'b10,"O"};assignmemory[40]={2'b10,"U"};assign memory[41]={2'b10,"S"};assignmemory[42]={2'b10,"E"};
I then re-compiled the code and re-uploaded it to the Mimas V2 Development board. Here is what I got:

In order to reduce the number of spaces at the beginning the lines:assignmemory[19]={2'b00,8'h1C}; assignmemory[32]={2'b00,8'h1C}; must be changed to suit the requirements. The section 2'b00 section sets the RS and RW registers to zero. Setting the RS register to zero means the display is being sent a command by the FPGA. The RW pin is 0 when sending information to the display and 1 when displaying items on the display. I am not sure what the h1C part of the code performs.

I have found that by changing the line to

assignmemory[19]={2'b00,8'h1B};

moves the text starting position to the first block on the display. I also increased the amount of memory present in the register by changing this line from 43 to 44:

// Specify the depth of block memory where the data and command are to be saved.
parameter Length=44;

That allowed me to obtain the following result:

That's all for now. I will investigate further as to how this code works.

About Me

I'm an electronics engineer and uber geek from the UK. I live in the North West of England in Manchester. I mostly spend my time working in electronics and developing custom electronic solutions to problems as well as a few fun projects. I do a lot of development using microcontrollers - particularly using the arduino