Interface SPI LCD with ESP32 – Step-by-Step Guide

In this tutorial you will learn how to connect and program an SPI LCD(ILI9341) with ESP32 using Espressif IDE. This tutorial includes wiring, code, and display tips for your embedded projects. The project is available to download at the end of this post.

SPI LCD and ESP32

This is the 10th tutorial in the ESP32 series using the Espressif-IDE and 1st in the mini series covering SPI-LCDs in ESP32. In this mini series we will first see how to interface the SPI based displays with ESP32 using the ESP32’s built in LCD library. Then we will implement the LVGL functionality to our project. Finally we will also implement the touch function in the displays that supports it.

Introducing ILI9341 Display

The ILI9341 is a popular 2.4″ to 3.2″ TFT LCD display controller that supports 240×320 resolution and SPI interface. It’s often paired with the XPT2046 touch controller, enabling capacitive or resistive touch input. Together, they provide an affordable and efficient solution for graphical user interfaces in embedded systems like STM32 and ESP32.

ILI9341

Here are the important features of the ILI9341 Display:

  • 240×320 pixel resolution with 262K color support
  • SPI communication for both display (ILI9341) and touch (XPT2046)
  • Integrated resistive touch support via XPT2046
  • Compact size and low power consumption, ideal for embedded GUIs

WIRING DIAGRAM

Below is the image showing the connection between ESP32 and ILI9341.

ILI9341 ESP32 Connection

I am going to use the VSPI_HOST for the SPI. The SPI pins in the image above are defined according to it. You can check the SPI tutorial to know more about the available instances on ESP32.

Pin NameFunctionConnected to
VCCPower Supply (3.3V)3.3V
GNDGroundGND
SDI / DI (MOSI)SPI Data InputGPIO23
SDO / DO (MISO)SPI Data OutputGPIO19
SCK SPI ClockGPIO18
DC / RSData/Command SelectGPIO21
RESETReset PinGPIO22
LCD_CSChip Select (LCD)GPIO5
LEDBacklight ControlGPIO4 / 3.3V

INITIAL SETUP

We will first create a basic project in the Espressif IDE.

Espressif IDE project create

Now go to the https://components.espressif.com/ and search for the display controller (ILI9341 in this case). You will get a lot of results, but make sure to open one with esp_lcd_**** as shown in the image below.

ILI9341 component selector

Download the component archive and extract it.

ILI9341 component download

Now copy this extracted folder inside the project folder. I have created a new folder (components), where I will place all the components used in the project.

ILI9341 component configuration

We need to link this component to our project. To do so, we will create a new file (idf_component.yml) inside the main folder.

ILI9341 component configuration

Now add the dependency of the lcd component in this file.

dependencies:
 esp_lcd_ili9341: "^2.0.0"

Note:- make sure there is only single space in the 2nd line.

Here the 2.0.0 is the component library version and it can be found in the component page itself.

ILI9341 component configuration

THE CODE

Inclusions

First we need to include the necessary files.

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include "driver/gpio.h"
#include "esp_lcd_ili9341.h"
#include "esp_lcd_panel_commands.h"
#include "esp_lcd_panel_dev.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "hal/gpio_types.h"
#include "hal/spi_types.h"

Here I have included the esp_lcd_ili9341.h file from the component. The rest of the inclusions starting with esp_lcd_panel_** are needed for the LCD operations. All of them might not be needed, but I have included all available.

Definitions

Now we will define the pins used in this project.

#define LCD_HOST	VSPI_HOST
#define PIN_NUM_SCLK    18
#define PIN_NUM_MOSI    23
#define PIN_NUM_MISO    19
#define PIN_NUM_LCD_CS  5
#define PIN_NUM_BKL     4
#define PIN_NUM_RST     22
#define PIN_NUM_LCD_DC  21

As I mentioned earlier, I am going to use the VSPI_HOST for the LCD. The pins are defined according to the connection diagram shown in the beginning of the article.

Next define some LCD related constants we will be using.

#define LCD_H_RES	240
#define LCD_V_RES	320
#define LCD_PIXEL_CLOCK_HZ	20*1000*1000
#define LCD_CMD_BITS	8
#define LCD_PARAM_BITS	8

H_RES and V_RES are the Horizontal and Vertical resolution of the display. The LCD_PIXEL_CLOCK_HZ is the Piexl clock frequency in Hertz. The pixel clock determines how quickly the ESP32 can send data to update the LEDs, thus controlling the refresh rate and animation speed. I have set it to 20MHz.

LCD_CMD_BITS and LCD_PARAM_BITS sets the bit width of the command and parameter that recognised by the LCD controller chip. This is chip specific, and ILI9341 uses 8 bits for it.

Display Driver

We will create a separate function to initialise the display driver. We just need to follow the steps mentioned in this guide.

