lcd display esp32 factory
ESP chips can generate various kinds of timings that needed by common LCDs on the market, like SPI LCD, I80 LCD (a.k.a Intel 8080 parallel LCD), RGB/SRGB LCD, I2C LCD, etc. The esp_lcd component is officially to support those LCDs with a group of universal APIs across chips.
In esp_lcd, an LCD panel is represented by esp_lcd_panel_handle_t, which plays the role of an abstract frame buffer, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer and the hardware connection interface, the LCD panel drivers are mainly grouped into the following categories:
Controller based LCD driver involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install. The frame buffer is located in the controller’s internal GRAM (Graphical RAM). ESP-IDF provides only a limited number of LCD controller drivers out of the box (e.g. ST7789, SSD1306), More Controller Based LCD Drivers are maintained in the Espressif Component Registry
LCD Panel IO Operations - provides a set of APIs to operate the LCD panel, like turning on/off the display, setting the orientation, etc. These operations are common for either controller-based LCD panel driver or RGB LCD panel driver.
esp_lcd_panel_io_spi_config_t::dc_gpio_num: Sets the gpio number for the DC signal line (some LCD calls this RS line). The LCD driver will use this GPIO to switch between sending command and sending data.
esp_lcd_panel_io_spi_config_t::cs_gpio_num: Sets the gpio number for the CS signal line. The LCD driver will use this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e. this LCD), you can set the gpio number to -1 to occupy the bus exclusively.
esp_lcd_panel_io_spi_config_t::pclk_hz sets the frequency of the pixel clock, in Hz. The value should not exceed the range recommended in the LCD spec.
esp_lcd_panel_io_spi_config_t::spi_mode sets the SPI mode. The LCD driver will use this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the SPI Master API doc.
esp_lcd_panel_io_spi_config_t::lcd_cmd_bits and esp_lcd_panel_io_spi_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
esp_lcd_panel_io_spi_config_t::trans_queue_depth sets the depth of the SPI transaction queue. A bigger value means more transactions can be queued up, but it also consumes more memory.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the SPI IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_panel_io_i2c_config_t::dev_addr sets the I2C device address of the LCD controller chip. The LCD driver will use this address to communicate with the LCD controller chip.
esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits and esp_lcd_panel_io_i2c_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I2C IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_i80_bus_config_t::data_gpio_nums is the array of the GPIO number of the data bus. The number of GPIOs should be equal to the esp_lcd_i80_bus_config_t::bus_width value.
esp_lcd_panel_io_i80_config_t::pclk_hz sets the pixel clock frequency in Hz. Higher pixel clock frequency will result in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
esp_lcd_panel_io_i80_config_t::lcd_cmd_bits and esp_lcd_panel_io_i80_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
esp_lcd_panel_io_i80_config_t::trans_queue_depth sets the maximum number of transactions that can be queued in the LCD IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I80 IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_panel_dev_config_t::reset_gpio_num sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to -1.
More LCD panel drivers and touch drivers are available in IDF Component Registry. The list of available and planned drivers with links is in this table.
esp_lcd_panel_draw_bitmap() is the most significant function, that will do the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
ESP chips can generate various kinds of timings that needed by common LCDs on the market, like SPI LCD, I80 LCD (a.k.a Intel 8080 parallel LCD), RGB/SRGB LCD, I2C LCD, etc. The esp_lcd component is officially to support those LCDs with a group of universal APIs across chips.
In esp_lcd, an LCD panel is represented by esp_lcd_panel_handle_t, which plays the role of an abstract frame buffer, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer, the LCD panel allocation functions are mainly grouped into the following categories:
Drivers for some LCD and touch controllers are available in IDF Component Registry. The list of available and planned drivers with links is in this table.
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
ESP chips can generate various kinds of timings that needed by common LCDs on the market, like SPI LCD, I80 LCD (a.k.a Intel 8080 parallel LCD), RGB LCD, I2C LCD, etc. The esp_lcd component is officially to support those LCDs with a group of universal APIs across chips.
In esp_lcd, an LCD panel is represented by esp_lcd_panel_handle_t, which plays the role of an abstract frame buffer, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer, the LCD panel allocation functions are mainly grouped into the following categories:
NoteCommands sent by this function are short, so they are sent using polling transactions. The function does not return before the command tranfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
ESP chips can generate various kinds of timings that needed by common LCDs on the market, like SPI LCD, I80 LCD (a.k.a Intel 8080 parallel LCD), RGB/SRGB LCD, I2C LCD, etc. The esp_lcd component is officially to support those LCDs with a group of universal APIs across chips.
In esp_lcd, an LCD panel is represented by esp_lcd_panel_handle_t, which plays the role of an abstract frame buffer, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer and the hardware connection interface, the LCD panel drivers are mainly grouped into the following categories:
Controller based LCD driver involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install. The frame buffer is located in the controller’s internal GRAM (Graphical RAM). ESP-IDF provides only a limited number of LCD controller drivers out of the box (e.g. ST7789, SSD1306), More Controller Based LCD Drivers are maintained in the Espressif Component Registry
RGB Interfaced LCD - is based on a group of specific synchronous signals indicating where to start and stop a frame. The frame buffer is allocated on the ESP side. The driver install steps are much simplified because we don’t need to install any IO interface driver in this case.
LCD Panel IO Operations - provides a set of APIs to operate the LCD panel, like turning on/off the display, setting the orientation, etc. These operations are common for either controller-based LCD panel driver or RGB LCD panel driver.
esp_lcd_panel_io_spi_config_t::dc_gpio_num: Sets the gpio number for the DC signal line (some LCD calls this RS line). The LCD driver will use this GPIO to switch between sending command and sending data.
esp_lcd_panel_io_spi_config_t::cs_gpio_num: Sets the gpio number for the CS signal line. The LCD driver will use this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e. this LCD), you can set the gpio number to -1 to occupy the bus exclusively.
esp_lcd_panel_io_spi_config_t::pclk_hz sets the frequency of the pixel clock, in Hz. The value should not exceed the range recommended in the LCD spec.
esp_lcd_panel_io_spi_config_t::spi_mode sets the SPI mode. The LCD driver will use this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the SPI Master API doc.
esp_lcd_panel_io_spi_config_t::lcd_cmd_bits and esp_lcd_panel_io_spi_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
esp_lcd_panel_io_spi_config_t::trans_queue_depth sets the depth of the SPI transaction queue. A bigger value means more transactions can be queued up, but it also consumes more memory.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the SPI IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_panel_io_i2c_config_t::dev_addr sets the I2C device address of the LCD controller chip. The LCD driver will use this address to communicate with the LCD controller chip.
esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits and esp_lcd_panel_io_i2c_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I2C IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_i80_bus_config_t::data_gpio_nums is the array of the GPIO number of the data bus. The number of GPIOs should be equal to the esp_lcd_i80_bus_config_t::bus_width value.
esp_lcd_panel_io_i80_config_t::pclk_hz sets the pixel clock frequency in Hz. Higher pixel clock frequency will result in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
esp_lcd_panel_io_i80_config_t::lcd_cmd_bits and esp_lcd_panel_io_i80_config_t::lcd_param_bits set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
esp_lcd_panel_io_i80_config_t::trans_queue_depth sets the maximum number of transactions that can be queued in the LCD IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I80 IO device handle that allocated in the last step, and some panel specific configurations:
esp_lcd_panel_dev_config_t::bits_per_pixel sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
esp_lcd_panel_dev_config_t::reset_gpio_num sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to -1.
More LCD panel drivers and touch drivers are available in IDF Component Registry. The list of available and planned drivers with links is in this table.
esp_lcd_rgb_panel_config_t::clk_src selects the clock source for the RGB LCD controller. The available clock sources are listed in lcd_clock_source_t.
esp_lcd_rgb_panel_config_t::bits_per_pixel set the number of bits per pixel. This is different from esp_lcd_rgb_panel_config_t::data_width. By default, if you set this field to 0, the driver will automatically adjust the bpp to the esp_lcd_rgb_panel_config_t::data_width. But in some cases, these two value must be different. For example, a Serial RGB interface LCD only needs 8 data lines, but the color width can reach to RGB888, i.e. the esp_lcd_rgb_panel_config_t::bits_per_pixel should be set to 24.
esp_lcd_rgb_panel_config_t::sram_trans_align and esp_lcd_rgb_panel_config_t::psram_trans_align set the alignment of the allocated frame buffer. Internally, the DMA transfer ability will adjust against these alignment values. A higher alignment value can lead to a bigger DMA burst size. Please note, the alignment value must be a power of 2.
esp_lcd_rgb_panel_config_t::bounce_buffer_size_px set the size of bounce buffer. This is only necessary for a so-called “bounce buffer” mode. Please refer to Bounce Buffer with Single PSRAM Frame Buffer for more information.
esp_lcd_rgb_panel_config_t::timings sets the LCD panel specific timing parameters. All required parameters are listed in the esp_lcd_rgb_timing_t, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
esp_lcd_rgb_panel_config_t::fb_in_psram sets whether to allocate the frame buffer from PSRAM or not. Please refer to Single Frame Buffer in PSRAM for more information.
esp_lcd_rgb_panel_config_t::num_fbs sets the number of frame buffers allocated by the driver. For backward compatibility, 0 means to allocate one frame buffer. Please use esp_lcd_rgb_panel_config_t::no_fb if you don’t want to allocate any frame buffer.
Most of the time, the RGB LCD driver should maintain at least one screen sized frame buffer. According to the number and location of the frame buffer, the driver provides several different buffer modes.
This is the default and simplest and you don’t have to specify flags or bounce buffer options. A frame buffer is allocated from the internal memory. The frame data is read out by DMA to the LCD verbatim. It needs no CPU intervention to function, but it has the downside that it uses up a fair bit of the limited amount of internal memory.
If you have PSRAM and want to store the frame buffer there rather than in the limited internal memory, the LCD peripheral will use EDMA to fetch frame data directly from the PSRAM, bypassing the internal cache. You can enable this feature by setting the esp_lcd_rgb_panel_config_t::fb_in_psram to true. The downside of this is that when both the CPU as well as EDMA need access to the PSRAM, the bandwidth will be shared between them, that is, EDMA gets half and the CPUs get the other half. If there’re other peripherals using EDMA as well, with a high enough pixel clock this can lead to starvation of the LCD peripheral, leading to display corruption. However, if the pixel clock is low enough for this not to be an issue, this is a solution that uses almost no CPU intervention.
The PSRAM shares the same SPI bus with the main Flash (the one stores your firmware binary). At one time, there only be one consumer of the SPI bus. When you also use the main flash to serve your file system (e.g. SPIFFS), the bandwidth of the underlying SPI bus will also be shared, leading to display corruption. You can use esp_lcd_rgb_panel_set_pclk() to update the pixel clock frequency to a lower value.
To avoid tearing effect, using two screen sized frame buffers is the easiest approach. In this mode, the frame buffer can only be allocated from PSRAM, because of the limited internal memory. The frame buffer that the CPU write to and the frame buffer that the EDMA read from are guaranteed to be different and independent. The EDMA will only switch between the two frame buffers when the previous write operation is finished and the current frame has been sent to the LCD. The downside of this mode is that, you have to maintain the synchronization between the two frame buffers.
This mode allocates two so-called bounce buffers from the internal memory, and a main frame buffer that is still in PSRAM. This mode is selected by setting the esp_lcd_rgb_panel_config_t::fb_in_psram flag and additionally specifying a non-zero esp_lcd_rgb_panel_config_t::bounce_buffer_size_px value. The bounce buffers only need to be large enough to hold a few lines of display data, which is significantly less than the main frame buffer. The LCD peripheral will use DMA to read data from one of the bounce buffers, and meanwhile an interrupt routine will use the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral has finished reading the bounce buffer, the two buffers change place and the CPU can fill the others. The advantage of this mode is that, you can achieve higher pixel clock frequency. As the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a major increase in CPU use and the LCD CAN’T work if we disable the cache of the external memory, via e.g. OTA or NVS write to the main flash.
It’s highly recommended to turn on the “PSRAM XIP (Execute In Place)” feature in this mode by enabling the Kconfig options: CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA, which allows the CPU to fetch instructions and readonly data from the PSRAM instead of the main flash. What’s more, the external memory cache won’t be disabled even if you attempt to write to the main flash through SPI1. This makes it possible to display an OTA progress bar for your application.
Note that this mode also allows for a esp_lcd_rgb_panel_config_t::bb_invalidate_cache flag to be set. Enabling this frees up the cache lines after they’re used to read out the frame buffer data from PSRAM, but it may lead to slight corruption if the other core writes data to the frame buffer at the exact time the cache lines are freed up. (Technically, a write to the frame buffer can be ignored if it falls between the cache writeback and the cache invalidate calls.)
This mode is similar to the Bounce Buffer with Single PSRAM Frame Buffer, but there is no PSRAM frame buffer initialized by the LCD driver. Instead, the user supplies a callback function that is responsible for filling the bounce buffers. As this driver does not care where the written pixels come from, this allows for the callback doing e.g. on-the-fly conversion from a smaller, 8-bit-per-pixel PSRAM frame buffer to an 16-bit LCD, or even procedurally-generated frame-buffer-less graphics. This option is selected by setting the esp_lcd_rgb_panel_config_t::no_fb flag and supplying a esp_lcd_rgb_panel_config_t::bounce_buffer_size_px value. And then register the esp_lcd_rgb_panel_event_callbacks_t::on_bounce_empty callback by calling esp_lcd_rgb_panel_register_event_callbacks().
It should never happen in a well-designed embedded application, but it can in theory be possible that the DMA cannot deliver data as fast as the LCD consumes it. In the ESP32-S3 hardware, this leads to the LCD simply outputting dummy bytes while DMA waits for data. If we were to run DMA in a stream fashion, this would mean a de-sync between the LCD address the DMA reads the data for and the LCD address the LCD peripheral thinks it outputs data for, leading to a permanently shifted image.
In order to stop this from happening, you can either enable the CONFIG_LCD_RGB_RESTART_IN_VSYNC option, so the driver can restart the DMA in the VBlank interrupt automatically or call esp_lcd_rgb_panel_restart() to restart the DMA manually. Note esp_lcd_rgb_panel_restart() doesn’t restart the DMA immediately, the DMA will still be restarted in the next VSYNC event.
esp_lcd_panel_draw_bitmap() is the most significant function, that will do the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
Commands sent by this function are short, so they are sent using polling transactions. The function does not return before the command transfer is completed. If any queued transactions sent by esp_lcd_panel_io_tx_color() are still pending when this function is called, this function will wait until they are finished and the queue is empty before sending the command(s).
This function can be useful when the LCD controller is out of sync with the DMA because of insufficient bandwidth. To save the screen from a permanent shift, you can call this function to restart the LCD DMA.
If CONFIG_LCD_RGB_RESTART_IN_VSYNC is enabled, you don’t need to call this function manually, because the restart job will be done automatically in the VSYNC event handler.
If this flag is enabled, the host only refresh the frame buffer when esp_lcd_panel_draw_bitmap is called. This is useful when the LCD screen has a GRAM and can refresh the LCD by itself.
[in] How many pixels already were sent to the display in this frame, in other words, at what pixel the routine should start putting data into bounce_buf
Open Arduino IDE, find TFT_eSPI in the file and example, the T-Display factory test program is located at TFT_eSPI -> FactoryTest, you can also use other sample programs provided by TFT_eSPI
3 In the Arduino IDE tool options, select the development board ESP32 Dev Module, select Disable in the PSRAM option, select 4MB in the Flash Size option, Other keep the default
In this project we are using ESP32 so select “Bring Your Own Thing”but if you are using Arduino or Raspberry Pi you may select the same as shown in the screenshot.
There a lot of different LCD. HD44780 compatible parallel (4bit and 8 bit mode), UART (RS232), I2C or ISP. Also known as character LCD. Then you have graphic displays.
This tutorial shows how to use the I2C LCD (Liquid Crystal Display) with the ESP32 using Arduino IDE. We’ll show you how to wire the display, install the library and try sample code to write text on the LCD: static text, and scroll long messages. You can also use this guide with the ESP8266.
Additionally, it comes with a built-in potentiometer you can use to adjust the contrast between the background and the characters on the LCD. On a “regular” LCD you need to add a potentiometer to the circuit to adjust the contrast.
Before displaying text on the LCD, you need to find the LCD I2C address. With the LCD properly wired to the ESP32, upload the following I2C Scanner sketch.
After uploading the code, open the Serial Monitor at a baud rate of 115200. Press the ESP32 EN button. The I2C address should be displayed in the Serial Monitor.
Displaying static text on the LCD is very simple. All you have to do is select where you want the characters to be displayed on the screen, and then send the message to the display.
The next two lines set the number of columns and rows of your LCD display. If you’re using a display with another size, you should modify those variables.
Then, you need to set the display address, the number of columns and number of rows. You should use the display address you’ve found in the previous step.
To display a message on the screen, first you need to set the cursor to where you want your message to be written. The following line sets the cursor to the first column, first row.
Scrolling text on the LCD is specially useful when you want to display messages longer than 16 characters. The library comes with built-in functions that allows you to scroll text. However, many people experience problems with those functions because:
The messageToScroll variable is displayed in the second row (1 corresponds to the second row), with a delay time of 250 ms (the GIF image is speed up 1.5x).
In a 16×2 LCD there are 32 blocks where you can display characters. Each block is made out of 5×8 tiny pixels. You can display custom characters by defining the state of each tiny pixel. For that, you can create a byte variable to hold the state of each pixel.
In summary, in this tutorial we’ve shown you how to use an I2C LCD display with the ESP32/ESP8266 with Arduino IDE: how to display static text, scrolling text and custom characters. This tutorial also works with the Arduino board, you just need to change the pin assignment to use the Arduino I2C pins.
We hope you’ve found this tutorial useful. If you like ESP32 and you want to learn more, we recommend enrolling in Learn ESP32 with Arduino IDE course.