基于ST7789TFT移植LVGL

原创 嵌入式Lee 2024-05-26 08:00

一. 准备代码

https://github.com/lvgl/lvgl.git

下载指定版本,版本9和版本8变化有点大,我这里以用的比较多的版本8为例。

git clone --branch v8.4.0 https://github.com/lvgl/lvgl.git

删除不必要的文件,只保留src,examples/portingdemos文件夹,以及2h文件lv_conf_template.hlvgl.h

|-- demos|   |-- README.md|   |-- benchmark|   |-- keypad_encoder|   |-- lv_demos.h|   |-- lv_demos.mk|   |-- music|   |-- stress|   `-- widgets|-- examples|   `-- porting|-- lv_conf_template.h|-- lvgl.h`-- src    |-- core    |-- draw    |-- extra    |-- font    |-- hal    |-- lv_api_map.h    |-- lv_conf_internal.h    |-- lv_conf_kconfig.h    |-- lvgl.h    |-- misc`-- widgets

Src下是源码,按需添加无需修改

demos下是demo程序

examples/porting下有显示,输入,文件等移植模板

lv_conf_template.h是配置文件模板

lvgl.h是总的头文件

二. 添加源码到自己的工程

添加源码

lvgl/src/core/*.clvgl/src/draw/*.clvgl/src/draw/sw/*.clvgl/src/extra/*.clvgl/src/extra/layouts/flex/*.clvgl/src/extra/layouts/grid/*.clvgl/src/extra/libs/bmp/*.clvgl/src/extra/libs/ffmpeg/*.clvgl/src/extra/libs/freetype/*.clvgl/src/extra/libs/gif/*.clvgl/src/extra/libs/png/*.clvgl/src/extra/libs/qrcode/*.clvgl/src/extra/libs/rlottie/*.clvgl/src/extra/libs/sjpg/*.clvgl/src/extra/libs/tiny_ttf/*.clvgl/src/extra/others/fragment/*.clvgl/src/extra/others/grianav/*.clvgl/src/extra/others/ime/*.clvgl/src/extra/others/imgfont/*.clvgl/src/extra/others/monkey/*.clvgl/src/extra/others/msg/*.clvgl/src/extra/others/snapshot/*.clvgl/src/extra/themes/basic/*.clvgl/src/extra/themes/default/*.clvgl/src/extra/themes/mono/*.clvgl/src/extra/widgets/animimg/*.clvgl/src/extra/widgets/calendar/*.clvgl/src/extra/widgets/chart/*.clvgl/src/extra/widgets/colorwheel/*.clvgl/src/extra/widgets/imgbtn/*.clvgl/src/extra/widgets/keyboard/*.clvgl/src/extra/widgets/led/*.clvgl/src/extra/widgets/list/*.clvgl/src/extra/widgets/menu/*.clvgl/src/extra/widgets/meter/*.clvgl/src/extra/widgets/msgbox/*.clvgl/src/extra/widgets/span/*.clvgl/src/extra/widgets/spinbox/*.clvgl/src/extra/widgets/spinner/*.clvgl/src/extra/widgets/tabview/*.clvgl/src/extra/widgets/tileview/*.clvgl/src/extra/widgets/win/*.clvgl/src/font/*.clvgl/src/hal/*.clvgl/src/misc/*.clvgl/src/widgets/*.c

准备配置文件

复制lv_conf_template.hlv_conf.h放置在lvgl文件夹外和lvgl并列。

#if 0 /*Set it to "1" to enable content*/

改为

#if 1 /*Set it to "1" to enable content*/

准备显示适配文件

复制examples/porting下的lv_port_disp_template.c/h到工程目录,修改为

lv_port_disp.c/h

添加源码lv_port_disp.c到工程,有必要的话需要添加头文件lv_port_disp.h包含路径。

头文件包含路径

lvgl目录添加到头文件包含路径

完成以上工作后,基本完成了框架,但是还没有适配接口,编译还有问题,详细的适配见后面。

三. 适配

3.1 显示适配

