【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)

目录

一、前言  

二、代码

三、硬件说明

四、ESP-IDF设置(重要)

五、屏幕偏移修改驱动文件

参考资料


一、前言  

        前几天在ardiuno框架下基于platformIO用TFT_eSPI库实现了lvgl库的移植,这种方法虽然在lvgl的版本上有很大优势,但是platformIO的自定义库的添加有点麻烦,而且网上的大部分esp32资料都是基于ESP-IDF框架,因此还是在尝试一下ESP-IDF框架的lvgl开发。

        esp32移植lvgl有官方的lvgl_port_esp32项目可以直接移植,不过目前的lvgl_port_esp32项目是基于lvgl7,主要是由于这个项目的重要组件lvgl_esp32_drivers库的lvgl8版本还没升级出来,但也没关系,作为一个初学者,版本的优劣可以先不考虑。下面将参考lvgl_port_esp32项目的内容简单实现一下lvgl7的移植,其中遇到了一些bug做一个记录。

二、代码

        首先用ESP-IDF插件创建一个示例项目,并把lvgl_port_esp32项目内componets的lvgl和lvgl_esp32_drivers库复制到新建项目的componets内。

【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)_第1张图片 图一-示例文件树

         然后把lvgl_port_esp32的main的内容复制到项目里,但是这里不用官方demo示例,自己添加一个简单的库做一个spinner效果测试一下添加自定义库的方法。这一步骤有四个文件的添加,参考了b站一位大佬的代码,即preloader.h,preloader.c,CMakeLists.txt(perloader库内),main.c。下面贴出来代码:

preloader.h

#ifndef _PRELOADER_H
#define _PRELOADER_H

void create_preloader_demo();

#endif

preloader.c

#include "preloader.h"
#include "lvgl.h"

void create_preloader_demo(){
    lv_obj_t * preload = lv_spinner_create(lv_scr_act(),NULL);
    lv_obj_set_size(preload,100,100);
    lv_obj_align(preload,NULL,LV_ALIGN_CENTER,0,0);
}

CMakeLists.txt(perloader库内)

idf_component_register(SRCS "preloader.c"
                    INCLUDE_DIRS "."
                    REQUIRES lvgl)

main.c

/* LVGL Example project
 *
 * Basic project to test LVGL on ESP32 based projects.
 *
 * 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 
#include 
#include 
#include 

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "driver/gpio.h"

/* Littlevgl specific */
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif

#include "lvgl_helpers.h"

#include "preloader.h"


/*********************
 *      DEFINES
 *********************/
#define LV_TICK_PERIOD_MS 1

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void create_demo_application(void);

/**********************
 *   APPLICATION MAIN
 **********************/
void app_main() {

    /* If you want to use a task to create the graphic, you NEED to create a Pinned task
     * Otherwise there can be problem such as memory corruption and so on.
     * NOTE: When not using Wi-Fi nor Bluetooth you can pin the guiTask to core 0 */
    xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 0, NULL, 1);
}

/* Creates a semaphore to handle concurrent call to lvgl stuff
 * If you wish to call *any* lvgl function from other threads/tasks
 * you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;

static void guiTask(void *pvParameter) {

    (void) pvParameter;
    xGuiSemaphore = xSemaphoreCreateMutex();

    lv_init();

    /* Initialize SPI or I2C bus used by the drivers */
    lvgl_driver_init();

    lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1 != NULL);

    /* Use double buffered when not working with monochrome displays */
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2 != NULL);
#else
    static lv_color_t *buf2 = NULL;
#endif

    static lv_disp_buf_t disp_buf;

    uint32_t size_in_px = DISP_BUF_SIZE;

#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820         \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A    \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D     \
    || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306

    /* Actual size in pixels, not bytes. */
    size_in_px *= 8;
#endif

    /* Initialize the working buffer depending on the selected display.
     * NOTE: buf2 == NULL when using monochrome displays. */
    lv_disp_buf_init(&disp_buf, buf1, buf2, size_in_px);

    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_driver_flush;

    /* When using a monochrome display we need to register the callbacks:
     * - rounder_cb
     * - set_px_cb */
#ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    disp_drv.rounder_cb = disp_driver_rounder;
    disp_drv.set_px_cb = disp_driver_set_px;
#endif

    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    /* Register an input device when enabled on the menuconfig */
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.read_cb = touch_driver_read;
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    lv_indev_drv_register(&indev_drv);
#endif

    /* Create and start a periodic timer interrupt to call lv_tick_inc */
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"
    };
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));

    /* Create the demo application */
    create_demo_application();

    while (1) {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));

        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
       }
    }

    /* A task should NEVER return */
    free(buf1);
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    free(buf2);
#endif
    vTaskDelete(NULL);
}

