Description
Hello!
I am using an ESP32 (WroverKit) with the ili9341 LCD display, it is hooked up using SPI communication. I am using VSC with the ESP IDF extension.
when setting up the environment, I added LVGL (ver 9.2.0) as a dependency using:
idf.py add-dependency "lvgl/lvgl"
and I added a driver for the display using:
idf.py add-dependency "espressif/esp_lcd_ili9341"
After this I wrote some code which displays a label on the screen, which seemed to work fine.
A while later I set up the environment on a different computer, I added the dependencies the same way (this time getting LVGL ver 9.3.0) and flashed the code, but nothing showed on the display (the backlight did turn on but the display was black).
So I tried downgrading LVGL back to ver 9.2.0 and it worked fine.
I was wondering why this is happening and what can I change in the code to make it work for version 9.3.0.
Any help and feedback would be greatly appreciated! thank you so much!
What MCU/Processor/Board and compiler are you using?
ESP32
What LVGL version are you using?
9.2.0 and 9.3.0
What do you want to achieve?
to understand why the attached code works for 9.2.0 but does not work on 9.3.0.
Code to reproduce
here is a simplified version of the code
#include <stdio.h>
#include <unistd.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "esp_lcd_ili9341.h"
static _lock_t lvgl_api_lock;
static void LVGL_TASK(void *arg)
{
uint32_t time_till_next_ms = 0;
uint32_t time_threshold_ms = 1000 / CONFIG_FREERTOS_HZ;
while (1) {
_lock_acquire(&lvgl_api_lock);
time_till_next_ms = lv_timer_handler();
_lock_release(&lvgl_api_lock);
// in case of triggering a task watch dog time out
time_till_next_ms = MAX(time_till_next_ms, time_threshold_ms);
usleep(1000 * time_till_next_ms);
}
}
static void LVGL_Flush_Callback(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// because SPI LCD is big-endian, we need to swap the RGB bytes order
lv_draw_sw_rgb565_swap(px_map, (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1));
// copy a buffer's content to a specific area of the display
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
}
static void Increment_LVGL_Tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(2);
}
static bool Notify_LVGL_Flush(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_display_t *disp = (lv_display_t *)user_ctx;
lv_display_flush_ready(disp);
return false;
}
void app_main(void)
{
//set the back light
gpio_set_direction(5,GPIO_MODE_OUTPUT);
gpio_set_level(5, 0);
//initialize the SPI communication with the panel
spi_bus_config_t Panel_SPI_Bus = {
.sclk_io_num = 19,
.mosi_io_num = 23,
.miso_io_num = 25,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 240 * 80 * sizeof(uint16_t),
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &Panel_SPI_Bus, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_handle_t io_handle = NULL; //create the IO handler the lcd panel library will use
esp_lcd_panel_io_spi_config_t io_config = { //the configuration of the panel
.dc_gpio_num = 21, //Data/Command pin
.cs_gpio_num = 22, //chip select pin
.pclk_hz = 20 * 1000 * 1000, //pixel clock
.lcd_cmd_bits = 8, //the amount of command bits in the SPI communication
.lcd_param_bits = 8, //the amount of parameter bits in the SPI communication
.spi_mode = 0,
.trans_queue_depth = 10, // A bigger value means more transactions can be queued up, but it also consumes more memory.
};
// Attach the LCD to the SPI bus
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &io_handle));
//configure panel
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = 18,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
.bits_per_pixel = 16,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
// user can flush pre-defined pattern to the screen before we turn on the screen or backlight
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
lv_init();
// create a lvgl display
lv_display_t *display = lv_display_create(240, 320);
// alloc draw buffers used by LVGL
// it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
size_t draw_buffer_sz = 240 * 20 * sizeof(lv_color16_t);
void *buf1 = spi_bus_dma_memory_alloc(SPI2_HOST, draw_buffer_sz, 0);
assert(buf1);
void *buf2 = spi_bus_dma_memory_alloc(SPI2_HOST, draw_buffer_sz, 0);
assert(buf2);
// initialize LVGL draw buffers
lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL);
// associate the mipi panel handle to the display
lv_display_set_user_data(display, panel_handle);
// set color depth
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
// set the callback which can copy the rendered image to an area of the display
lv_display_set_flush_cb(display, LVGL_Flush_Callback);
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &Increment_LVGL_Tick,
.name = "lvgl_tick"
};
//create the timers
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 2 * 1000));
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = Notify_LVGL_Flush,
};
/* Register done callback */
ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, display));
xTaskCreate(LVGL_TASK, "LVGL", 8192, NULL, 2, NULL);
_lock_acquire(&lvgl_api_lock);
lv_obj_t * scr = lv_scr_act();
lv_obj_set_style_bg_color(scr, lv_color_hex(0x115588), LV_PART_MAIN);
lv_obj_t * label = lv_label_create(scr);
lv_label_set_text(label, "I only work on LVGL ver 9.2.0!");
lv_obj_set_style_text_color(label, lv_color_hex(0xf2f3f4 ), LV_PART_MAIN);
lv_obj_set_style_text_font(label, &lv_font_montserrat_14, LV_PART_MAIN);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); // Start at center
_lock_release(&lvgl_api_lock);
}