前面已经准备好了lv_port_disp.clv_port_disp.h文件。

现在来实现接口,显示适配很简单,只需要实现自己的写点函数或者显示区域函数即可,最开始可以先适配为写点,后面再优化为写区域。

1.使能条件编译

lv_port_disp.c#if 0改为#if 1

lv_port_disp.h#if 0改为#if 1

2.头文件改名

#include "lv_port_disp_template.h"

改为

#include "lv_port_disp.h"

3.设置分辨率

#include

添加

#define MY_DISP_HOR_RES 240

#define MY_DISP_VER_RES 320

4.显示初始化

lv_port_disp_init中保留

Example for 1) 后的代码,注释掉

Example for 2)  Example for 3)后的代码,即使用一个行缓存。

 /* Example for 1) */    static lv_disp_draw_buf_t draw_buf_dsc_1;    static lv_color_t buf_1[MY_DISP_HOR_RES * 10];                          /*A buffer for 10 rows*/    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/
    /* Example for 2) */    //static lv_disp_draw_buf_t draw_buf_dsc_2;    //static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10];                        /*A buffer for 10 rows*/    //static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10];                        /*An other buffer for 10 rows*/    //lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/
    /* Example for 3) also set disp_drv.full_refresh = 1 below*/    //static lv_disp_draw_buf_t draw_buf_dsc_3;    //static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*A screen sized buffer*/    //static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*Another screen sized buffer*/    //lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,    //                      MY_DISP_VER_RES * LV_VER_RES_MAX);   /*Initialize the display buffer*/

实现初始化

static void disp_init(void){    st7789_itf_init();    /*You code here*/}

5.显示

disp_flush

/*put_px(x, y, *color_p)*/

后添加我们的写点函数

st7789_itf_set_pixel(x, y, *((uint16_t*)color_p));当然前面要包含头文件

#include "st7789_itf.h"

6.颜色格式设置

根据实际配置,我这里是RGB565,所以是16位。

lv_conf.h

#define LV_COLOR_DEPTH 16

这里还可以使用宏LV_COLOR_16_SWAP配置是否交换高低字节(即大小端交换),这适用于适配8位接口传输16位数据时的大小端对应。

LV_COLOR_16_SWAP配置为0不交换,配置为1交换。

7.调用初始化

注意在lv_init后调用lv_port_disp_init

#include "lv_port_disp.h"
lv_init();lv_port_disp_init();

4.1 适配时间戳

lvgl有两种方式获取时间戳,一种是

lv_conf.h中配置宏LV_TICK_CUSTOM0(默认)

然后周期调用lv_tick_inc,比如1ms调用一次即lv_tick_inc(1).

LV_TICK_CUSTOM配置为1

实现宏LV_TICK_CUSTOM_INCLUDE LV_TICK_CUSTOM_SYS_TIME_EXPR

分别是用户提供的实现时间戳获取的头文件和函数。

我这里使用前者,固定周期调用lv_tick_inc

4.2 适配日志打印于assert

lv_conf.h,设置

#define LV_USE_LOG 1 使能日志

设置日志等级,调试时可以设置LV_LOG_LEVEL_TRACE看详细过程,一般设置WARN即可。

#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN

设置打印接口,

#define LV_LOG_PRINTF 1,则使用printf,需要有stdio.h

#define LV_LOG_PRINTF 0,则需要调用lv_log_register_print_cb初始化打印,

比如

static void my_print(const char * buf)

{

wq_printf("%s\r\n",buf);

}

lv_log_register_print_cb(my_print);

lv_init();

lv_conf.h

LV_USE_ASSERT开头的宏设置为1,使能对应的assert

4.5 输入适配

下次再分享

4.6 文件系统适配

下次再分享

.简单测试

参考

examples/get_started/lv_example_get_started_1.c

代码如下:

#include "lvgl.h"#include "lv_port_disp.h"static void tick_callback(void){    lv_tick_inc(1);}
static void my_print(const char * buf){    wq_printf("%s\r\n",buf);}

