This article will discuss the details of the interface of a basic 16 × 2 LCD module with an FPGA.
In this tutorial, we will use Verilog HDL to design a digital circuit that interfaces with common LCD modules that are based on the HD44780 LCD driver / controller chip. The Mojo V3 FPGA board will be used to implement the design. The LCD module used in this article is the 1602A display.
HD44780 Compatible LCD Modules
In a previous article, we discussed the details of LCD modules using the HD44780 LCD driver / controller chip. The pinout for these LCD modules is shown in Figure 1.
Figure 1. Image courtesy of AAC.
As you can see, there are three control pins (RS, R / W, and E) along with eight data pins (DB7 to DB0). For a write operation, we need to apply the appropriate voltages to these pins, as shown in the timing diagram in Figure 2 below.
Figure 2. Courtesy of HITACHI.
The different delay values from the above timing diagram are given in the following table:
Table 1. Courtesy of HITACHI.
There are several instructions that can be used to configure the LCD module. You can find a complete list of these instructions on page 24 of this data sheet. Some important instructions are listed below.
Design Building Blocks: Route 1
We want to display the message “HELLO WORLD!” On the LCD screen. Let’s see what building blocks our digital system needs to communicate with the LCD module. We need a ROM to store the ASCII code for the message characters. As shown in Figure 3, the ROM output will be connected to the eight data pins of the LCD module.
The message “HELLO WORLD!” It has 12 characters, and we can use a 12 ✕ 8 bit ROM to store the message. However, let’s consider a 16 ✕ 8-bit ROM for the message, so that we can display messages of up to 16 characters (the length of the first row of the LCD screen).
You may wonder why the figure above shows a 20 ✕ 8-bit ROM if our message has a maximum length of 16 characters. These additional four bytes will be used to store data related to the LCD screen instructions that allow us to configure the module. For example, as indicated in Table 2, we can apply hexadecimal value 0x38 to the LCD data pins to specify that the LCD module should receive / send data in 8-bit lengths. Like character codes, command hexadecimal values are applied to data pins, therefore we can store them in the same ROM.
In this article, we will use four commands (0x38, 0x06, 0x0C, and 0x01) to configure the LCD. You can find a brief description of these commands in Table 2. For a detailed explanation, please refer to this article. So we have a 20 ✕ 8 bit ROM, where the first four bytes are 0x38, 0x06, 0x0C, and 0x01. The next 16 bytes store the ASCII code for our message.
Taking into account the timing diagram in Figure 2, we note that the data applied to DB7-DB0 must not change for a certain time for the LCD screen to be able to read it correctly. Therefore, as shown in Figure 3, we need a set of D-type (DFF) flip-flops to keep the address of the ROM (and consequently DB7-DB0) constant over a period of time. After the LCD screen reads the data, we will need to increase the address value by one to apply the next character / command to the LCD screen. Therefore, as shown in Figure 4, a multiplexer and an incrementer must be placed before the DFFs.
Using the selection input of the multiplexer, we can specify whether the address value should remain constant (S1 = 0) or be incremented (S1 = 1). The multiplexer has a third option that allows us to reset the value of the address to 00000. This can be achieved by resetting the DFFs as well. We will refer to all of these blocks as Way 1, as the picture shows. As we will see later in the article, we will use S1 signal to control circuits within the Way 1 to block. the addr-reg The signal will be used to monitor the status of Way 1.
It’s worth mentioning that while the figure above shows only one DFF, there are actually five of them (because the 20-byte ROM needs a five-bit address bus).
Building Block Design: Implementing Delays
As mentioned above, the waveforms applied to the LCD control inputs should follow the timing diagram in Figure 2. After the RW and RS pins change state, we should wait for tLIKE before setting pin E to high logic. Then a transition from high to low in E starts a write operation. However, for a certain time before (tDSW) and after (tH) the falling edge of E, the data should not change.
Therefore, to successfully communicate with the LCD, we have to implement the delays tCOMO, tDSW, and TH. This can be accomplished by an accountant. As sometimes we need to stop the counter or reset it, we will put a multiplexer before the DFFs of the counter. This is shown in Figure 5 below.
In this way, we can use input S2 to control the counter and the cnt_reg The output will give us the status of the counter, which is an indication of the elapsed time. According to the time diagram, we need three different time delays: tCOMO = 40 ns, tDSW = 80 ns, and tH = 10 ns. Noting that the Mojo V3 clock frequency is 50 MHz (a clock period of 20 ns), we can calculate the number of counts needed for each time delay.
However, it turns out that my 16 × 2 LCD needs much longer delays to operate. This is probably due to the fact that I am using relatively long cables and a breadboard to test my design. In this article, I will implement tHOW = 10 ms and tH = 3 ms. I will keep the E signal high for 58 ms (PWEh = 58 ms). My layout should be able to work with slightly shorter delays. You can do some experiments and modify these numbers for your design.
How many bits do we need for the counter? With the delays chosen, the total delay will be 10 + 58 + 3 ms = 71 ms. Taking into account the Mojo’s clock period (20 ns), a delay of 71 ms can be implemented counting from 0 to 3,549,999. Therefore, as shown in Figure 5, we need a 22-bit counter.
Design Building Blocks: The Control Path
The last building block we need is a unit that controls the Way 1 Y Way 2 It locks properly so that the desired waveforms are generated for the LCD module. This can be achieved by a finite state machine (FSM) that receives addr_reg Y cnt_reg signals (the state of Way 1 Y Way 2) and generates appropriate s1 Y s2 signals An initial design for this FSM is shown in Figure 6.
Note that the diagram only shows the state transitions (FSM outputs are not shown). There are three states: during idle State the signals get their initial values. the lcd_init status applies LCD configuration commands; that is why the state transition condition is (addr_reg = 3 && cnt_reg = 3,550,000). In other words, we have four commands, and when the fourth command (addr_reg = 3) is applied to the LCD for 71 ms (cnt_reg = 3,550,000), we must go to the lcd_print State that sends the message data to the LCD screen. When we reach the end of the ROM (addr_reg = 19), we will have to wait 71 ms (cnt_reg = 3,550,000) and then move on to the idle state.
The difficult part of the project is almost finished. Now, we just need to design the FSM and write its Verilog code. These tasks will be covered in the next part of the article.
With FPGAs, we generally have to see the problem at the lowest level of the design. What we have are logic gates and some basic building blocks, such as adders and comparators. While this makes FPGA design difficult, FPGAs do offer several advantages. In this article, we examine the building blocks for connecting an FPGA to a common 16 × 2 LCD module. A ROM, some DFFs and multiplexers are required to implement the FPGA to LCD interface. Also, we need an FSM to control these building blocks. The following article will give you the details for designing the FSM and writing its Verilog code. Then we will program the Mojo V3 board to verify our design.