今天我们来移植一下LVGL,其实LVGL和Qt差不多,操作起来都很简单,看着官方文档都可以自己学习使用。
难就难在移植上面,移植个LVGL花了我三天才弄明白(虽然最后发现在一个很弱智的问题上耽误了我两天),网上的教程基本上只说应该怎么做,都没有说为什么要这么做,那么今天我们就来移植一下,知道原理之后就可以举一反三地去移植任何开发板了。
包括移植也是有官方手册的,英文好的小伙伴可以自行去查阅。官方手册讲的肯定比我好。
Porting — LVGL documentationhttps://docs.lvgl.io/master/porting/index.html
虽然说是可以移植任何开发板,但是还是有要求的。
而我们常用的STM32C8T6是不符合要求的,GD32E230也是不符合要求的(虽然网上好像有移植成功的案例,但是作为初学者我们尽量还是拉高配置吧,省的不必要的麻烦),我这边用的是GD32F407VET6,配置上是足够的,包括ESP32-S3什么的也是满足要求的。
那么确认手上板子能够移植LVGL之后我们就可以进行下一步了,首先先要获取到LVGL的库文件,这个需要我们到github上面去下载。
https://github.com/lvgl/lvglhttps://github.com/lvgl/lvgl考虑到github常常进不去,我把常用的版本给下载下来了。
链接:https://pan.baidu.com/s/1ubfUKtC1EuiZ_fM9rX4aHg?pwd=u5b7
提取码:u5b7
里面是8.2,8.4和9.1。其中8.4和9.1是官方推荐版本,而8.2是部分网上视频教程里用的(某点原子),我接下来以8.4为例。
我们需要先整一个手上开发板可以用的工程文件,可以是空白的,但是要编译通过,这个不难,你就拿你之前项目的文件就行。
接着我们把下载好的LVGL解压。
得到的是这样的。
其中大部分文件是可以删除的,当然你不删也可以,但是会显得很臃肿。
我们来明确一下LVGL是什么东西,不要把LVGL当成是什么很高大上的东西,说白了就是第三方库,和我们自己封装的头文件什么的是一个样的,只不过人家LVGL的文件数量多了点,仅此而已。
既然和我们写的头文件一样,那么我们只需要保留.c和.h文件即可,其他后缀的都是可以删除了。
接下来我们就对解压过后的LVGL进行删减。
只留下面这三个文件夹和两个.h文件即可。
为什么把其他的删掉呢?那当然是因为它们不包含我们需要的.c和.h文件啦。
顺便把留下的这五个东西介绍一下。
第一个demos,跟它的名字一样,存放的是官方的demo,因此实际上我们也是可以删掉的,留下来的原因就是等我们移植完LVGL之后可以调用一下demo看看效果。
第二个examples,存放的是一些关于示例代码,我们只需要留下里面的porting即可。
然后porting里的文件是三个(一对的.c和.h算一个)。
disp是显示相关的模板,fs是文件管理的模板,indev是输入相关的模板。
因为我的屏幕没有输入,也不需要文件管理,因此对于我来说只需要disp即可,但是其他没用到的最好也别删了,保不齐什么时候就用上了(没事的时候打算整个触摸屏再移植一下LVGL玩玩)。
然后就是src,这个就是核心的文件了,一个都不能少。
lv_conf_tempplate.h这个文件是配置文件。
lvgl.h这个头文件里面包含了我们常用的其他头文件,因此我们只需要包含这个就等同于包含了其他很多头文件了,比较方便。
然后配置文件的名字我们需要把后面的_template给删掉,变成lv_conf.h。
修改名称的原因就在于LVGL里面源文件中包含的配置文件的名称就是lv_conf.h,
那么接下来我们就要把LVGL的文件加入到我们的工程文件里了。
接下来在你工程文件里存放模块的文件夹里新建一个文件夹来存放LVGL的文件(名字随意,我这边就叫LVGL)
接下来在这个LVGL的文件夹里再新建两个文件夹,名字随意,一个用来存放源码一个用来存放官方demo,其实是可以省略的,只不过我们要运行官方demo的话区分一下会比较清晰,如果不需要运行官方demo的可以省略这一步,直接跳到下一步。
我这边就借用一下某点原子的命名规则了,建立两个文件夹,GUI用来存放源码,GUI_APP存放官方demo。
接着在把删减过后的LVGL中的demos文件夹放到GUI_APP里(跳过上面一步的小伙伴这一步也可以省略)。然后在GUI里建一个名为lvgl的文件(这里名字是固定的,一定要这么写,否则后续要修改的地方非常多),接着把剩余两个文件夹和两个头文件放到这个lvgl文件夹里。
最后就是这样子的。
其他命名啊,结构啊什么的都无所谓,但是lvgl这个文件夹是一定要安装这个名字去创建的,并且要像上图这样把删减后的LVGL放进去。
我们随便打开一个lvgl源码的头文件,可以发现人家包含头文件是有一定结构的,因此我们需要按照这种结构去存放lvgl的源码。
那么接下来我们的工程里有了LVGL的代码了,我们就需要再Keil里设置包含住他们。
首先现在分组里添加上一些组。
可以看到我设置的分组的名字和我们LVGL文件名称是基本对应的,不过实际上分组名字是可以随便起的,你要是乐意起个ABCD都是可以的,但是名字对应上方便我们查看代码。
这边分组完我们还需要把每个文件夹内的.c文件放进每个分组里,记得不要重复包含,也不要漏掉包含,就这一步是最容易疏漏的。
每个文件夹内的文件夹里的.c文件也是需要包含的,虽然大部分我们用不上,但是作为小白的我们暂时还分不出哪些是可以不用的,所以我们就麻烦一下全部加上。
记住,是对应分组的文件夹里的所有.c文件!!!
这一步是最繁琐也是最容易出问题的,如果移植失败,那么90%的原因是在这里!!!
添加完毕之后在左侧我们就可以看到添加进工程的文件了。
但是光添加进来没有用,我们还得让编译器知道这些文件的存在,因此我们还需要添加头文件路径。就像下图这样,可以顺便把C99给勾上。
一共往上面添加四个路径。
这个根据大家命名的不同需要选择不同的路径,但是通过上面这个图,大体上可以看出需要包含哪些路径的对吧。
上面步骤做完之后我们就可以先编译一下看看有没有什么不对的地方了。
会有警告,但是正常情况不会有报错,我们选择无视警告。
如果有报错的,一般是报这个错误Error: L6218E: Undefined symbol ,这种情况基本就是给分组添加文件的时候没有添加全,换句话说就是没把某些LVGL的文件添加进我们的工程中。
报错的小伙伴重新再把文件添加进工程里,记得不要重复包含。
只有警告的小伙伴可以跟我一起进行下一步了。
我们再新建个分组,把配置文件添加进来,不进行这一步也可以,但是就需要在Keil外面修改配置文件了。
第一步我们就先把配置文件给打开,打开的方式就是把这个判断条件修改为1。
包括其他配置也可以在这个文件里进行修改。
比如下面这个宏定义,就是给LVGL分配的内存大小。
默认给48KB,我的板子是给的起,但是有些板子内存资源可能有些不太够,分太多会编译报错,那么就可以在这边修改小一点,但是最小不要小于2KB。
还有颜色相关的配置也在这边。
默认的颜色像素是RGB565,如果你们的屏幕不是RGB565的话,就是在这边修改的。
下一步把显示相关的.c和.h也打开,也是通过修改判断条件。
接着修改宏定义来指定屏幕大小。
按照顺序分别是宽和高。
如果是8.2版本的是没有这俩宏定义的,需要自己去定义。
接着我们需要指定LVGL的缓冲模式,有三种,按照顺序效果是越来越好的,但是相对的内存消耗会更大,大家量力而行,一般来说模式一都够用了。
选择的方式就是把另外两种模式的代码注释掉。
看的出来我们上面的宏定义是有用在这边的。
然后因为这是和显示相关的,因此我们肯定是需要自己的屏幕驱动代码的,不懂写的小伙伴可以回顾一下我之前的文章。
【硬件模块】ST7735S(1.8寸TFT-LCD)-CSDN博客SPI,英文全称Serial Peripheral Interface,即串行外围设备接口,是一种高速、全双工、同步的串行通信总线。我们之前说过I2C,那么我们就拿I2C和SPI做个对比。SPI和I2C对比,优势在于SPI的传输速率比I2C快得多,劣势在于SPI需要用的通信线比较多。SCK(Serial Clock):串行时钟线,由主设备产生,用于同步数据传输。MOSI(Master Output Slave Input):主机输出从机输入线,主设备通过这条线发送数据给从设备。https://blog.csdn.net/m0_63235356/article/details/139422591?spm=1001.2014.3001.5502
在LVGL显示相关的文件中包含我们的屏幕驱动代码。
接着在disp_init这个函数里调用我们屏幕的初始化函数。
在disp_flush函数里调用我们的打点函数。
其中函数的形参area是个结构体,一共有四个成员,分别是x1,x2,y1,y2,表示打点的范围,color_p是像素点的数组,我们一般是要强转一下类型再给我们的打点函数。
上面是我写的打点函数,大家根据自己的驱动去填写参数即可。
那么关于显示的配置我们就算配置完了,因为我的屏幕不是触摸的,因此我就不配置输入相关的了,不过大致流程也是这样的。
接下来我们回到我们的主函数。
先包含两个头文件,我这是只有输出的,如果配置输入的话,还需要包含另外一个"lv_port_indev_template.h"。
#include "lvgl.h"
#include "lv_port_disp_template.h"
我们最后还需要给LVGL一个心跳,也就是我们需要使用定时器,在一定的时间里调用一个LVGL的函数lv_tick_inc。
不懂定时器的小伙伴可以参考我之前的文章。
STM32,ESP32,GD32都有,往前翻一翻。
然后我们每隔1ms进入一次中断,我们就给lv_tick_inc这个函数传入1,如果是隔5ms一次进入中断,那么给的参数就是5,也就是说隔几毫秒,传的参数就是几。
接着在主循环里每隔5ms调用一次lv_timer_handler就可以了。
那么我们就算是移植完LVGL了。
但是我们要看效果,还需要调用一些初始化函数,下面是输出的初始化,如果有输入的话,还需哟啊调用输入初始化函数。
lv_init();lv_port_disp_init();
初始化完之后我们再创建组件,鉴于大家都是小白(包括我),因此我们直接从官方文档里复制示例代码来看看效果。
我这里就只贴出main.c的内容,大家大概知道流程是怎么样的就可以了。
#include "board.h"
#include "Z_TFT.h"
#include "Z_Timer.h"#include "lvgl.h"
#include "lv_port_disp_template.h"void TIMER0_UP_TIMER9_IRQHandler(void){if(timer_interrupt_flag_get(TIMER9, TIMER_INT_FLAG_UP) == SET) { timer_interrupt_flag_clear(TIMER9, TIMER_INT_FLAG_UP); // 清除中断标志位 lv_tick_inc(1);}
}int main(void){board_init();Z_Timer_Init();lv_init();lv_port_disp_init();lv_obj_t * bar1 = lv_bar_create(lv_scr_act());lv_obj_set_size(bar1, 100, 20);lv_obj_center(bar1);lv_bar_set_value(bar1, 70, LV_ANIM_OFF);while (1){delay_ms(5);lv_timer_handler();}
}
效果是这样的。
那么至此,各位就成功地移植LVGL了,完整做下来之后实际上没有那么困难,LVGL就是普普通通的第三方库,我们要做的就是把这一堆文件塞进我们工程里,再让我们的编译器识别到这些文件就行了。
最后再总结一下可能会出错的点。
第一个点就将文件包含进工程里,这是最容易出错的地方,多了少了都可能会出错。
第二就是配置显示的时候自己的屏幕驱动了,不过跟我的驱动走没什么大问题。
我这边留个课后作业,我们做删减的时候不是把LVGL的官方demo留下来了嘛,给大家的作业就是运行这些官方demo,流程是一样的,就是把文件包含进工程里,再添加头文件路径,然后改个配置调用即可。
。
。
。
算了,带大家做一遍吧。
一样是先创建分组,再把文件弄进来,板子配置一般的小伙伴运行stress那个demo,配置好的可以用music。我这边用stress,本来想玩一玩那个music的,但是调了一会还是运行不了music然后就放弃了。
接着是头文件路径,我们一样是加进来,不同的demo用不同的路径。
然后在配置文件里打开stress的宏定义,不同的demo打开不同的宏定义。
完成上面步骤之后就可以运行了,在main.c里包含一下lv_demo_stress.h,再调用lv_demo_stress就可以了。
我们就可以看见屏幕正在飞快刷新着,这是LVGL在不断地调用各种组件。
至此,我相信各位小伙伴都已经会移植LVGL了。