How to Write an LCD Driver

Setting up a C library for the DFR0555 1602 LCD Display.

Alberto Tontoni
Published in
5 min readApr 11, 2022

--

As an embedded programmer, you usually have to deal with a variety of physical devices, and even though sometimes you may find available libraries to drive them using software, most of the times it’s not the case.
In my experience, I commonly start with an open source Arduino library, but very soon realize that I need more control over the underlying machinery, so I embrace my keyboard, create an .h file, and start creating my own driver.

Today I’m glad to share with you an approach
to rapidly develop minimalist drivers for I2C devices!

To make the explanation simpler to follow, we will make use of the following case study: let the DFR0555 LCD display interact with the FRDM-KL25Z development board from NXP, which is based on ARM Cortex-M0+ microcontroller, and is compatible with many Arduino shields.

Note that, however, the discussion is very general and can be followed using potentially any microcontroller.

Alright, let’s move on!

Step 1 : Docs & Codes

First of all, let’s make an idea about the kind of device we’re dealing with.

The DFR0555 is a 16x2 LCD display that integrates a backlight controller.
From DFRobot website, we can easily download the datasheet and discover that the display is based on AIP31068L controller.

Perhaps more interestingly, DFRobot also provides an Arduino library for fast interfacing with the device they sold us.
Someone may reasonably argue that the FRDM board is not compatible with the Arduino software API. But here’s the trick: use any Arduino-compatible board that’s somehow similar to the one you’re dealing with, and run the example codes there.

In my case, I’ve used the lovely STM32F401RE from STMicroelectronics as the board to run the Blink example. Obviously, you don’t need a board substitute if the example code can be compiled and ran for your target.

At this point, we can leverage the onboard debugger to step through the library code: in this way, we get an idea of which bytes are sent from the microcontroller to the LCD display through I2C.
For instance, the function DFRobot_RGBLCD1602::init() starts the communication sending the bytes 0x80 0x28 to the slave with address 0x3e.

Looking at the datasheet, we find out the following:

  1. 0x3e is the LCD display address
  2. 0x28 is a command that stands for “Function set: Display in 2-lines mode”
  3. Before sending any command to the display, we must issue the 0x80 control byte
AiP31068 Datasheet, Table 3 — Instruction Table

Section recap:
Logically you would look for the docs before having the codes, but very often it’s easier to do the opposite. Instead, run the example code and step through the library instructions using a debugger. Then employ the datasheet to perform a double check and see whether the operations are consistent.

In the next step, we’ll find out exactly how the whole communication process works by using analysis of digital signals.

Step 2 : Logic Level Analysis

Using a debugger to analyze an entire I2C communication is tedious.
Instead, you better save hours of imprecations by putting approximately ten bucks in a logic level analyzer: it’s a piece of hardware which is able to sample the zeros and ones along one or more wires.

You can attach this tool to the SCL and SDA lines of I2C peripheral, and inspect what’s going on using Pulseview.

A logic level analyzer

Here’s part of what I obtained by running the previous sketch. From the timing diagram, I could desume the following:

  1. I2C Master (ie. STM32) starts write sequence to slave 0x3e (ie. DFR0555)
  2. Master writes a few bytes on the bus
  3. Slave acknowledges each byte
  4. At the end of communication, master issues a stop condition
How Pulseview looks — SDA and SCL lines attached to CH0, CH1 (1), software decodes the message (2)

Now we have precise information on how to make up an I2C transaction from scratch.
But before writing any library code, let’s replicate the communication
by hand.

Step 3 : I2C Console

Find an existing, or implement your own minimalist, firmware for I2C-through-UART communication: it basically allows you to type a bunch of bytes on a serial monitor, and the microcontroller will relay them over the I2C data bus. Just for simplicity, I’m gonna call it the I2C Console.

Taking advantage of the session captured using the logic analyzer, let’s extract all the bytes that are sent to the LCD module. Here’s a small recap.

DFR0555 commands for initialization and character display

Now, try out the sequence of commands using the I2C Console. As an example, here’s how the module behaves when I type in the command to display a couple of characters.

Using I2C Console to display “Hello!” on the LCD

Okay, end of the boring part 🤓
It’s time to get our hands dirty with some embedded coding!

Side note: here’s my version of the I2C Console for the FRDM board, if you want to take inspiration.

Step 4: Automate!

If you’ve employed the so-called board substitute in the previous steps, this is the time to get back to your actual target microcontroller.
I won’t cover the nitty gritty of the implementation here, since my architecture may be really different from yours.

Nonetheless, you can take a look at my code on GitHub for a ready-made application that uses the FRDM-KL25Z board to write characters on the display. Feel free to take it as an inspiration for your projects!

I’ve used plain old C and the code has been developed, compiled, and uploaded on the board using Keil uVision 5.

Conclusion

That’s it for today! I hope you enjoyed the discussion. For any feedback, or if anything is unclear to you, feel free to get in touch with me. I’ll be glad to clarify 😊

Many thanks for your attention. See you next time!

--

--