[正点原子]ESP32S3 RGB屏幕移植LVGL
ESP32S3 RGB屏幕移植lvgl
- 简介
- 准备工作
- 移植过程
- 创建文件加
- 修改配置
- 修改适配文件
- main函数
- lvgl的图形化配置
- 着重要注意的
- 屏幕驱动的问题
- 效果展示
简介
最近入手了 正点原子ESP32开发版准备学习LVGL,该板子支持RGB屏幕RGB565,之前买Linux开发板的时候有一块4,3寸的RGB屏幕,准备以此开始学习LVGL,参考了正点原子的移植方法,但是适配度不高,一个是屏幕大小不一样我的是480272 官方是800480 其次是触摸芯片读取不到数据
准备工作
首先是需要搭建ESP32-IDF的开发环境 这个已经很熟练了,本次是在树莓派4B上编译的,但是环境是官方脚本一键的,因此区别不大
- LVGL8.3源代码
- 正点原子开发例程 24_touch
移植过程
创建文件加
在components目录下将lvgl8.3的源码包放置在这里,解压,重命名为LVGL,然后在main目录下新建目录APP lvgl的驱动适配文件就在这里。
修改配置
在lvgl目录下 找到lv_conf_temple.h 重命名为lvgl_conf.h 在里面也把多余的内容去掉 if 0 改成if 1
在main/APP目录下新建文件lvgl_demo.c lvgl_demo.h
修改适配文件
在lvgl_demo.c中 拷贝移植文档中的内容
/******************************************************************************************************* @file lvgl_demo.c* @author 正点原子团队(ALIENTEK)* @version V1.0* @date 2023-12-01* @brief LVGL V8移植 实验* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 ESP32-S3 开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com******************************************************************************************************/#include "lvgl_demo.h"const esp_timer_create_args_t lvgl_tick_timer_args={.callback = &increase_lvgl_tick,.name = "lvgl_tick",
};
esp_timer_handle_t lvgl_tick_timer = NULL;/**
* @brief 初始化并注册显示设备
* @param 无
* @retval 无
*/void lv_port_disp_init(void)
{// void *buf1 = NULL;// void *buf2 = NULL;/* 初始化显示设备LCD */lcd_init(); /* RGB 屏初始化 */rgblcd_display_dir(1); /* 设置横屏 */lcd_show_string(10, 40, 240, 32, 32, "ESP32-S3", RED);char tmp[32];sprintf(tmp,"%ld-%ld",lcddev.width,lcddev.height);lcd_show_string(10, 80, 240, 32, 32, tmp, RED);/*-----------------------------* 创建一个绘图缓冲区*----------------------------*//*** LVGL 需要一个缓冲区用来绘制小部件* 随后,这个缓冲区的内容会通过显示设备的 'flush_cb'(显示设备刷新函数) 复制到显示设备上* 这个缓冲区的大小需要大于显示设备一行的大小** 这里有3种缓冲配置:* 1. 单缓冲区:* LVGL 会将显示设备的内容绘制到这里,并将他写入显示设备。** 2. 双缓冲区:* LVGL 会将显示设备的内容绘制到其中一个缓冲区,并将他写入显示设备。* 需要使用 DMA 将要显示在显示设备的内容写入缓冲区。* 当数据从第一个缓冲区发送时,它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。* 这样使得渲染和刷新可以并行执行。** 3. 全尺寸双缓冲区* 设置两个屏幕大小的全尺寸缓冲区,并且设置 disp_drv.full_refresh = 1。* 这样,LVGL将始终以 'flush_cb' 的形式提供整个渲染屏幕,您只需更改帧缓冲区的地址。*//* 创建一个绘图缓冲区 *///buf1 = heap_caps_malloc(ltdcdev.width * 10 * sizeof(lv_color_t), MALLOC_CAP_8BIT);/* 初始化显示缓冲区 */static lv_color_t buf_1[480*32]; static lv_disp_draw_buf_t disp_buf; /* 保存显示缓冲区信息的结构体 */lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, lcddev.width * 10); /* 初始化显示缓冲区 *///lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*//* 在LVGL中注册显示设备 */static lv_disp_drv_t disp_drv; /* 显示设备的描述符(HAL要注册的显示驱动程序、与显示交互并处理与图形相关的结构体、回调函数) */lv_disp_drv_init(&disp_drv); /* 初始化显示设备 *//* 设置显示设备的分辨率 * 这里为了适配正点原子的多款屏幕,采用了动态获取的方式,* 在实际项目中,通常所使用的屏幕大小是固定的,因此可以直接设置为屏幕的大小 */disp_drv.hor_res = lcddev.width;disp_drv.ver_res = lcddev.height;/* 用来将缓冲区的内容复制到显示设备 */disp_drv.flush_cb = lvgl_disp_flush_cb;/* 设置显示缓冲区 */disp_drv.draw_buf = &disp_buf;/* 注册显示设备 */lv_disp_drv_register(&disp_drv);
}volatile bool disp_flush_enabled = true;/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL*/
void disp_enable_update(void)
{disp_flush_enabled = true;
}/**
* @brief 将内部缓冲区的内容刷新到显示屏上的特定区域
* @note 可以使用 DMA 或者任何硬件在后台加速执行这个操作
* 但是,需要在刷新完成后调用函数 'lv_disp_flush_ready()'
* @param disp_drv : 显示设备
* @param area : 要刷新的区域,包含了填充矩形的对角坐标
* @param color_map : 颜色数组
* @retval 无
*/
static void lvgl_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area,
lv_color_t *color_map)
{esp_lcd_panel_handle_t panel_handle=(esp_lcd_panel_handle_t)drv->user_data;/* 特定区域打点 */lcd_color_fill(area->x1, area->y1, area->x2,area->y2,(uint16_t *) color_map);/* 重要!!! 通知图形库,已经刷新完毕了 */lv_disp_flush_ready(drv);
}
#if 1
/**
* @brief 初始化并注册输入设备
* @param 无正点原子 LVGL 开发教程
10
LVGL 开发指南
* @retval 无
*/
void lv_port_indev_init(void)
{/* 初始化触摸屏 *///tp_init();tp_dev.init();/* 初始化输入设备 */static lv_indev_drv_t indev_drv;lv_indev_drv_init(&indev_drv);/* 配置输入设备类型 */indev_drv.type = LV_INDEV_TYPE_POINTER;/* 设置输入设备读取回调函数 */indev_drv.read_cb = touchpad_read;/* 在 LVGL 中注册驱动程序,并保存创建的输入设备对象 */lv_indev_t *indev_touchpad;indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/**
* @brief 获取触摸屏设备的状态
* @param 无
* @retval 返回触摸屏设备是否被按下
*/
static bool touchpad_is_pressed(void)
{tp_dev.scan(0); /* 触摸按键扫描 */if (tp_dev.sta & TP_PRES_DOWN){return true;}return false;
}
/**
* @brief 在触摸屏被按下的时候读取 x、 y 坐标
* @param x : x 坐标的指针
* @param y : y 坐标的指针
* @retval 无正点原子 LVGL 开发教程
11
LVGL 开发指南
*/
static void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y)
{(*x) = tp_dev.x[0];(*y) = tp_dev.y[0];
}
/**
* @brief 图形库的触摸屏读取回调函数
* @param indev_drv : 触摸屏设备
* @param data : 输入设备数据结构体
* @retval 无
*/
void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{static lv_coord_t last_x = 0;static lv_coord_t last_y = 0;/* 保存按下的坐标和状态 */if(touchpad_is_pressed()){touchpad_get_xy(&last_x, &last_y); /* 在触摸屏被按下的时候读取 x、 y 坐标 */data->state = LV_INDEV_STATE_PR;}else{data->state = LV_INDEV_STATE_REL;}/* 设置最后按下的坐标 */data->point.x = last_x;data->point.y = last_y;
}
#endifvoid lvgl_ex_label(void)
{lv_obj_t *bj = lv_obj_create(lv_scr_act());lv_obj_set_size(bj,200,200);lv_obj_set_align(bj,LV_ALIGN_CENTER);lv_obj_set_style_bg_color(bj,lv_color_hex(0xFF0000),LV_STATE_DEFAULT);lv_obj_t *switch_1 = lv_switch_create(bj);lv_obj_set_size(switch_1,60,30);lv_obj_set_align(switch_1,LV_ALIGN_CENTER);}/**
* @brief lvgl_demo 入口函数
* @param 无
* @retval 无
*/
void lvgl_demo(void)
{lv_init(); /* 初始化 LVGL 图形库 */lv_port_disp_init(); /* lvgl 显示接口初始化,放在 lv_init()的后面 */lv_port_indev_init(); /* lvgl 输入接口初始化,放在 lv_init()的后面 *//* 为 LVGL 提供时基单元 */ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 1 * 1000));/* 官方 demo,需要在 SDK Configuration 中开启对应 Demo *///lvgl_ex_label();// lv_demo_benchmark();lv_demo_music();while (1){lv_timer_handler(); /* LVGL 计时器 */vTaskDelay(pdMS_TO_TICKS(10)); /* 延时 10 毫秒 */}
}
/**
* @brief 告诉 LVGL 运行时间
* @param arg : 传入参数(未用到)
* @retval 无
*/
static void increase_lvgl_tick(void *arg)
{ /* 告诉 LVGL 已经过了多少毫秒 */lv_tick_inc(1);
}
在lvgl_demo.h中 加入一下内容
/** @Author: Nsouther nsouther@163.com* @Date: 2025-05-28 11:13:01* @LastEditors: Nsouther nsouther@163.com* @LastEditTime: 2025-05-30 11:01:01* @FilePath: /esp-idf/Myproject/23_rgb/main/APP/lvgl_demo.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef __LVGL_DEMO_H
#define __LVGL_DEMO_H#include "esp_timer.h"
#include "lvgl.h"
#include "demos/lv_demos.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "lcd.h"
#include "rgblcd.h"
#include "gt9xxx.h"
#include "touch.h"/* 函数声明 */
void lvgl_demo(void);
void disp_enable_update(void);
/* lvgl_demo 入口函数 */
void lv_port_disp_init(void);
/* 初始化并注册显示设备 */
void lv_port_indev_init(void);
/* 初始化并注册输入设备 */
void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
/* 图形库的触摸屏读取回调函数 */
static void increase_lvgl_tick(void *arg);
/* 告诉 LVGL 运行时间 */
static void lvgl_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area,
lv_color_t *color_map); /* 将内部缓冲区的内容刷新到显示屏上的特定区域 */
#endif
我们在这里主要做了如下修改
加入了LCD屏幕的初始化 在LCD屏幕初始化中获取了屏幕的大小 宽度和高度
这个是主要的函数,调用了lvgl的画点函数
其次是时基函数 提供时间基准 初始化了一个定时器 1000us 1ms
main函数
main函数比较简单,主要就是初始化了一些外设,然后跳转到lvgl的初始化中去了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "led.h"
#include "lcd.h"
#include "rgblcd.h"#include "xl9555.h"
#include "lvgl_demo.h"
#include "myiic.h"
#include "lvgl.h"
//i2c_obj_t i2c0_master;void app_main(void)
{esp_err_t ret;ret = nvs_flash_init(); /* 初始化 NVS */if (ret==ESP_ERR_NVS_NO_FREE_PAGES || ret==ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}myiic_init(); /* IIC初始化 */xl9555_init(); /* XL9555初始化 */lvgl_demo(); /* 运行 LVGL 例程 */
}
在lvgl_demo()中初始化了lvgl的驱动 以及显示 触摸的驱动 最后运行一个音乐界面来演示效果
lvgl的图形化配置
在调用lvgl前 首先需要将其注册成组件 在esp_lvgl的CMakeLists.txt中增加下面一行,表示增加lvgl的组件
set(EXTRA_COMPONENT_DIRS ${EXTRA_COMPONENT_DIRS} components/LVGL)
执行idf.py menuconig ->Component Config->LVGL configuration->Demos->Music player demo
LVGL configuration->Font usage 使能12 14 16号字体 其余的暂时不用动
着重要注意的
大部分教程都是这样的 但是在移植过程中出现了绿屏条纹 以及触摸驱动提示接受到了空恢复的问题(i2c)
绿屏问题是驱动的程序有问题,使用23_rbg可以驱动 但是lvgl就不行了 24_touch 里增加了ESP-IDF的官方驱动接口,所以驱动效果好一点,因此使用24的例程,其次i2c接口总是提示收到空数据,实际上是I2C的地址不对,
屏幕驱动的问题
运行时总是报错 说不能被整除,实际上是这个地方给错了,源码给的是48020 实际上是48034 刚好被整除,因此这是一个需要修修改的点
经过上面的修改 可以显示图像 但是触摸不行,调试触摸驱动 提示读取不到数据,后来才知道是gtx触摸芯片的i2c地址错误,源码给的是0x14 但是实际上是0x5D