avatar

松果工作室

欢迎光临

  • 首页
  • freeRTOS
  • LVGL
  • ESP
  • 开发手册
  • 快速笔记
  • 个人收藏
  • 时事记录
  • 考察日志
  • 工具
Home LVGL 移植
文章

LVGL 移植

Posted 2024-04-3 Updated 2025-06- 14
By YCP
49~63 min read

通用对接方法

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, &reg, 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();
    }
    
坑和笔记
ESP LVGL
License:  CC BY 4.0
Share

Further Reading

Dec 23, 2024

其他笔记

EC800K AT连接移远云 配置过程 # 配置产品信息(初次连接需配置) AT+QIOTCFG="productinfo","pxxxxt","cDVTxxxxxxxxWGVB" # 连接开发者中心 AT+QIOTREG=1 # 查询当前连接状态(+QIOTSTATE: 8为正常) AT+QI

Jun 21, 2024

环形滤波算法

#include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 10 // 缓冲区大小 #define THRESHOLD 180

Jun 17, 2024

STM32 ADC采集的三种方式

采样周期 单个采集模式 ADC_Settings: 程序使用 uint16_t ADC_Read(

OLDER

算丰学院

NEWER

LVGL(一) 创建对象

Recently Updated

  • ESP32(十) BLE OTA
  • ESP32(九) BLE GATTS
  • LVGL(四) 动画
  • LVGL(三) 对象中创建对象
  • LVGL(二) 定时回调

Trending Tags

LVGL WCH Linux Elec freeRTOS STM ESP Flutter Others SwiftUI

Contents

©2025 松果工作室. Some rights reserved.

Using the Halo theme Chirpy