esp-idf(lvgl移植篇)
LVGL(轻量且多功能的图形库)是一个免费且开源的图形库,提供了创建嵌入式 GUI 所需的一切,包括易于使用的图形元素、美丽的视觉效果和低内存占用。本篇文章采用lvgl8.3.0版本,之前尝试lvgl9.2.2版本失败,因此改为8.3版本,如有大佬使用成功希望出一个详细教程。
·
LVGL(轻量且多功能的图形库)是一个免费且开源的图形库,提供了创建嵌入式 GUI 所需的一切,包括易于使用的图形元素、美丽的视觉效果和低内存占用。
本篇文章采用lvgl8.3.0版本,之前尝试lvgl9.2.2版本失败,因此改为8.3版本,如有大佬使用成功希望出一个详细教程
1.添加lvgl组件
idf.py add-dependency "lvgl/lvgl^8.3.0"
后面为版本号,可以自行修改
idf.py add-dependency "espressif/esp_lvgl_port^2.5.0"
此组件可帮助将 LVGL 与 Espressif 的 LCD 和触摸驱动程序配合使用。它可与任何带有 LCD 显示器的项目一起使用
可以在idf_component.yml文件里查看或者直接修改
2.初始化lvgl
具体用法可去 esp_lvgl_port组件官网查看
//初始化lvgl
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
esp_err_t err = lvgl_port_init(&lvgl_cfg);
3.添加设备
lcd屏幕
当然,在添加设备前要完成初始化lcd屏幕
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = lcd_panel_handle,
.buffer_size = DISP_WIDTH*DISP_HEIGHT,
.double_buffer = true,
.hres = DISP_WIDTH,
.vres = DISP_HEIGHT,
.monochrome = false,
.mipi_dsi = false,
.color_format = LV_COLOR_FORMAT_RGB565,
.rotation = {
.swap_xy = false,
.mirror_x = false,
.mirror_y = false,
},
.flags = {
.buff_dma = true,
.swap_bytes = false,
}
};
disp_handle = lvgl_port_add_disp(&disp_cfg);
触摸组件
同样先完成初始化
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp_handle,
.handle = tp,
};
lv_indev_t* touch_handle = lvgl_port_add_touch(&touch_cfg);
4.案例
这里采用立创实战派的lvgl例程代码,不过我使用的是idf的新版i2c,立创教程用的是旧版
//i2c总线句柄
i2c_master_bus_handle_t bus_handle;
//pca9557句柄
i2c_master_dev_handle_t pca9557_handle;
void bsp_i2c_init(void)
{
//初始化i2c总线
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = BSP_I2C_NUM,
.scl_io_num = BSP_I2C_SCL,
.sda_io_num = BSP_I2C_SDA,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &bus_handle));
//挂载pca9557设备
i2c_device_config_t dev_cfg = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7, //选择从设备的地址长度
.device_address = 0x19, //I2C设备原始地址。(7/10位地址,不含读写位)
.scl_speed_hz = 100000, //I2C SCL线路频率
};
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &pca9557_handle));
}
// 读取PCA9557寄存器的值
esp_err_t pca9557_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_transmit_receive(pca9557_handle, ®_addr, 1, data, len, 1000 / portTICK_PERIOD_MS);
}
// 给PCA9557的寄存器写值
esp_err_t pca9557_register_write_byte(uint8_t reg_addr, uint8_t data)
{
uint8_t write_buf[2] = {reg_addr, data};
return i2c_master_transmit(pca9557_handle, write_buf, sizeof(write_buf), 1000 / portTICK_PERIOD_MS);
}
void pca9557_init(void)
{
// 写入控制引脚默认值 DVP_PWDN=1 PA_EN = 0 LCD_CS = 1
pca9557_register_write_byte(PCA9557_OUTPUT_PORT, 0x05);
// 把PCA9557芯片的IO1 IO1 IO2设置为输出 其它引脚保持默认的输入
pca9557_register_write_byte(PCA9557_CONFIGURATION_PORT, 0xf8);
}
// 设置PCA9557芯片的某个IO引脚输出高低电平
esp_err_t pca9557_set_output_state(uint8_t gpio_bit, uint8_t level)
{
uint8_t data;
esp_err_t res = ESP_FAIL;
pca9557_register_read(PCA9557_OUTPUT_PORT, &data, 1);
res = pca9557_register_write_byte(PCA9557_OUTPUT_PORT, SET_BITS(data, gpio_bit, level));
return res;
}
// 控制 PCA9557_LCD_CS 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void lcd_cs(uint8_t level)
{
pca9557_set_output_state(LCD_CS_GPIO, level);
}
// 控制 PCA9557_PA_EN 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void pa_en(uint8_t level)
{
pca9557_set_output_state(PA_EN_GPIO, level);
}
// 控制 PCA9557_DVP_PWDN 引脚输出高低电平 参数0输出低电平 参数1输出高电平
void dvp_pwdn(uint8_t level)
{
pca9557_set_output_state(DVP_PWDN_GPIO, level);
}
//初始化LEDC 控制屏幕背光
esp_err_t bsp_display_brightness_init(void)
{
//准备并应用led PWM定时器配置
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, //速度模式(值必须为 LEDC_LOW_SPEED_MODE)
.duty_resolution = LEDC_TIMER_10_BIT, //PWM 占空比分辨率
.timer_num = 1, //定时器索引
.freq_hz = 5000, // PWM 信号频率(Hz)
.clk_cfg = LEDC_AUTO_CLK //时钟源
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// 准备并应用LEDC PWM通道配置
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE, //高速模式(仅在esp32上存在)或低速模式
.channel = LCD_LEDC_CH, //LEDC通道
.timer_sel = 1, //选择通道的定时器源
.intr_type = LEDC_INTR_DISABLE, //配置中断
.gpio_num = BSP_LCD_BACKLIGHT, //LEDC输出的GPIO
.duty = 0, // LEDC通道占空比
.hpoint = 0, //LEDC通道hpoint值
.flags.output_invert = 1 //启用(1)或禁用(0)gpio输出反相
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
return ESP_OK;
}
//设置屏幕背光亮度 参数为百分比
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100) {
brightness_percent = 100;
} else if (brightness_percent < 0) {
brightness_percent = 0;
}
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
//将百分比转换为10位PWM占空比值(LEDC分辨率设为10位,所以100%对应1023)
uint32_t duty_cycle = (1023 * brightness_percent) / 100;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
return ESP_OK;
}
//背光熄灭
esp_err_t bsp_display_backlight_off(void)
{
return bsp_display_brightness_set(0);
}
//背光最亮
esp_err_t bsp_display_backlight_on(void)
{
return bsp_display_brightness_set(100);
}
static esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
static esp_lcd_touch_handle_t tp; // 触摸屏句柄
static lv_disp_t *disp; // 指向液晶屏
static lv_indev_t *disp_indev = NULL; // 指向触摸屏
// 液晶屏初始化
esp_err_t bsp_display_new(void)
{
esp_err_t ret = ESP_OK;
// 背光初始化
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
// 初始化SPI总线
ESP_LOGD(TAG, "Initialize SPI bus");
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");
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
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 = 10,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, &io_handle), err, TAG, "New panel IO failed");
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = BSP_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_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(0); // 拉低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:
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;
}
// LCD显示初始化
esp_err_t bsp_lcd_init(void)
{
esp_err_t ret = ESP_OK;
ret = bsp_display_new(); // 液晶屏驱动初始化
lcd_set_color(0x0000); // 设置整屏背景黑色
ret = esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
ret = bsp_display_backlight_on(); // 打开背光显示
ESP_LOGI(TAG,"over");
return ret;
}
static lv_disp_t *bsp_display_lcd_init(void)
{
/* 初始化液晶屏 */
bsp_display_new(); // 液晶屏驱动初始化
lcd_set_color(0xffff); // 设置整屏背景白色
esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
/* 液晶屏添加LVGL接口 */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT, // LVGL缓存大小
.double_buffer = false, // 是否开启双缓存
.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);
}
// 触摸屏初始化
esp_err_t bsp_touch_new(esp_lcd_touch_handle_t *ret_touch)
{
/* Initialize touch */
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_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
tp_io_config.scl_speed_hz = BSP_I2C_FREQ_HZ; // 设置为总线的100kHz
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(bus_handle, &tp_io_config, &tp_io_handle), TAG, "");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, ret_touch));
return ESP_OK;
}
// 触摸屏初始化+添加LVGL接口
static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp)
{
/* 初始化触摸屏 */
ESP_ERROR_CHECK(bsp_touch_new(&tp));
assert(tp);
/* 添加LVGL接口 */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
return lvgl_port_add_touch(&touch_cfg);
}
// 开发板显示初始化
void bsp_lvgl_start(void)
{
/* 初始化LVGL */
lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);
/* 初始化液晶屏 并添加LVGL接口 */
disp = bsp_display_lcd_init();
/* 初始化触摸屏 并添加LVGL接口 */
disp_indev = bsp_display_indev_init(disp);
/* 打开液晶屏背光 */
bsp_display_backlight_on();
}
// 显示图片
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage)
{
// 分配内存 分配了需要的字节大小 且指定在外部SPIRAM中分配
size_t pixels_byte_size = (x_end - x_start)*(y_end - y_start) * 2;
uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixels_byte_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == pixels)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
return;
}
memcpy(pixels, gImage, pixels_byte_size); // 把图片数据拷贝到内存
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_end, y_end, (uint16_t *)pixels); // 显示整张图片数据
heap_caps_free(pixels); // 释放内存
}
// 设置液晶屏颜色
void lcd_set_color(uint16_t color)
{
// 分配内存 这里分配了液晶屏一行数据需要的大小
uint16_t *buffer = (uint16_t *)heap_caps_malloc(BSP_LCD_H_RES * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (NULL == buffer)
{
ESP_LOGE(TAG, "Memory for bitmap is not enough");
}
else
{
for (size_t i = 0; i < BSP_LCD_H_RES; i++) // 给缓存中放入颜色数据
{
buffer[i] = color;
}
for (int y = 0; y < 240; y++) // 显示整屏颜色
{
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 320, y+1, buffer);
}
free(buffer); // 释放内存
}
}
注意SDK设置这里要修改一下,并启动demo,才可以运行demo

void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
//bsp_lcd_init();
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
//lcd_draw_pictrue(0, 0, 320, 240, gImage_yingwu); // 显示3只鹦鹉图片
/* 下面5个demos 只打开1个运行 */
// lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
//xTaskCreate(lvgl_task, "lvgl", 4096, NULL, 5, NULL); // 优先级建议≥5
lv_demo_widgets();
}
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)