Skip to content

Commit

Permalink
Merge branch 'feat/add_lvgl_filesystem_example' into 'master'
Browse files Browse the repository at this point in the history
feat(LCD):  Update i80 example of loading image from file system

Closes IDF-8026

See merge request espressif/esp-idf!25661
  • Loading branch information
suda-morris committed Sep 6, 2023
2 parents 61920d0 + 5ea4df6 commit 6f6ed69
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 55 deletions.
14 changes: 12 additions & 2 deletions examples/peripherals/lcd/i80_controller/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |

# LVGL porting example (based on i80 interfaced LCD controller)

LVGL is an open-source graphics library for creating modern GUIs. It has plenty of built-in graphical elements with low memory footprint, which is friendly for embedded GUI applications.
Expand All @@ -26,7 +27,7 @@ This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en

The connection between ESP Board and the LCD is as follows:

```
```text
ESP Board LCD Screen
┌─────────────┐ ┌────────────────┐
│ │ │ │
Expand Down Expand Up @@ -71,6 +72,8 @@ Run `idf.py menuconfig` to open a terminal UI where you can tune specific config

* `Allocate color data from PSRAM`: Select this option if you want to allocate the LVGL draw buffers from PSRAM.

* `LCD image source from`: Select where to load the image resource. See [Image Resource](#image-resource) for more details.

Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A fancy animation will show up on the LCD as expected.

The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from registry into `managed_components` folder.
Expand Down Expand Up @@ -99,15 +102,22 @@ I (558) example: Display LVGL animation
This example supports touch screen connected via I2C. You can enable it by running `idf.py menuconfig` and navigating to `Example Configuration -> Enable LCD touch`. When touch is enabled, there will be a new button in the GUI that can restart the animation.

These touch controllers are supported:

* [GT911](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_gt911)
* [TT21100](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_tt21100)
* [FT5X06](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch_ft5x06)

## Image Resource

This example supports two ways of reading images

* from the [SPIFFS file system](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/spiffs.html). This is the suggested way we use in the example. It may take a little bit longer to load the image because of the bottleneck of the SPI flash read speed, but it will save the binary size.
* from the embedded binary (i.e., pre-decode the image into an array and pack it together with the application firmware). By this way, you can get faster image loading speed at the cost of bloating your application binary. What's worse, if you enabled the [XIP from PSRAM](https://github.com/espressif/esp-idf/tree/master/examples/system/xip_from_psram) feature, it will increase the PSRAM usage as well.

## Troubleshooting

* Can't get a stable UI when `EXAMPLE_LCD_I80_COLOR_IN_PSRAM` is enabled.

This is because of the limited PSRAM bandwidth, compared to the internal SRAM. You can either decrease the PCLK clock `EXAMPLE_LCD_PIXEL_CLOCK_HZ` in [i80_controller_example_main.c](main/i80_controller_example_main.c) or increase the PSRAM working frequency `SPIRAM_SPEED` from the KConfig (e.g. **ESP32S3-Specific** -> **Set RAM clock speed**) or decrease the FPS in LVGL configuration. For illustration, this example has set the refresh period to 100ms in the default sdkconfig file.

For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
12 changes: 10 additions & 2 deletions examples/peripherals/lcd/i80_controller/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
file(GLOB_RECURSE IMAGE_SOURCES images/*.c)
set(embedded_images)
if(CONFIG_EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY)
file(GLOB_RECURSE embedded_images images/*.c)
endif()

idf_component_register(SRCS "i80_controller_example_main.c" "lvgl_demo_ui.c" ${IMAGE_SOURCES}
idf_component_register(SRCS "i80_controller_example_main.c" "lvgl_demo_ui.c" ${embedded_images}
INCLUDE_DIRS ".")

if(CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM)
# Create a partition to store the image resources in the filesystem
spiffs_create_partition_image(storage ./images/filesystem FLASH_IN_PROJECT)
endif()
12 changes: 12 additions & 0 deletions examples/peripherals/lcd/i80_controller/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,16 @@ menu "Example Configuration"
bool "FT5X06"
endchoice

choice EXAMPLE_LCD_IMAGE_SOURCE
prompt "LCD image source from"
default EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM
help
Select LCD image source

config EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM
bool "File system"

config EXAMPLE_LCD_IMAGE_FROM_EMBEDDED_BINARY
bool "Embedded binary"
endchoice
endmenu
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
Expand All @@ -11,21 +11,20 @@
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_touch.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
#include "driver/i2c.h"
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
#include "esp_lcd_touch_gt911.h"
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_TT21100
#include "esp_lcd_touch_tt21100.h"
#elif CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06
#include "esp_lcd_touch_ft5x06.h"
#endif
#endif

static const char *TAG = "example";

Expand Down Expand Up @@ -112,7 +111,7 @@ static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_
}

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static void example_lvgl_touch_cb(lv_indev_drv_t * drv, lv_indev_data_t * data)
static void example_lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t touchpad_x[1] = {0};
uint16_t touchpad_y[1] = {0};
Expand Down Expand Up @@ -140,19 +139,46 @@ static void example_increase_lvgl_tick(void *arg)
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

void app_main(void)
#if CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM
void example_init_filesystem(void)
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions

ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
ESP_LOGI(TAG, "Initializing filesystem");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = "storage",
.max_files = 5,
.format_if_mount_failed = true
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);

// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
esp_err_t ret = esp_vfs_spiffs_register(&conf);

if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
}
return;
}

size_t total = 0, used = 0;
ret = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
esp_spiffs_format(conf.partition_label);
return;
} else {
ESP_LOGI(TAG, "Partition size: total: %zu, used: %zu", total, used);
}
}
#endif // CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM

void example_init_i80_bus(esp_lcd_panel_io_handle_t *io_handle, void *user_ctx)
{
ESP_LOGI(TAG, "Initialize Intel 8080 bus");
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config = {
Expand Down Expand Up @@ -185,7 +211,7 @@ void app_main(void)
.sram_trans_align = 4,
};
ESP_ERROR_CHECK(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 = {
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
Expand All @@ -200,12 +226,15 @@ void app_main(void)
.swap_color_bytes = !LV_COLOR_16_SWAP, // Swap can be done in LvGL (default) or DMA
},
.on_color_trans_done = example_notify_lvgl_flush_ready,
.user_ctx = &disp_drv,
.user_ctx = user_ctx,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, io_handle));
}

void example_init_lcd_panel(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t *panel)
{
esp_lcd_panel_handle_t panel_handle = NULL;
#if CONFIG_EXAMPLE_LCD_I80_CONTROLLER_ST7789
ESP_LOGI(TAG, "Install LCD driver of st7789");
Expand Down Expand Up @@ -258,26 +287,28 @@ void app_main(void)
// ILI9341 is very similar to ST7789 and shares the same driver.
// Anything unconventional (such as this custom gamma table) can
// be issued here in user code and need not modify the driver.
esp_lcd_panel_io_tx_param(io_handle, 0xF2, (uint8_t[]) { 0 }, 1); // 3Gamma function disable
esp_lcd_panel_io_tx_param(io_handle, 0x26, (uint8_t[]) { 1 }, 1); // Gamma curve 1 selected
esp_lcd_panel_io_tx_param(io_handle, 0xF2, (uint8_t[]) {
0
}, 1); // 3Gamma function disable
esp_lcd_panel_io_tx_param(io_handle, 0x26, (uint8_t[]) {
1
}, 1); // Gamma curve 1 selected
esp_lcd_panel_io_tx_param(io_handle, 0xE0, (uint8_t[]) { // Set positive gamma
0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00 }, 15);
0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00
}, 15);
esp_lcd_panel_io_tx_param(io_handle, 0xE1, (uint8_t[]) { // Set negative gamma
0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F }, 15);
0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F
}, 15);
#endif

// 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_LOGI(TAG, "Turn on LCD backlight");
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
*panel = panel_handle;
}

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
void example_init_lcd_touch(esp_lcd_touch_handle_t *tp_handle)
{
esp_lcd_touch_handle_t tp = NULL;
esp_lcd_panel_io_handle_t tp_io_handle = NULL;

ESP_LOGI(TAG, "Initialize I2C");

const i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = EXAMPLE_I2C_SDA,
Expand Down Expand Up @@ -315,7 +346,6 @@ void app_main(void)
},
};


/* Initialize touch */
#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_GT911
ESP_LOGI(TAG, "Initialize touch controller GT911");
Expand All @@ -327,8 +357,48 @@ void app_main(void)
ESP_LOGI(TAG, "Initialize touch controller FT5X06");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp));
#endif
*tp_handle = tp;
}
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED

#endif
void app_main(void)
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions

#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0

#if CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM
example_init_filesystem();
#endif // CONFIG_EXAMPLE_LCD_IMAGE_FROM_FILE_SYSTEM

esp_lcd_panel_io_handle_t io_handle = NULL;
example_init_i80_bus(&io_handle, &disp_drv);

esp_lcd_panel_handle_t panel_handle = NULL;
example_init_lcd_panel(io_handle, &panel_handle);

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
esp_lcd_touch_handle_t tp_handle = NULL;
example_init_lcd_touch(&tp_handle);
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED

// Stub: 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));

#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn on LCD backlight");
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0

ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
Expand All @@ -338,15 +408,12 @@ void app_main(void)
lv_color_t *buf2 = NULL;
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
buf1 = heap_caps_aligned_alloc(EXAMPLE_PSRAM_DATA_ALIGNMENT, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
assert(buf1);
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
buf2 = heap_caps_aligned_alloc(EXAMPLE_PSRAM_DATA_ALIGNMENT, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
#endif // CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
assert(buf1);
assert(buf2);
ESP_LOGI(TAG, "buf1@%p, buf2@%p", buf1, buf2);
// initialize LVGL draw buffers
Expand Down Expand Up @@ -377,10 +444,9 @@ void app_main(void)
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.disp = disp;
indev_drv.read_cb = example_lvgl_touch_cb;
indev_drv.user_data = tp;

indev_drv.user_data = tp_handle;
lv_indev_drv_register(&indev_drv);
#endif
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED

ESP_LOGI(TAG, "Display LVGL animation");
example_lvgl_demo_ui(disp);
Expand Down
Loading

0 comments on commit 6f6ed69

Please sign in to comment.