static void display_init (void)
{
	gpio_set_direction(PIN_NUM_BKL, GPIO_MODE_OUTPUT);
	gpio_set_level(PIN_NUM_BKL, 1);

First of all I am setting the LCD backlight pin as output and the level of this pin is set to high. If you are connecting the backlight pin to 3.3V, you do not need to do this.

STEP 1

Create a SPI bus.

       spi_bus_config_t buscfg = {
    .sclk_io_num = PIN_NUM_SCLK,
    .mosi_io_num = PIN_NUM_MOSI,
    .miso_io_num = PIN_NUM_MISO,
    .quadwp_io_num = -1,
    .quadhd_io_num = -1,
    .max_transfer_sz = LCD_H_RES * 80 * sizeof(uint16_t), // transfer 80 lines of pixels (assume pixel is RGB565) at most in one SPI transaction
};
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); // Enable the DMA feature

Here we will simply define the CLK, MISO and MOSI pins for the SPI. Since we are not using QUADSPI, the respective pins are set to -1. The maximum transfer size in one SPI transaction is set to LCD_H_RES * 80 * 2 bytes. Basically we can transfer upto 80 lines data in one transaction.

STEP 2

Allocate an LCD IO device handle from the SPI bus.

esp_lcd_panel_io_spi_config_t io_config = {
    .dc_gpio_num = PIN_NUM_LCD_DC,
    .cs_gpio_num = PIN_NUM_LCD_CS,
    .pclk_hz = LCD_PIXEL_CLOCK_HZ,
    .lcd_cmd_bits = LCD_CMD_BITS,
    .lcd_param_bits = LCD_PARAM_BITS,
    .spi_mode = 0,
    .trans_queue_depth = 10,
};
// Attach the LCD to the SPI bus
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

Here we will allocate the CS and DC pins along with the Pixel Clock and command and parameter bits. The SPI Mode is set to Mode 0 and the queue depth is set to 10. This is basically the  depth of the SPI transaction queue.

STEP 3

Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip.

esp_lcd_panel_dev_config_t panel_config = {
    .reset_gpio_num = PIN_NUM_RST,
    .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
    .bits_per_pixel = 16,
};
// Create LCD panel handle for ILI9341, with the SPI IO device handle
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &lcd_panel_handle));

The rgb_ele_order sets the R-G-B element order of each color data. It is set to BGR in case of ILI9341. If there is some colour related issue, you can set it back to RGB.

bits_per_pixel sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip. ILI9341 uses RGB565, which requires 2 bytes per pixel, hence it is set to 16 bits.

The function esp_lcd_new_panel_ili9341 will attach the above configuration to the ILI9341. This function is present in the component library we downloaded. In fact a similar function is present in each LCD component library. So say for example in case of ST7735, the function will change to esp_lcd_new_panel_st7735.

STEP 4

Perform the LCD IO Operations.

So far we have only attached the LCD to the SPI. Now we will perform the initialization for the display.

esp_lcd_panel_reset(lcd_panel_handle);
esp_lcd_panel_init(lcd_panel_handle);
esp_lcd_panel_disp_on_off(lcd_panel_handle, true);
}

Here we will first reset the LCD. The initialise it and finally turn the display on.

The main function

Inside the main function, we will first initialise the display and then draw some bitmap on it. This display driver is created to be used with libraries like LVGL, hence it does not provide many functions to draw on display. Therefore we only have a function to draw the bitmap on the display.

void app_main(void)
{
    display_init();
    esp_lcd_panel_swap_xy(lcd_panel_handle, true);
    esp_lcd_panel_draw_bitmap(lcd_panel_handle, 0, 0, 100, 50, hello_map);
    while (true) {
        sleep(1);
    }
}

After initialising the display, I am using the swap function to swap the X and Y Axes of the display. Basically you can use a combination of esp_lcd_panel_swap_xy and esp_lcd_panel_mirror function to rotate the display.

I am using a small bitmap of resolution 100×50. The drawing starts at the beginning of the display (0,0). The bitmap array is defined as hello_map, which you can find in the final project itself.

Any other SPI display can be interfaced using the same method. There are only few things which needs to be taken care of.

  • The function to Create LCD panel handle, esp_lcd_new_panel_ili9341 will change according to the display.
  • The rgb_ele_order might change based on the display color endianness. You can try keeping the same order, but if the colours are different than the actual image, change this order. There are only 2 orders supported, RGB and BGR.
  • The bits_per_pixel might change based on the display. Check if the display controller supports RGB565(16 bits) or RGB888(24 bits) or XRGB8888(32 bits).
  • The LCD_H_RES and LCD_V_RES will definitely change based on the display size.
  • The LCD_CMD_BITS and LCD_PARAM_BITS might change based on the display. Do check the datasheet of the display controller.

RESULT

Below is the image showing the bitmap printed on the display.

ILI9341 ESP32 Working

The display is rotated because of the sawp function I used before drawing the bitmap.

VIDEO TUTORIAL

You can check the video to see the complete explanation and working of this project.

Check out the Video Below

PROJECT DOWNLOAD

Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

You May Also Like..

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

🛈 Advertising Disclosure

This website relies on advertisements as its main source of revenue to support the creation of free, high-quality content.
If you enjoy our work and would like to help us continue, please consider disabling your ad blocker while browsing here.

Your support truly makes a difference — thank you!

Share this Article

Recent Posts

Join Our Community

Weekly Newsletter

Subscribe to our newsletter to get our news