Просмотр исходного кода

Merge branch 'feature/lcd_lvgl_example' into 'master'

lcd: lvgl porting example

See merge request espressif/esp-idf!15042
morris 4 лет назад
Родитель
Сommit
ce969ad7fe

+ 9 - 0
examples/peripherals/lcd/lvgl/CMakeLists.txt

@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(lcd_lvgl)
+
+# As the upstream LVGL library has build warnings in esp-idf build system, this is only for temporarily workaround
+# Will remove this file when upstream LVGL fixes the warnings in the next release
+idf_component_get_property(lvgl_lib lvgl__lvgl COMPONENT_LIB)
+target_compile_options(${lvgl_lib} PRIVATE "-Wno-empty-body" "-Wno-strict-prototypes")

+ 83 - 0
examples/peripherals/lcd/lvgl/README.md

@@ -0,0 +1,83 @@
+| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- |
+# LVGL porting example
+
+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.
+
+This example can be taken as a skeleton of porting the LVGL library onto the `esp_lcd` driver layer. **Note** that, this example only focuses on the display interface, regardless of the input device driver.
+
+The whole porting code is located in [this main file](main/lvgl_example_main.c), and the UI demo code is located in [another single file](main/lvgl_demo_ui.c).
+
+The UI will display two images (one Espressif logo and another Espressif text), which have been converted into C arrays by the [online converting tool](https://lvgl.io/tools/imageconverter), and will be compiled directly into application binary.
+
+This example is constructed by [IDF component manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html), all the external dependency will be handled by the CMake build system automatically. In this case, it will help download the lvgl from [registry](https://components.espressif.com/component/lvgl/lvgl), with the version specified in the [manifest file](main/idf_component.yml).
+
+This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html) to generate the ticks needed by LVGL. For more porting guides, please refer to [LVGL porting doc](https://docs.lvgl.io/master/porting/index.html).
+
+## How to use the example
+
+### Hardware Required
+
+* An ESP development board
+* An Intel 8080 interfaced (so called MCU interface or parallel interface) LCD
+* An USB cable for power supply and programming
+
+### Hardware Connection
+
+The connection between ESP Board and the LCD is as follows:
+
+```
+   ESP Board                      LCD Screen
+┌─────────────┐              ┌────────────────┐
+│             │              │                │
+│         3V3 ├─────────────►│ VCC            │
+│             │              │                │
+│         GND ├──────────────┤ GND            │
+│             │              │                │
+│  DATA[0..7] │◄────────────►│ DATA[0..7]     │
+│             │              │                │
+│        PCLK ├─────────────►│ PCLK           │
+│             │              │                │
+│          CS ├─────────────►│ CS             │
+│             │              │                │
+│         D/C ├─────────────►│ D/C            │
+│             │              │                │
+│         RST ├─────────────►│ RST            │
+│             │              │                │
+│    BK_LIGHT ├─────────────►│ BCKL           │
+│             │              │                │
+└─────────────┘              └────────────────┘
+```
+
+The GPIO number used by this example can be changed in [lvgl_example_main.c](main/lvgl_example_main.c).
+Especially, please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lvgl_example_main.c](main/lvgl_example_main.c).
+
+### Build and Flash
+
+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.
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
+
+### Example Output
+
+```bash
+I (0) cpu_start: Starting scheduler on APP CPU.
+I (418) example: Turn off LCD backlight
+I (418) gpio: GPIO[2]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
+I (428) example: Initialize Intel 8080 bus
+I (438) example: Install LCD driver of st7789
+I (558) example: Turn on LCD backlight
+I (558) example: Initialize LVGL library
+I (558) example: Register display driver to LVGL
+I (558) example: Install LVGL tick timer
+I (558) example: Display LVGL animation
+```
+
+
+## Troubleshooting
+
+For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

+ 4 - 0
examples/peripherals/lcd/lvgl/main/CMakeLists.txt

@@ -0,0 +1,4 @@
+file(GLOB_RECURSE IMAGE_SOURCES images/*.c)
+
+idf_component_register(SRCS "lvgl_example_main.c" "lvgl_demo_ui.c" ${IMAGE_SOURCES}
+                       INCLUDE_DIRS ".")

+ 3 - 0
examples/peripherals/lcd/lvgl/main/idf_component.yml

@@ -0,0 +1,3 @@
+dependencies:
+  idf: ">=4.4"
+  lvgl/lvgl: "==8.0.2"

Разница между файлами не показана из-за своего большого размера
+ 311 - 0
examples/peripherals/lcd/lvgl/main/images/esp_logo.c


BIN
examples/peripherals/lcd/lvgl/main/images/esp_logo.png


Разница между файлами не показана из-за своего большого размера
+ 13 - 0
examples/peripherals/lcd/lvgl/main/images/esp_text.c


BIN
examples/peripherals/lcd/lvgl/main/images/esp_text.png


+ 99 - 0
examples/peripherals/lcd/lvgl/main/lvgl_demo_ui.c

@@ -0,0 +1,99 @@
+/* LCD LVGL UI example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <math.h>
+#include "lvgl.h"
+
+#ifndef PI
+#define PI  (3.14159f)
+#endif
+
+// LVGL image declare
+LV_IMG_DECLARE(esp_logo)
+LV_IMG_DECLARE(esp_text)
+
+static lv_obj_t *arc[3];
+static lv_obj_t *img_logo;
+static lv_obj_t *img_text;
+static lv_color_t arc_color[] = {
+    LV_COLOR_MAKE(232, 87, 116),
+    LV_COLOR_MAKE(126, 87, 162),
+    LV_COLOR_MAKE(90, 202, 228),
+};
+
+static void anim_timer_cb(lv_timer_t *timer)
+{
+    static int32_t count = -90;
+    lv_obj_t *scr = (lv_obj_t *) timer->user_data;
+
+    // Play arc animation
+    if (count < 90) {
+        lv_coord_t arc_start = count > 0 ? (1 - cosf(count / 180.0f * PI)) * 270 : 0;
+        lv_coord_t arc_len = (sinf(count / 180.0f * PI) + 1) * 135;
+
+        for (size_t i = 0; i < sizeof(arc) / sizeof(arc[0]); i++) {
+            lv_arc_set_bg_angles(arc[i], arc_start, arc_len);
+            lv_arc_set_rotation(arc[i], (count + 120 * (i + 1)) % 360);
+        }
+    }
+
+    // Delete arcs when animation finished
+    if (count == 90) {
+        for (size_t i = 0; i < sizeof(arc) / sizeof(arc[0]); i++) {
+            lv_obj_del(arc[i]);
+        }
+
+        // Create new image and make it transparent
+        img_text = lv_img_create(scr);
+        lv_img_set_src(img_text, &esp_text);
+        lv_obj_set_style_img_opa(img_text, 0, 0);
+    }
+
+    // Move images when arc animation finished
+    if ((count >= 100) && (count <= 180)) {
+        lv_coord_t offset = (sinf((count - 140) * 2.25f / 90.0f) + 1) * 20.0f;
+        lv_obj_align((lv_obj_t *) timer->user_data, LV_ALIGN_CENTER, 0, -offset);
+        lv_obj_align(img_text, LV_ALIGN_CENTER, 0, 2 * offset);
+        lv_obj_set_style_img_opa(img_text, offset / 40.0f * 255, 0);
+    }
+
+    // Delete timer when all animation finished
+    if (++count >= 180) {
+        lv_timer_del(timer);
+    }
+}
+
+void example_lvgl_demo_ui(lv_obj_t *scr)
+{
+    // Create image
+    img_logo = lv_img_create(scr);
+    lv_img_set_src(img_logo, &esp_logo);
+    lv_obj_center(img_logo);
+
+    // Create arcs
+    for (size_t i = 0; i < sizeof(arc) / sizeof(arc[0]); i++) {
+        arc[i] = lv_arc_create(scr);
+
+        // Set arc caption
+        lv_obj_set_size(arc[i], 220 - 30 * i, 220 - 30 * i);
+        lv_arc_set_bg_angles(arc[i], 120 * i, 10 + 120 * i);
+        lv_arc_set_value(arc[i], 0);
+
+        // Set arc style
+        lv_obj_remove_style(arc[i], NULL, LV_PART_KNOB);
+        lv_obj_set_style_arc_width(arc[i], 10, 0);
+        lv_obj_set_style_arc_color(arc[i], arc_color[i], 0);
+
+        // Make arc center
+        lv_obj_center(arc[i]);
+    }
+
+    // Create timer for animation
+    lv_timer_create(anim_timer_cb, 20, (void *) scr);
+}

+ 187 - 0
examples/peripherals/lcd/lvgl/main/lvgl_example_main.c

@@ -0,0 +1,187 @@
+/* LCD LVGL porting example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <stdio.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 "esp_err.h"
+#include "esp_log.h"
+#include "lvgl.h"
+
+static const char *TAG = "example";
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (10 * 1000 * 1000)
+#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
+#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
+#define EXAMPLE_PIN_NUM_DATA0          19
+#define EXAMPLE_PIN_NUM_DATA1          21
+#define EXAMPLE_PIN_NUM_DATA2          0
+#define EXAMPLE_PIN_NUM_DATA3          22
+#define EXAMPLE_PIN_NUM_DATA4          23
+#define EXAMPLE_PIN_NUM_DATA5          33
+#define EXAMPLE_PIN_NUM_DATA6          32
+#define EXAMPLE_PIN_NUM_DATA7          27
+#define EXAMPLE_PIN_NUM_PCLK           18
+#define EXAMPLE_PIN_NUM_CS             4
+#define EXAMPLE_PIN_NUM_DC             5
+#define EXAMPLE_PIN_NUM_RST            -1
+#define EXAMPLE_PIN_NUM_BK_LIGHT       2
+
+// The pixel number in horizontal and vertical
+#define EXAMPLE_LCD_H_RES              240
+#define EXAMPLE_LCD_V_RES              280
+// Bit number used to represent command and parameter
+#define EXAMPLE_LCD_CMD_BITS           8
+#define EXAMPLE_LCD_PARAM_BITS         8
+
+#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
+
+extern void example_lvgl_demo_ui(lv_obj_t *scr);
+
+static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, void *user_data, void *event_data)
+{
+    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_data;
+    lv_disp_flush_ready(disp_driver);
+    return false;
+}
+
+static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
+{
+    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
+    int offsetx1 = area->x1;
+    int offsetx2 = area->x2;
+    int offsety1 = area->y1;
+    int offsety2 = area->y2;
+    // 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, color_map);
+}
+
+static void example_increase_lvgl_tick(void *arg)
+{
+    /* Tell LVGL how many milliseconds has elapsed */
+    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
+}
+
+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
+
+    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);
+
+    ESP_LOGI(TAG, "Initialize Intel 8080 bus");
+    esp_lcd_i80_bus_handle_t i80_bus = NULL;
+    esp_lcd_i80_bus_config_t bus_config = {
+        .dc_gpio_num = EXAMPLE_PIN_NUM_DC,
+        .wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
+        .data_gpio_nums = {
+            EXAMPLE_PIN_NUM_DATA0,
+            EXAMPLE_PIN_NUM_DATA1,
+            EXAMPLE_PIN_NUM_DATA2,
+            EXAMPLE_PIN_NUM_DATA3,
+            EXAMPLE_PIN_NUM_DATA4,
+            EXAMPLE_PIN_NUM_DATA5,
+            EXAMPLE_PIN_NUM_DATA6,
+            EXAMPLE_PIN_NUM_DATA7,
+        },
+        .bus_width = 8,
+        .max_transfer_bytes = EXAMPLE_LCD_H_RES * 40 * sizeof(uint16_t)
+    };
+    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,
+        .trans_queue_depth = 10,
+        .dc_levels = {
+            .dc_idle_level = 0,
+            .dc_cmd_level = 0,
+            .dc_dummy_level = 0,
+            .dc_data_level = 1,
+        },
+        .on_color_trans_done = example_notify_lvgl_flush_ready,
+        .user_data = &disp_drv,
+        .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_LOGI(TAG, "Install LCD driver of st7789");
+    esp_lcd_panel_handle_t panel_handle = NULL;
+    esp_lcd_panel_dev_config_t panel_config = {
+        .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
+        .color_space = ESP_LCD_COLOR_SPACE_RGB,
+        .bits_per_pixel = 16,
+    };
+    ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
+
+    esp_lcd_panel_reset(panel_handle);
+    esp_lcd_panel_init(panel_handle);
+    esp_lcd_panel_invert_color(panel_handle, true);
+    // the gap is LCD panel specific, even panels with the same driver IC, can have different gap value
+    esp_lcd_panel_set_gap(panel_handle, 0, 20);
+
+    ESP_LOGI(TAG, "Turn on LCD backlight");
+    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
+
+    ESP_LOGI(TAG, "Initialize LVGL library");
+    lv_init();
+    // 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
+    lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
+    assert(buf1);
+    lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
+    assert(buf2);
+    // initialize LVGL draw buffers
+    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);
+
+    ESP_LOGI(TAG, "Register display driver to LVGL");
+    lv_disp_drv_init(&disp_drv);
+    disp_drv.hor_res = EXAMPLE_LCD_H_RES;
+    disp_drv.ver_res = EXAMPLE_LCD_V_RES;
+    disp_drv.flush_cb = example_lvgl_flush_cb;
+    disp_drv.draw_buf = &disp_buf;
+    disp_drv.user_data = panel_handle;
+    lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
+
+    ESP_LOGI(TAG, "Install LVGL tick timer");
+    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
+    const esp_timer_create_args_t lvgl_tick_timer_args = {
+        .callback = &example_increase_lvgl_tick,
+        .name = "lvgl_tick"
+    };
+    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, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
+
+    ESP_LOGI(TAG, "Display LVGL animation");
+    lv_obj_t *scr = lv_disp_get_scr_act(disp);
+    example_lvgl_demo_ui(scr);
+
+    while (1) {
+        // raise the task priority of LVGL and/or reduce the handler period can improve the performance
+        vTaskDelay(pdMS_TO_TICKS(10));
+        // The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
+        lv_timer_handler();
+    }
+}

+ 3 - 0
examples/peripherals/lcd/lvgl/sdkconfig.defaults

@@ -0,0 +1,3 @@
+CONFIG_LV_USE_PERF_MONITOR=y
+CONFIG_LV_USE_USER_DATA=y
+CONFIG_LV_COLOR_16_SWAP=y

+ 0 - 2
examples/peripherals/lcd/tjpgd/README.md

@@ -6,8 +6,6 @@
 
 This example shows how to decode a jpeg image and display it on an SPI-interfaced LCD, and rotates the image periodically.
 
-Due to the fact that ESP32S2 and ESP32C3 don't have enough memory to hold the decoded image, the graphic shown on the LCD is calculated randomly and has nothing to do with the picture in the example project.
-
 If you want to adapt this example to another type of display or pinout, check [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c) for comments with some implementation details.
 
 ## How to Use Example

+ 1 - 0
tools/ci/check_examples_cmake_make-cmake_ignore.txt

@@ -6,3 +6,4 @@ main/
 build_system/cmake/
 mb_example_common/
 examples/cxx/experimental/blink_cxx
+examples/peripherals/lcd/lvgl

Некоторые файлы не были показаны из-за большого количества измененных файлов