LVGL 移植
通用对接方法
LVGL源码
https://github.com/lvgl/lvgl/tree/release/v8.3
主要文件
// 其余文件根据自己需要
- examples
- env_support
- src
lv_conf.h
lvgl.h
接口对接
examples
中port
文件夹,其中有显示接口,触控接口
将需要的对接文件复制到根目录,并改文件中的 #if 0
为#if 1
显示对接
定义屏幕尺寸在 lv_conf.h
#define MY_DISP_HOR_RES 240
#define MY_DISP_VER_RES 240
双缓存刷新和单缓存刷新在lv_port_disp.c
第 87 行
对接画点函数在lv_port_disp.c
第 167 行
输入对接
例子
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "GC9A01.h"
#include "LVGL/lvgl.h"
#include "lv_port_disp.h"
#include "benchmark/lv_demo_benchmark.h"
#include "ui.h"
void lv_ex_label(void) {
lv_obj_t *label = lv_label_create(lv_scr_act());//创建一个标签
lv_label_set_recolor(label, true);//通过内联命令启用重新着色
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);//使用比对象大小更长的文本设置标签的行为
lv_obj_set_width(label, 240);
lv_label_set_text_fmt(label, "#ff0000 6666666666666666666666666666666666666666666666#");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 10);//对齐方式
}
void LCD(void * arg)
{
lv_init();
lv_port_disp_init();
//lv_demo_benchmark();
//lv_ex_label();
ui_init();
while (vTaskDelay(1),true)
{
lv_task_handler();
GC9A01_Update();
}
}
void lv_tick_task() {
lv_tick_inc(10);
}
void app_main(void)
{
const esp_timer_create_args_t periodic_timer_args = {
.callback = &lv_tick_task,
.name = "periodic_gui"
};
esp_timer_handle_t periodic_timer;
esp_timer_create(&periodic_timer_args, &periodic_timer);
esp_timer_start_periodic(periodic_timer, 10000);
xTaskCreate(LCD, "Test LCD", 24 * 1024, NULL, tskIDLE_PRIORITY, NULL);
ESP Panel 对接
- 拉取
LVGL
idf_component.yml
dependencies: espressif/esp_lvgl_port: version: '^2.5.0' espressif/esp_lcd_touch_ft5x06: version: '^1.0.6' idf: version: '>=4.4.2' lvgl/lvgl: version: '^8.4.0'
- 完整案例
#include <driver/ledc.h> #include <esp_check.h> #include <driver/spi_master.h> #include <driver/i2c.h> #include "lv_task.h" #include "esp_lcd_types.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_ops.h" #include "pca9557.h" #include "lvpage_time.h" #include "nvs_exa.h" static char *TAG = "lv_task"; /** * [25-05-18]: LCD 背光配置 **/ void lcd_backlight_init() { const ledc_channel_config_t LCD_backlight_channel = { .gpio_num = LCD_BACKLIGHT_PIN, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_0, .intr_type = LEDC_INTR_DISABLE, .timer_sel = 0, .duty = 0, .hpoint = 0, .flags.output_invert = true }; const ledc_timer_config_t LCD_backlight_timer = { .speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = LEDC_TIMER_10_BIT, .timer_num = 0, .freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK }; ESP_ERROR_CHECK(ledc_timer_config(&LCD_backlight_timer)); ESP_ERROR_CHECK(ledc_channel_config(&LCD_backlight_channel)); } void lcd_backlight_brightness_set(uint8_t brightness_percent) { if (brightness_percent > 100) { brightness_percent = 100; } if (brightness_percent == 0) { brightness_percent = 1; } uint32_t duty_cycle = (1023 * brightness_percent) / 100; ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty_cycle)); ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0)); } /** * [25-05-18]: i2c 对接 io拓展,cs引脚 **/ int8_t i2c_send_data(uint8_t reg, uint8_t data, uint8_t len) { uint8_t write_buf[2] = {reg, data}; i2c_master_write_to_device(BSP_I2C_NUM, PCA9557_SENSOR_ADDR, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS); return 0; } int8_t i2c_recv_data(uint8_t reg, uint8_t data, uint8_t len) { i2c_master_write_read_device(BSP_I2C_NUM, PCA9557_SENSOR_ADDR, ®, 1, &data, len, 1000 / portTICK_PERIOD_MS); return 0; } void i2c_init_cfg() { i2c_config_t i2c_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = BSP_I2C_SDA, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_io_num = BSP_I2C_SCL, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = BSP_I2C_FREQ_HZ }; i2c_param_config(BSP_I2C_NUM, &i2c_conf); i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0); } void lcd_cs_init() { I2C_MASTER i2CMaster = { .i2c_send = i2c_send_data, .i2c_recv = i2c_recv_data, .i2c_init = i2c_init_cfg, }; pca9557_init(&i2CMaster); pca9557_set_output_state(&i2CMaster, LCD_CS_GPIO, 0); } /** * [25-05-18]: lcd 初始化 * 以下两个句柄一个用于控制 LCD 面板参数,另一个用于控制引脚,对接到 LVGL **/ static esp_lcd_panel_handle_t panel_handle = NULL; static esp_lcd_panel_io_handle_t io_handle = NULL; esp_err_t lcd_display_init(void) { esp_err_t ret = ESP_OK; // 1. 背光初始化 lcd_backlight_init(); lcd_backlight_brightness_set(settings.brightness); // 2. 初始化SPI总线,分为以下三个步骤 // 2.1 初始化 spi 的数据引脚,也就是 SPI_HOST const spi_bus_config_t buscfg = { .sclk_io_num = BSP_LCD_SPI_CLK, .mosi_io_num = BSP_LCD_SPI_MOSI, .miso_io_num = GPIO_NUM_NC, .quadwp_io_num = GPIO_NUM_NC, .quadhd_io_num = GPIO_NUM_NC, .max_transfer_sz = BSP_LCD_H_RES * BSP_LCD_V_RES * sizeof(uint16_t), }; ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed"); // 2.2 初始化 spi 的 功能配置,也配置了 CS 等引脚,这里用了 lcd_panel 的接口厨初始化它,然后留下了一个句柄给面板控制。 const esp_lcd_panel_io_spi_config_t io_config = { .dc_gpio_num = BSP_LCD_DC, .cs_gpio_num = BSP_LCD_SPI_CS, .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, .lcd_cmd_bits = LCD_CMD_BITS, .lcd_param_bits = LCD_PARAM_BITS, .spi_mode = 2, .trans_queue_depth = 2, }; esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) BSP_LCD_SPI_NUM, &io_config, &io_handle); // 2.3 初始化 lcd 面板,配置了 RST 引脚,留下了一个句柄供用户控制 const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = BSP_LCD_RST, .color_space = ESP_LCD_COLOR_SPACE_RGB, .bits_per_pixel = BSP_LCD_BITS_PER_PIXEL, }; ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle), err, TAG, "New panel failed"); esp_lcd_panel_reset(panel_handle); // 液晶屏复位 lcd_cs_init(); // 拉低CS引脚 esp_lcd_panel_init(panel_handle); // 初始化配置寄存器 esp_lcd_panel_invert_color(panel_handle, true); // 颜色反转 esp_lcd_panel_swap_xy(panel_handle, true); // 显示翻转 esp_lcd_panel_mirror(panel_handle, true, false); // 镜像 return ret; err: ESP_LOGI(TAG, "lcd_display_init failed\r\n"); if (panel_handle) { esp_lcd_panel_del(panel_handle); } if (io_handle) { esp_lcd_panel_io_del(io_handle); } spi_bus_free(BSP_LCD_SPI_NUM); return ret; } void lcd_set_color(uint16_t color) { uint16_t *buffer = (uint16_t *) heap_caps_malloc( BSP_LCD_H_RES * BSP_LCD_V_RES * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT // 如果需要 8 位对齐 ); if (NULL == buffer) { ESP_LOGE(TAG, "Memory for bitmap is not enough"); } else { for (size_t i = 0; i < BSP_LCD_H_RES * BSP_LCD_V_RES; i++) { buffer[i] = color; } esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 320, 240, buffer); free(buffer); // 释放内存 } } #include "esp_lvgl_port_disp.h" #include "esp_lvgl_port.h" /** * [25-05-18]: LVGL_DISP 对接 DISP **/ lv_disp_t *lv_add_disp(void) { // 1. 初始化 LCD lcd_display_init(); // 液晶屏驱动初始化 lcd_set_color(0xffff); // 设置整屏背景白色 esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示 // 2. 对接 LVGL 的显示 const lvgl_port_display_cfg_t disp_cfg = { .io_handle = io_handle, .panel_handle = panel_handle, .buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES, // LVGL缓存大小 .double_buffer = true, // 是否开启双缓存 .hres = BSP_LCD_H_RES, // 液晶屏的宽 .vres = BSP_LCD_V_RES, // 液晶屏的高 .monochrome = false, // 是否单色显示器 /* Rotation的值必须和液晶屏初始化里面设置的 翻转 和 镜像 一样 */ .rotation = { .swap_xy = true, // 是否翻转 .mirror_x = true, // x方向是否镜像 .mirror_y = false, // y方向是否镜像 }, .flags = { .buff_dma = false, // 是否使用DMA 注意:dma与spiram不能同时为true .buff_spiram = true, // 是否使用PSRAM 注意:dma与spiram不能同时为true } }; return lvgl_port_add_disp(&disp_cfg); } /** * [25-05-18]: LVGL TOUCH 添加到 LVGL DISP **/ #include "esp_lcd_touch.h" #include "esp_lcd_touch_ft5x06.h" #include "lv_structure.h" static lv_indev_t *lv_add_touch(lv_disp_t *disp) { esp_lcd_touch_config_t tp_cfg = { .x_max = BSP_LCD_V_RES, .y_max = BSP_LCD_H_RES, .rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset .int_gpio_num = GPIO_NUM_NC, .levels = { .reset = 0, .interrupt = 0, }, .flags = { .swap_xy = 1, .mirror_x = 1, .mirror_y = 0, }, }; esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG(); esp_lcd_panel_io_handle_t tp_io_handle = NULL; esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t) BSP_I2C_NUM, &tp_io_config, &tp_io_handle); esp_lcd_touch_handle_t touchHandle = NULL; esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &touchHandle); const lvgl_port_touch_cfg_t touch_cfg = { .disp = disp, .handle = touchHandle, }; return lvgl_port_add_touch(&touch_cfg); } void lvgl_start(void) { lvgl_port_cfg_t lvgl_cfg = { .task_priority = 20, .task_stack = 1024 * 10, .task_affinity = 1, .task_max_sleep_ms = 500, .timer_period_ms = 5, }; lvgl_port_init(&lvgl_cfg); /* 初始化液晶屏 并添加LVGL接口 */ lv_disp_t *disp = lv_add_disp(); /* 初始化触摸屏 并添加LVGL接口 */ lv_indev_t *touch = lv_add_touch(disp); /* 初始化 LVGL 页面框架 */ lv_structure_init(); }
License:
CC BY 4.0