LVGL官方文档:文档链接
本文使用屏幕:ST7735(1.8寸,分辨率128*160)
本文使用框架:ESP-IDF(VSCODE)

移植

准备工作

下载 lvgl (注意是v7版本,v8暂不适配):
https://github.com/lvgl/lvgl/tree/release/v7
下载 lvgl_esp32_drivers(master 分支):
https://github.com/lvgl/lvgl_esp32_drivers/tree/master

开始移植

  • 创建新项目
    新手可以参考 这一篇文章 里面的 常用操作 -> 自己创建工程

  • 创建 components
    新建 components 文件夹,并将准备工作中下载的 lvgl 和 lvgl_esp32_drivers 移入该文件夹中,创建结果应与下图相符合

在这里插入图片描述

  • 添加 SPI_HOST_MAX 定义
    在 lvgl_esp32_drivers 里的 lvgl_helpers.h 里面的合适位置添加 SPI_HOST_MAX 定义
#define SPI_HOST_MAX 3
  • 在 menuconfig 里面进行相关配置
    !!!这一环节很重要,一步没注意就会导致屏幕显示异常
    • LVGL configuration
      在此处修改屏幕分辨率(128*160为例),勾选 Swap 这一选项
      在这里插入图片描述
    • LVGL TFT Display controller
      Display orientation 可以更改屏幕显示方向,controller model 选择ST7735S,勾选 Use custom SPI clock frequency 我的设置为10MHz
      在这里插入图片描述
    • Display Pin Assignments
      这里的引脚设置可以自定义,以下仅作参考,Backlight Control 可有可无,不用时选择 Not Used
      在这里插入图片描述
  • 修改驱动代码
    在 lvgl_esp32_drivers -> lvgl_tft 里的 st7735s.h 文件里 修改以下定义,COLSTART 和 ROWSTART 的值需要根据所用的屏幕分辨率进行调整,本文所用的128*160,应将两者设置为0
#define COLSTART            0
#define ROWSTART            0

编译运行

  • 添加 main.c 代码
    给出一段测试代码(lv_port_esp32 工程中删减的 main.c 代码),运行效果为白底黑字显示“Hello world”,关于代码的讲解放在下一篇去介绍。
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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"

/*********************
 *      DEFINES
 *********************/
#define TAG "demo"
#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 */
    lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2 != NULL);

    static lv_disp_buf_t disp_buf;

    uint32_t size_in_px = DISP_BUF_SIZE;

    /* 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;

#if defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT || defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT_INVERTED
    disp_drv.rotated = 1;
#endif

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

    /* 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)
{
    /* When using a monochrome display we only show "Hello World" centered on the
     * screen */

    /* use a pretty small demo for monochrome displays */
    /* Get the current screen  */
    lv_obj_t * scr = lv_disp_get_scr_act(NULL);

    /*Create a Label on the currently active screen*/
    lv_obj_t * label1 =  lv_label_create(scr, NULL);

    /*Modify the Label's text*/
    lv_label_set_text(label1, "Hello\nworld");

    /* Align the Label to the center
     * NULL means align on parent (which is the screen now)
     * 0, 0 at the end means an x, y offset after alignment*/
    lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);
}

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

    lv_tick_inc(LV_TICK_PERIOD_MS);
}

  • 编译运行
    ESP32 与 TFT 的引脚连接参考如下,编译成功的话,将代码烧录到 ESP32 中
ESP32TFT本文使用引脚
MOSISDA23
CLKSCL18
CSCS19
DCDC21
RSTRST22
  • 结果展示

在这里插入图片描述

花屏问题

  • 检查分辨率设置
    • 在 lvgl_helpers.h 中通过修改 LV_HOR_RES_MAX 和 LV_VER_RES_MAX 的值来设置显示器的像素宽度和高度可能是没有作用的,应该在 menuconfig -> LVGL configuration 里面设置
    • 同时要注意 horizontal 和 vertical 的值不要写颠倒了,可以尝试调换两个值的所处位置
  • 调整 SPI 频率
    • 可以在 menuconfig -> LVGL TFT Display controller 里面调整 SPI 频率,我设置的频率是10MHz

ST7735显示方向

目前使用的驱动,可以支持的 ST7735 显示方向只有 Portrait (竖屏)和 Landscape (横屏)。

要想将显示方向调整为 Portrait inverted 和 Landscape inverted 的话,需要修改 lvgl_esp32_drivers -> lvgl_tft 里的 st7735s.c 文件

找到文件里的 st7735s_set_orientation 函数,并将其替换成如下所示

static void st7735s_set_orientation(uint8_t orientation)
{
    const char *orientation_str[] = {
        "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED"
    };

    ESP_LOGD(TAG, "Display orientation: %s", orientation_str[orientation]);

    /*
        Portrait:  0xC8 = ST77XX_MADCTL_MX | ST77XX_MADCTL_MY | ST77XX_MADCTL_BGR
		Portrait inverted: 0x08
        Landscape: 0xA8 = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV | ST77XX_MADCTL_BGR
		Landscape inverted: 0x78
    */
    uint8_t data[] = {0xC8, 0x08, 0xA8, 0x78};

    ESP_LOGD(TAG, "0x36 command value: 0x%02X", data[orientation]);

    st7735s_send_cmd(ST7735_MADCTL);
    st7735s_send_data((void *) &data[orientation], 1);
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