static void create_demo_application(void)
{
    create_preloader_demo();
}

static void lv_tick_task(void *arg) {
    (void) arg;

    lv_tick_inc(LV_TICK_PERIOD_MS);
}

        其中,main文件就是lvgl_port_esp32的main的create_demo_application函数内容删除,并改成我们自己的 create_preloader_demo函数。

三、硬件说明

        开发板:ESP-WROOM-32

        屏幕:中景园1.47寸lcd显示屏高清ips172x320 st7789驱动液晶屏

四、ESP-IDF设置(重要)

      ESP-IDF设置是有个小坑的,这里先贴一下主要需要改的部分: 

【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)_第2张图片 图二-ESP-IDF设置-LVGL configuration

【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)_第3张图片 图三-ESP-IDF设置-LVGL TFT Display controller

【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)_第4张图片 图四-ESP-IDF设置-Display Pin Assignments

        这些设定大部分没什么值得说的,按照自己的情况修改即可。但是这个LVGL TFT Display controller的频率设定应该是有坑的,我的设备这里只能设定为80MHz,其他情况下都会全黑屏,什么都不显示。

五、屏幕偏移修改驱动文件

        到上一步编译其实是已经能点亮屏幕并显示的,但是屏幕会出现偏移的现象,经过多方查找,找到了这个屏幕的偏移修改方法。

        在lvgl_esp32_drivers库的lvgl_tft文件夹内找到st7789.c驱动文件。并对函数void st7789_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map)进行修改增加两行:

    offsety1+=34;
    offsety2+=34;

修改后的函数内容变为:

/* The ST7789 display controller can drive 320*240 displays, when using a 240*240
 * display there's a gap of 80px, we need to edit the coordinates to take into
 * account that gap, this is not necessary in all orientations. */
void st7789_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map)
{
    uint8_t data[4] = {0};

    uint16_t offsetx1 = area->x1;
    uint16_t offsetx2 = area->x2;
    uint16_t offsety1 = area->y1;
    uint16_t offsety2 = area->y2;

    offsety1+=34;
    offsety2+=34;

#if (CONFIG_LV_TFT_DISPLAY_OFFSETS)
    offsetx1 += CONFIG_LV_TFT_DISPLAY_X_OFFSET;
    offsetx2 += CONFIG_LV_TFT_DISPLAY_X_OFFSET;
    offsety1 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET;
    offsety2 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET;

#elif (LV_HOR_RES_MAX == 240) && (LV_VER_RES_MAX == 240)
#if (CONFIG_LV_DISPLAY_ORIENTATION_PORTRAIT)
    offsetx1 += 80;
    offsetx2 += 80;
#elif (CONFIG_LV_DISPLAY_ORIENTATION_LANDSCAPE_INVERTED)
    offsety1 += 80;
    offsety2 += 80;
#endif
#endif

    /*Column addresses*/
    st7789_send_cmd(ST7789_CASET);
    data[0] = (offsetx1 >> 8) & 0xFF;
    data[1] = offsetx1 & 0xFF;
    data[2] = (offsetx2 >> 8) & 0xFF;
    data[3] = offsetx2 & 0xFF;
    st7789_send_data(data, 4);

    /*Page addresses*/
    st7789_send_cmd(ST7789_RASET);
    data[0] = (offsety1 >> 8) & 0xFF;
    data[1] = offsety1 & 0xFF;
    data[2] = (offsety2 >> 8) & 0xFF;
    data[3] = offsety2 & 0xFF;
    st7789_send_data(data, 4);

    /*Memory write*/
    st7789_send_cmd(ST7789_RAMWR);

    uint32_t size = lv_area_get_width(area) * lv_area_get_height(area);

    st7789_send_color((void*)color_map, size * 2);

}

        自此,已经成功实现esp32移植lvgl7驱动st7789屏幕。

【esp32&lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)_第5张图片 图五-运行效果

参考资料

ESP32 IDF LVGL 1.47寸圆角屏幕测试_史达芬林的博客-CSDN博客(屏幕设定)

ESP32 IDF 移植 LVGL - 哔哩哔哩 (bilibili.com)(代码主体)

【开源】中景园1.47寸圆角屏测试 IPS 172*320,PPI很高呀!_哔哩哔哩_bilibili(硬件来源)

你可能感兴趣的:(lvgl,单片机)