static void btn_event_cb(lv_event_t * e){    lv_event_code_t code = lv_event_get_code(e);    lv_obj_t * btn = lv_event_get_target(e);    if(code == LV_EVENT_CLICKED) {        static uint8_t cnt = 0;        cnt++;
        /*Get the first child of the button which is the label and change its text*/        lv_obj_t * label = lv_obj_get_child(btn, 0);        lv_label_set_text_fmt(label, "Button: %d", cnt);    }}
/** * Create a button with a label and react on click event. */static void lv_example_get_started_1(void){    lv_obj_t * btn = lv_btn_create(lv_scr_act());     /*Add a button the current screen*/    lv_obj_set_pos(btn, 10, 10);                            /*Set its position*/    lv_obj_set_size(btn, 120, 50);                          /*Set its size*/    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);           /*Assign a callback to the button*/
    lv_obj_t * label = lv_label_create(btn);          /*Add a label to the button*/    lv_label_set_text(label, "Button");                     /*Set the labels text*/    lv_obj_center(label);}
void main(void){
    while(1)    {       
        lv_log_register_print_cb(my_print);        lv_init();        lv_port_disp_init();        os_hook_tick_register_callback(tick_callback);    lv_example_get_started_1();        while (1) {            uint32_t delay = lv_timer_handler();            if (delay < 1) delay = 1;            os_delay(delay);            st7789_itf_sync();        }        lv_deinit();    }    return;}

测试效果如下

DEMO测试

添加如下代码到自己的工程

lvgl/demos/benchmark/*.clvgl/demos/benchmark/assets/*.clvgl/demos/keypad_encoder/*.clvgl/demos/music/*.clvgl/demos/music/assets/*.clvgl/demos/stress/*.clvgl/demos/widgets/*.clvgl/demos/widgets/assets/*.c

lv_conf.h中将带DEMO的宏配置为1,可以挑选部分。

这里以MUSICDEMO为例,如果空间不够可以设置LV_DEMO_MUSIC_LARGE0

/*Music player demo*/#define LV_USE_DEMO_MUSIC 1#if LV_USE_DEMO_MUSIC    #define LV_DEMO_MUSIC_SQUARE    1    #define LV_DEMO_MUSIC_LANDSCAPE 1    #define LV_DEMO_MUSIC_ROUND     1    #define LV_DEMO_MUSIC_LARGE     0    #define LV_DEMO_MUSIC_AUTO_PLAY 1#endif

demo需要用到一些字体,lv_conf.h设置LV_FONT_MONTSERRAT_12LV_FONT_MONTSERRAT_22LV_FONT_MONTSERRAT_32,,LV_FONT_MONTSERRAT_16的宏为1

使能以下宏计算帧率

#define LV_USE_PERF_MONITOR 1 

代码如下

#include "lvgl.h"#include "demos/lv_demos.h"#include "lv_port_disp.h"#include "st7789_itf.h"
static void tick_callback(void){    lv_tick_inc(1);}
static void my_print(const char * buf){    wq_printf("%s\r\n",buf);}

void main(void){    while(1)    {        lv_log_register_print_cb(my_print);        lv_init();        lv_port_disp_init();        os_hook_tick_register_callback(tick_callback);        lv_demo_music();        uint32_t pre_time = os_get_ticks();        uint32_t cur_time = pre_time;        while (1) {            uint32_t delay = lv_timer_handler();            cur_time = os_get_ticks();            if(cur_time - pre_time >= 25)            {                pre_time = cur_time;                st7789_itf_sync();            }            if (delay < 1) delay = 1;            os_delay(delay);            //wq_printf("delay %d\r\n",delay);        }
        lv_deinit();    }    return;}

测试结果如下

六. 总结

LVGL的可移植性超级好,只需要实现一个简单的写点或者刷指定范围屏的接口即可,我们也可以其相关可移植性相关的设计。本分享实现了LVGL移植以及DEMO演示,后面可以考虑输入,文件系统相关移植,以及性能的优化。


评论
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 166浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 110浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 97浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 79浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 92浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 48浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 76浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 72浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 71浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 87浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