陈拓 2022/11/27-2022/12/10
1. 概述
在《ESP32 ESP-IDF TFT-LCD(ST7735 128x160) LVGL演示》
和《ESP32 ESP-IDF LVGL ST7789 演示lv_demo_music》
两篇文章中,我们演示了LVGL官方ESP32移植项目https://github.com/lvgl/lv_port_esp32
该项目使用使用的LVGL 7.9版本。
本文在此基础上移植LVGL8.3.3。
官方文档:http://docs.lvgl.io/8.3/
下面的例子中任然使用前面两篇文章中使用的ST7735和ST7789显示屏。
2. 构建ESP32 ESP-IDF开发环境
首先构建ESP32 ESP-IDF开发环境,可用参考《Ubuntu构建ESP32 ESP-IDF开发环境(简约版)》
https://blog.csdn.net/chentuo2000/article/details/128034585?spm=1001.2014.3001.5502
3. LVGL官方文档查看
https://docs.lvgl.io/master/get-started/platforms/espressif.html
LVGL可作为标准ESP-IDF组件使用和配置。
- ESP32的LVGL演示项目
我们创建了lv_port_esp32(https://github.com/lvgl/lv_port_esp32),这是一个使用ESP-IDF和LVGL来展示demos (https://github.com/lvgl/lvgl/tree/master/demos)中的一个演示的项目。您可以将项目配置为许多受支持的显示控制器和目标芯片之一。
请参阅lvgl_esp32_drivers(https://github.com/lvgl/lvgl_esp32_drivers)存储库,以获取支持的显示器和独立触摸控制器和目标的完整列表。
- 在ESP-IDF项目中使用LVGL
ESP-IDF v4.1及以上版本。
查看我的ESP-IDF版本:
get_idf
idf.py --version
- 获取LVGL
只需将LVGL克隆到project_root/components目录中,它就会自动集成到项目中。
- 配置
在项目根目录中使用idf.py menuconfig命令配置LVGL。
- 在ESP-IDF项目中使用lvgl_esp32_drivers。
您还可以将lvgl_esp32_drivers添加为“组件”。该组件应位于项目根目录中名为“components”的目录中,和lvgl目录并列。
4. 创建工程项目
- 复制项目模板
mkdir ~/esp442
cd ~/esp442/
cp -r ~/esp442/esp-idf/examples/get-started/sample_project/ ./esp32_lvgl833
- 获取LVGL
cd esp32_lvgl833
mkdir components
cd components
- 下载LVGL最新版本
https://github.com/lvgl/lvgl/tags
git clone -b v8.3.3 https://github.com/lvgl/lvgl.git
- 获取lvgl_esp32_drivers
git clone https://github.com/lvgl/lvgl_esp32_drivers.git
- 项目目录结构
其中:Makefile和component.mk可用删除。
5. 编译
- 刷新esp-idf环境
get_idf
- 设定目标芯片
idf.py set-target esp32
删除build目录:
rm -r build/
idf.py set-target esp32
- 配置项目
idf.py menuconfig
1) 将闪存设置为4MB
2) 不选择开发板
3) 选择显示屏控制芯片
或者
4) SPI总线选择
默认是SPI2_HOST。
ESP32有4组SPI外设。
SPI0和SPI1在内部用于访问ESP32所连接的闪存。
SPI2和SPI3是通用SPI控制器,分别称为HSPI和VSPI(LVGL 默认是HSPI)。它们向用户开放。
SPI2和SPI3的默认引脚:
这些引脚是可以重新映射的,所以下面我们的接线和默认并不相同。
在我们的实验中没有使用MISO,所以下面的接线表中没有MISO。
5) 定义引脚
接线表:
ST7735或ST7789 ESP32
GND GND
VCC 3V3
SCL IO14(CLK)
SDA IO27(MOSI)
RES IO16
DC IO17(DC)
CS IO25
BLK IO26
背光控制:
6) 屏幕方向
ST7735横屏
ST7789横屏
7) 设置显示字符大小
对于ST7735因为屏幕小,选用更小的字号12,默认是14。
对于ST7789使用默认14号字:
8) 交换16位颜色的2个字节
ST7735和ST7789的颜色深度是16位,选择Color depth. (16:RGB565),颜色值用2字节表示,ESP32是小端Little Endian模式,先发送低位字节,如果颜色失真,可以选交换颜色值的高低字节。
哪些显示屏需要交换颜色字节,在lvgl_esp32_drivers/README.md中有说明:
9) 不使用定制的lv_conf.h,这是默认
保存,退出。
- 编译项目
idf.py build
编译警告:
如果开发板(例如M5StickC)上有AXP192电池管理单元,需要在idf.py menuconfig配置中启用:
我们的板子上没有AXP192,所以忽略这些警告。或者删除
~/esp442/esp32_lvgl833/components/lvgl_esp32_drivers/lvgl_tft/st7735s.c
中相关代码。
编译错误:
在/home/ct/esp442/esp32_lvgl833/components/lvgl_esp32_drivers/lvgl_helpers.h中添加宏定义:
对于ST7735
/*********************
* DEFINES
*********************/
#define LV_HOR_RES_MAX 160
#define LV_VER_RES_MAX 128
#define SPI_HOST_MAX 3
再修改st7735s.h以适合我们的显示屏
~/esp/lv_port_esp32/components/lvgl_esp32_drivers/lvgl_tft/st7735s.h
#define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 / 1 offset)
#define COLSTART 0 //26
#define ROWSTART 0 //1
原来的参数是针对160x80写的,要将COLSTART和ROWSTART都改为0。
对于ST7789只需要修改:
#define LV_HOR_RES_MAX 320
#define LV_VER_RES_MAX 240
#define SPI_HOST_MAX 3
其他地方都不用动。
再编译:
idf.py build
编译通过,这样移植就完成了。
6. LVGL演示
为了看LVGL的效果,我们以
https://github.com/lvgl/lv_port_esp32/tree/master/main/main.c
为例进行演示,其中的代码是针对LVGL 7.9版本写的,我们需要做一些修改。
6.1 基本演示
- 修改main.c
现在的main.c是空的:
#include <stdio.h>
void app_main(void)
{
}
用下面的代码替换:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "lvgl.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "lvgl_helpers.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(void)
{
// xTaskCreate(guiTask, "gui", 4096*2, NULL, 1, NULL);
// xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 1, NULL, 1);
/* 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_draw_buf_t disp_buf;
uint32_t size_in_px = DISP_BUF_SIZE;
/* Initialize the working buffer depending on the selected display. */
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, size_in_px);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
disp_drv.flush_cb = disp_driver_flush;
disp_drv.draw_buf = &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));
lv_task_handler();
/* Try to take the semaphore, call lvgl related function on success */
if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
lv_task_handler();
xSemaphoreGive(xGuiSemaphore);
}
}
}
static void create_demo_application(void)
{
/* 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);
/*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, LV_ALIGN_CENTER, 0, 0);
}
static void lv_tick_task(void *arg) {
(void) arg;
lv_tick_inc(LV_TICK_PERIOD_MS);
}
- 编译
idf.py build
- 烧写项目
查看USB转串口设备:
ls -l /dev/ttyUSB*
修改权限:
sudo chmod 777 /dev/ttyUSB0
烧写:
idf.py -p /dev/ttyUSB0 -b 460800 flash
- 启用监视器
idf.py monitor -p /dev/ttyUSB0
当示例正常运行时,将观察到以下输出:
- ST7735屏显示
- ST7789屏显示
6.2 examples演示lv_example_get_started_1.c
- 选择一个例子lv_example_get_started_1.c进行测试
- 配置项目
idf.py menuconfig
- 修改main.c
添加头文件:
#include "examples/lv_examples.h"
修改create_demo_application函数
static void create_demo_application(void)
{
lv_example_get_started_1();
}
- 修改main目录下的 CMakelists.txt
idf_component_register(SRCS "main.c"
"../components/lvgl/examples/get_started/lv_example_get_started_1.c"
INCLUDE_DIRS ".")
- 编译、烧写
idf.py build
idf.py -p /dev/ttyUSB0 -b 460800 flash
- ST7735显示
按钮的颜色是反转的(蓝光与黄光互为补色),字符是正常的,因为字符颜色是白色,高低字节相同,高低字节交换不受影响。
我的解决方法见《ESP32 ESP-IDF LVGL ST7735颜色修正》
- ST7789显示
ST7789显示的颜色是正常的。
有一些型号的屏需要在设置中选中Invert colors in display反转颜色,要根据所使用的屏进行测试。
6.3 examples演示lv_example_btnmatrix_2.c
- 选择另一个例子lv_example_btnmatrix_2.c进行测试
~/esp442/esp32_lvgl833/components/lvgl/examples/widgets/btnmatrix/lv_example_btnmatrix_2.c
- 修改main.c
添加头文件:
#include "examples/lv_examples.h"
修改create_demo_application函数
static void create_demo_application(void)
{
lv_example_btnmatrix_2();
}
- 修改main目录下的 CMakelists.txt
idf_component_register(SRCS "main.c"
"../components/lvgl/examples/get_started/lv_example_get_started_1.c"
"../components/lvgl/examples/widgets/btnmatrix/lv_example_btnmatrix_2.c"
"../components/lvgl/examples/assets/img_star.c"
INCLUDE_DIRS ".")
- 编译、烧写
idf.py build
idf.py -p /dev/ttyUSB0 -b 460800 flash
- 经过颜色校正的ST7735S显示
对比下面ST7789的显示,小部件(widget)的颜色都对了,五角星图像的颜色还是反转的。如果有需求可以参照《ESP32 ESP-IDF LVGL ST7735颜色修正》一文的方法对图像进行颜色修正。
- ST7789显示
6.4 demos 演示
- 配置项目
idf.py menuconfig
选择Show some widget和Enable slide show
- 修改main.c
添加头文件:
#include "demos/lv_demos.h"
- 修改create_demo_application函数
static void create_demo_application(void)
{
#if defined CONFIG_LV_USE_DEMO_WIDGETS
lv_demo_widgets();
#elif defined CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER
lv_demo_keypad_encoder();
#elif defined CONFIG_LV_USE_DEMO_BENCHMARK
lv_demo_benchmark();
#elif defined CONFIG_LV_USE_DEMO_STRESS
lv_demo_stress();
#else
#error "No demo application selected."
#endif
}
- 修改main目录下的 CMakelists.txt
idf_component_register(SRCS "main.c"
"../components/lvgl/demos/widgets/lv_demo_widgets.c"
"../components/lvgl/demos/widgets/assets/img_lvgl_logo.c"
"../components/lvgl/demos/widgets/assets/img_demo_widgets_avatar.c"
"../components/lvgl/demos/widgets/assets/img_clothes.c"
INCLUDE_DIRS ".")
- 编译
idf.py build
内存不足,无法运行lv_demo_widget。请将LV_MEM_SIZE设置为至少38KB(38ul*1024ul)。建议使用48KB。
再编译,成功。
- 烧写
idf.py -p /dev/ttyUSB0 -b 460800 flash
- 经过颜色校正的ST7735S显示
- ST7789显示
6.5 其他的官方示例的演示类似。
参考文档
- ESP32-IDF Cmake编写要点
https://blog.csdn.net/qq_43940227/article/details/120893146 - 如何在ESP32-C3上添加自己的工程
https://www.pudn.com/news/6228e06b9ddf223e1ad2e675.html - ESP32,Linux下使用ESP-IDF添加自己的组件库(components)
https://blog.csdn.net/weixin_41683042/article/details/120698215