最近闲来无事,偶尔刷到了移植LVGL的教程,今天肝完了机械原理又移植完LVGL库,真是收获满满的一天,先接一杯水去。
回来了,发个朋友圈高级一下,好困。
lvgl v8.3移植及组件使用_lvgl界面编辑器-CSDN博客https://blog.csdn.net/weixin_45209978/article/details/127374115?spm=1001.2014.3001.5506LVGL - Light and Versatile Embedded Graphics Libraryhttps://lvgl.io/tools/imageconverter以上的两个网址分别为野火霸天虎移植LVGL的教程,第二个是一个图片转C代码的一个在线网址。
具体步骤大概就是,我总结一下。
获取LVGL源码
先从github获取lvgl的源码
地址:GitHub - lvgl/lvgl: Embedded graphics library to create beautiful UIs for any MCU, MPU and display type.
这里选择v8.3下载
这是压缩包中含有的所有文件,接下来根据个人意愿进行修改。
删掉多余的东西 , 只保留以下部分。
为什么把其他的删掉,因为它们不包含我们需要的.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,
把所有的c文件加入到项目中
对的,就是所有。并且需要在
这个页面把所有的文件文件夹位置添加进去,(md,累死我了)。
在添加完所有文件之后就相当于移植进去了LVGL库。
根据实际情况修改 #include "lvgl\lvgl.h" 或者 #include "lvgl.h".
( 看编译器是否能找到文件位置 )
之后编译,一般有错就是xxx函数未定义,那肯定就是添加c文件的时候落东西了。
直到没有错误,一堆警告为止。(doghead)
工程文件修改
这里搬运一下大佬的。
1.启动文件
注意在启动文件中修改堆、栈大小,建议各设置 8kB 空间:(大一些也没关系)
2.lvgl
在做这个之前最好先了解一下你的屏幕是怎么驱动的,然后再做下面的工作。
1.先打开 lv_conf.h
- 第27行,根据屏幕颜色数据类型修改
- 第52行,指定lvgl的动态内存可用大小
- 第96行,每英寸的像素,根据自己屏幕尺寸和像素算一下就好
- 第258行,开启style初始化断言
其他的浏览一下即可,用到的时候再去深究。
打开 lv_port_disp_template.h,将开头的 #if 0 条件编译取消 ,把0改成1
打开 lv_port_disp_template.c,将开头的 #if 0 条件编译取消,把0改成1,设置一下屏幕宽和高的像素。
#define MY_DISP_HOR_RES 480
#define MY_DISP_VER_RES 800
2.看下面的 void lv_port_disp_init(void)
LVGL缓冲区的图像写入有三种方式
- 第一种:一个缓冲区,默认存10行图像
- 第二种:两个缓冲区,默认每个缓冲区存10行图像
- 第三种:两个缓冲区,每个缓冲区存一整个屏幕的图像
这里我们用第二种,把第一种和第三种注释掉即可。
第二种是为存在DMA之类的数据传送机制的硬件设计的,可以把传送缓冲区的内容交给DMA处理,而cpu去执行其他工作(比如渲染),这里我暂时没用DMA,这样的话效果和第一种是一样的。(注:缓冲区大小为屏幕的1/10效果会比较好,这里为了节约一点RAM没有改大小)
也要记得把下面的 &draw_buf_dsc_1 改成 &draw_buf_dsc_2 。
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* 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*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_2;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1;
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
3.屏幕初始化
/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
/*You code here*/
NT35510_Init ();
}
4.刷新缓冲区
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
uint32_t width = area->x2 - area->x1 + 1;
uint32_t height = area->y2 - area->y1 + 1;
NT35510_OpenWindow(area->x1,area->y1,width,height);
NT35510_Write_Cmd ( CMD_SetPixel );
for(uint32_t i = 0;i < width * height;i++)
{
NT35510_Write_Data ( color_p->full );
color_p++;
}
// /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
// int32_t x;
// int32_t y;
// for(y = area->y1; y <= area->y2; y++) {
// for(x = area->x1; x <= area->x2; x++) {
// /*Put a pixel to the display. For example:*/
// /*put_px(x, y, *color_p)*/
// color_p++;
// }
// }
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
5.接下来修改main函数
int main ( void )
{
LED_GPIO_Config();
NT35510_Init ();
TIM7_Configuration();
DEBUG_USART_Config();
lv_init();
lv_port_disp_init();
lv_obj_t* btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 100, 100);
lv_obj_set_size(btn, 120, 50);
lv_obj_t* label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
// GTP_Init_Panel();
while ( 1 )
{
lv_timer_handler();
}
}
6.在周期位1s定时器中断里添加函数给lvgl做时钟
void BASIC_TIM7_IRQHandler (void)
{
if ( TIM_GetITStatus( BASIC_TIM7, TIM_IT_Update) != RESET )
{
lv_tick_inc(1);
TIM_ClearITPendingBit(BASIC_TIM7, TIM_IT_Update);
}
}
上述的有些操作调用了其他文件的函数,记得要包含相应函数的头文件,
比如SysTick_Handler()调用了lv_tick_inc(),那么在stm32f4xx_it.c开头要 加上#include “lvgl/lvgl.h”
效果如图所示
图片显示
1.首先准备一张英文名字的图片(不能是数字或者字母)
之后在在线转换网站中
2.按图所示进行设置之后输入图片,生成C代码
3.把C文件加入到工程中
图片文件不能太大,要不然单片机的flash放不下(也许可以放在sd卡中)。
4.将以下代码放到main函数中。
lv_obj_t * bg_top;
LV_IMG_DECLARE(nahida);
bg_top = lv_img_create(lv_scr_act());
lv_img_set_src(bg_top, &nahida );