Copy this code into your project and then modify it to easily configure the ESP LCD Panel API to work with your device. The code supports several types of SPI, and both 8 and 16 bit i8080.
Update: This code is fine, but if you want a PlatformIO project and, several drivers with this code (plus slightly updated functionality), you can download these bits here.
Introduction
I hate writing the same code more than once, especially when I find I need it in project after project, and yet some things are not worth abstracting, or the cost of abstracting is unrealistic.
Setting up Espressif's ESP LCD Panel API is one of those things. If you need the code once, chances are you'll need it over and over again in future projects, slightly changed for the particular hardware you are using.
To that end, I've provided a bunch of boilerplate code wherein you simply set some #define
s and call lcd_panel_init()
and Bob's your uncle. Instant initialization code for your device.
Licensing
This code is public domain and copyleft. It requires no attribution or license, but you use it at your own risk.
Using this Mess
Copy the code below into your project, and modify the defines to fit your hardware. I've provided several examples.
LCD_SPI_HOST is for SPI devices only and indicates which ESP32 SPI host is used for the LCD.
LCD_BK_LIGHT_ON_LEVEL should be 1 for most devices, but some turn the backlight on when the pin is low, so setting this to zero will work for those devices.
PIN_NUM_XXXXX are GPIO assignments for the various connections.
LCD_PANEL indicates the function to create a new driver instance for the display's controller.
LCD_FLUSH_CALLBACK is the routine that will be called when the DMA transfer is complete.
LCD_TRANSFER_SIZE is the maximum size of the DMA buffer that can be sent.
The rest of the LCD_XXXXX functions are various details and minor errata about your hardware.
I've provided examples for several devices - the Lilygo TTGO T1 Display, the Makerfabs ESP Display S3 Parallel w/ Touch, the Espressif ESP_WROVER_KIT 4.1, the M5Stack Core2, and the M5Stack Fire.
I'm posting the code in two parts. The first part is the license. I don't require you use it but it has been asked for, so I'm putting it here.
#ifdef TTGO_T1
#define LCD_SPI_HOST SPI3_HOST
#define LCD_BK_LIGHT_ON_LEVEL 1
#define LCD_BK_LIGHT_OFF_LEVEL !LCD_BK_LIGHT_ON_LEVEL
#define PIN_NUM_MOSI 19
#define PIN_NUM_CLK 18
#define PIN_NUM_CS 5
#define PIN_NUM_DC 16
#define PIN_NUM_RST 23
#define PIN_NUM_BCKL 4
#define LCD_PANEL esp_lcd_new_panel_st7789
#define LCD_HRES 135
#define LCD_VRES 240
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_RGB
#define LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define LCD_GAP_X 40
#define LCD_GAP_Y 52
#define LCD_MIRROR_X false
#define LCD_MIRROR_Y true
#define LCD_INVERT_COLOR true
#define LCD_SWAP_XY true
#define LCD_TRANSFER_SIZE (32*1024)
#define LCD_FLUSH_CALLBACK lcd_flush_ready
#endif // TTGO_T1
#ifdef ESP_WROVER_KIT
#define LCD_BK_LIGHT_ON_LEVEL 0
#define LCD_BK_LIGHT_OFF_LEVEL !LCD_BK_LIGHT_ON_LEVEL
#define LCD_SPI_HOST HSPI_HOST
#define PIN_NUM_MISO 25
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 19
#define PIN_NUM_CS 22
#define PIN_NUM_DC 21
#define PIN_NUM_RST 18
#define PIN_NUM_BCKL 5
#define LCD_PANEL esp_lcd_new_panel_ili9341
#define LCD_HRES 240
#define LCD_VRES 320
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_BGR
#define LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define LCD_GAP_X 0
#define LCD_GAP_Y 0
#define LCD_MIRROR_X false
#define LCD_MIRROR_Y false
#define LCD_INVERT_COLOR false
#define LCD_SWAP_XY true
#define LCD_TRANSFER_SIZE (32*1024)
#define LCD_FLUSH_CALLBACK lcd_flush_ready
#endif // ESP_WROVER_KIT
#ifdef ESP_DISPLAY_S3
#define LCD_BK_LIGHT_ON_LEVEL 1
#define LCD_BK_LIGHT_OFF_LEVEL !LCD_BK_LIGHT_ON_LEVEL
#define PIN_NUM_CS 37
#define PIN_NUM_WR 35
#define PIN_NUM_RD 48
#define PIN_NUM_RS 36
#define PIN_NUM_D00 47
#define PIN_NUM_D01 21
#define PIN_NUM_D02 14
#define PIN_NUM_D03 13
#define PIN_NUM_D04 12
#define PIN_NUM_D05 11
#define PIN_NUM_D06 10
#define PIN_NUM_D07 9
#define PIN_NUM_D08 3
#define PIN_NUM_D09 8
#define PIN_NUM_D10 16
#define PIN_NUM_D11 15
#define PIN_NUM_D12 7
#define PIN_NUM_D13 6
#define PIN_NUM_D14 5
#define PIN_NUM_D15 4
#define PIN_NUM_BCKL 45
#define LCD_PANEL esp_lcd_new_panel_ili9488
#define LCD_HRES 320
#define LCD_VRES 480
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_BGR
#define LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
#define LCD_GAP_X 0
#define LCD_GAP_Y 0
#define LCD_MIRROR_X false
#define LCD_MIRROR_Y false
#define LCD_INVERT_COLOR false
#define LCD_SWAP_XY true
#define LCD_TRANSFER_SIZE (32*1024)
#define LCD_FLUSH_CALLBACK lcd_flush_ready
#endif // ESP_DISPLAY_S3
#ifdef M5STACK_CORE2
#define LCD_SPI_HOST SPI3_HOST
#define LCD_BK_LIGHT_ON_LEVEL 1
#define LCD_BK_LIGHT_OFF_LEVEL !LCD_BK_LIGHT_ON_LEVEL
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 18
#define PIN_NUM_CS 5
#define PIN_NUM_DC 15
#define LCD_PANEL esp_lcd_new_panel_ili9342
#define LCD_HRES 240
#define LCD_VRES 320
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_BGR
#define LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define LCD_GAP_X 0
#define LCD_GAP_Y 0
#define LCD_MIRROR_X false
#define LCD_MIRROR_Y false
#define LCD_INVERT_COLOR true
#define LCD_SWAP_XY true
#define LCD_TRANSFER_SIZE (32*1024)
#define LCD_FLUSH_CALLBACK lcd_flush_ready
#endif
#ifdef M5STACK_FIRE
#define LCD_SPI_HOST SPI3_HOST
#define LCD_BK_LIGHT_ON_LEVEL 1
#define LCD_BK_LIGHT_OFF_LEVEL !LCD_BK_LIGHT_ON_LEVEL
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 18
#define PIN_NUM_CS 14
#define PIN_NUM_DC 27
#define PIN_NUM_RST 33
#define PIN_NUM_BCKL 32
#define LCD_PANEL esp_lcd_new_panel_ili9342
#define LCD_HRES 240
#define LCD_VRES 320
#define LCD_COLOR_SPACE ESP_LCD_COLOR_SPACE_BGR
#define LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define LCD_GAP_X 0
#define LCD_GAP_Y 0
#define LCD_MIRROR_X false
#define LCD_MIRROR_Y false
#define LCD_INVERT_COLOR true
#define LCD_SWAP_XY true
#define LCD_TRANSFER_SIZE (32*1024)
#define LCD_FLUSH_CALLBACK lcd_flush_ready
#endif
esp_lcd_panel_handle_t lcd_handle
void lcd_panel_init() {
#ifdef PIN_NUM_BCKL
pinMode(PIN_NUM_BCKL, OUTPUT);
#endif // PIN_NUM_BCKL
#ifdef LCD_SPI_HOST // 1-bit SPI
spi_bus_config_t bus_config;
memset(&bus_config, 0, sizeof(bus_config));
bus_config.sclk_io_num = PIN_NUM_CLK;
bus_config.mosi_io_num = PIN_NUM_MOSI;
#ifdef PIN_NUM_MISO
bus_config.miso_io_num = PIN_NUM_MISO;
#else
bus_config.miso_io_num = -1;
#endif // PIN_NUM_MISO
#ifdef PIN_NUM_QUADWP
bus_config.quadwp_io_num = PIN_NUM_QUADWP;
#else
bus_config.quadwp_io_num = -1;
#endif
#ifdef PIN_NUM_QUADHD
bus_config.quadhd_io_num = PIN_NUM_QUADHD;
#else
bus_config.quadhd_io_num = -1;
#endif
bus_config.max_transfer_sz = LCD_TRANSFER_SIZE + 8;
spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO);
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config;
memset(&io_config, 0, sizeof(io_config));
io_config.dc_gpio_num = PIN_NUM_DC,
io_config.cs_gpio_num = PIN_NUM_CS,
io_config.pclk_hz = LCD_PIXEL_CLOCK_HZ,
io_config.lcd_cmd_bits = 8,
io_config.lcd_param_bits = 8,
io_config.spi_mode = 0,
io_config.trans_queue_depth = 10,
io_config.on_color_trans_done = LCD_FLUSH_CALLBACK;
esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST,
&io_config, &
io_handle);
#elif defined(PIN_NUM_D07) // 8 or 16-bit i8080
pinMode(PIN_NUM_RD,OUTPUT);
digitalWrite(PIN_NUM_RD,HIGH);
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config;
memset(&bus_config,0,sizeof(bus_config));
bus_config.clk_src = LCD_CLK_SRC_PLL160M;
bus_config.dc_gpio_num = PIN_NUM_RS;
bus_config.wr_gpio_num = PIN_NUM_WR;
bus_config.data_gpio_nums[0] = PIN_NUM_D00;
bus_config.data_gpio_nums[1] = PIN_NUM_D01;
bus_config.data_gpio_nums[2] = PIN_NUM_D02;
bus_config.data_gpio_nums[3] = PIN_NUM_D03;
bus_config.data_gpio_nums[4] = PIN_NUM_D04;
bus_config.data_gpio_nums[5] = PIN_NUM_D05;
bus_config.data_gpio_nums[6] = PIN_NUM_D06;
bus_config.data_gpio_nums[7] = PIN_NUM_D07;
#ifdef PIN_NUM_D15
bus_config.data_gpio_nums[8] = PIN_NUM_D08;
bus_config.data_gpio_nums[9] = PIN_NUM_D09;
bus_config.data_gpio_nums[10] = PIN_NUM_D10;
bus_config.data_gpio_nums[11] = PIN_NUM_D11;
bus_config.data_gpio_nums[12] = PIN_NUM_D12;
bus_config.data_gpio_nums[13] = PIN_NUM_D13;
bus_config.data_gpio_nums[14] = PIN_NUM_D14;
bus_config.data_gpio_nums[15] = PIN_NUM_D15;
bus_config.bus_width = 16;
#else
bus_config.bus_width = 8;
#endif // PIN_NUM_D15
bus_config.max_transfer_bytes = LCD_TRANSFER_SIZE;
esp_lcd_new_i80_bus(&bus_config, &i80_bus);
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_i80_config_t io_config;
memset(&io_config,0,sizeof(io_config));
io_config.cs_gpio_num = PIN_NUM_CS;
io_config.pclk_hz = LCD_PIXEL_CLOCK_HZ;
io_config.trans_queue_depth = 20;
io_config.dc_levels.dc_idle_level=0;
io_config.dc_levels.dc_idle_level = 0;
io_config.dc_levels.dc_cmd_level = 0;
io_config.dc_levels.dc_dummy_level = 0;
io_config.dc_levels.dc_data_level = 1;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
io_config.on_color_trans_done = LCD_FLUSH_CALLBACK;
io_config.user_ctx = nullptr;
io_config.flags.swap_color_bytes = true;
io_config.flags.cs_active_high = false;
io_config.flags.reverse_color_bits = false;
esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle);
#endif // PIN_NUM_D15
lcd_handle = NULL;
esp_lcd_panel_dev_config_t panel_config;
memset(&panel_config, 0, sizeof(panel_config));
#ifdef PIN_NUM_RST
panel_config.reset_gpio_num = PIN_NUM_RST;
#else
panel_config.reset_gpio_num = -1;
#endif
panel_config.color_space = LCD_COLOR_SPACE;
panel_config.bits_per_pixel = 16;
LCD_PANEL(io_handle, &panel_config, &lcd_handle);
#ifdef PIN_NUM_BCKL
digitalWrite(PIN_NUM_BCKL, LCD_BK_LIGHT_OFF_LEVEL);
#endif // PIN_NUM_BCKL
esp_lcd_panel_reset(lcd_handle);
esp_lcd_panel_init(lcd_handle);
esp_lcd_panel_swap_xy(lcd_handle, LCD_SWAP_XY);
esp_lcd_panel_set_gap(lcd_handle, LCD_GAP_X, LCD_GAP_Y);
esp_lcd_panel_mirror(lcd_handle, LCD_MIRROR_X, LCD_MIRROR_Y);
esp_lcd_panel_invert_color(lcd_handle, LCD_INVERT_COLOR);
esp_lcd_panel_disp_off(lcd_handle, false);
#ifdef PIN_NUM_BCKL
digitalWrite(PIN_NUM_BCKL, LCD_BK_LIGHT_ON_LEVEL);
#endif // PIN_NUM_BCKL
}
History
- 8th March, 2023 - Initial submission