历史上的今天
返回首页

历史上的今天

今天是:2025年08月12日(星期二)

正在发生

2021年08月12日 | LVGL | lvgl最新版本在STM32上的移植使用

2021-08-12 来源:eefocus

lvgl简介

LittlevGL是一个免费的开源图形库,提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素、漂亮的视觉效果和低内存占用。

特点:

  • 强大的构建模组 按钮、图表、列表、滑块、图像等

  • 先进的图形 动画、反锯齿、半透明、平滑滚动

  • 多样的输入设备 触摸板、鼠标、键盘、编码器等

  • 多显示器支持 支持同时使用多个TFT或单色显示器

  • 多语言支持 UTF-8格式文字编码

  • 完全自定义 图形元素

  • 硬件无关 可用于任意微控制器或显示器

  • 可裁剪 用于小内存(80 KB FLASH,12 KB RAM)操作

  • 操作系统、外部存储以及GPU 支持但非必须

  • 单帧缓存 即可实现先进的图形效果

  • C语言编写 以最大化兼容(C++ 兼容)

  • 模拟器 无需嵌入式硬件就可以在电脑上开始GUI设计

  • 教程、示例、主题 从而快速GUI设计

  • 文档 在线及离线

  • 免费开源 基于MIT协议

运行的硬件要求:

lvgl资料

LVGL的资料很丰富,下面列出一些常用的资料链接:

1、lvgl英文官网:

https://lvgl.io/

2、lvgl中文网:

https://littlevgl.cn/    (还在建设中)

3、lvgl源码:

https://github.com/lvgl/lvgl

4、lvgl基于Visual sudio 的PC模拟器:

https://github.com/lvgl/lv_sim_visual_studio

5、正点原子lvgl教程资料:

http://www.openedv.com/docs/book-videos/zdyzshipin/4free/littleVGL.html

6、基于荔枝派Nano开发板的lvgl教程:

http://nano.lichee.pro/application/littlevgl.html

7、基于野牛开发板的 lvgl 6.0 例程:

https://gitee.com/mzy2364/LittlevGL_Demo

8、lvgl在线体验例程(可在浏览器体验):

https://lvgl.io/demos

9、lvgl官网教程:

https://docs.lvgl.io/latest/en/html/index.html

10、lvgl官方DEMO:

https://github.com/lvgl/lv_examples

lvgl移植到STM32

1、下载源码

源码链接中下载一份源码,lvgl已经更新迭代了很多个版本,这里我们选择目前最新的7.10.1版本来移植:

注意:不同版本之间可能有很大的不同,所以看本篇教程移植的的小伙伴尽量使用与本文相同的版本。

下载得到:

2、准备stm32工程

下面我基于普中的stm32f103开发板来移植,首先准备一个lcd显示的例程,并更名为lvgl_test:

3、新建GUI文件夹

在工程目录下新建一个GUI文件夹,GUI文件夹下新建两个子文件夹:

把刚才下载的lvgl-7.10.1里的所有内容复制到lvgl文件夹中,lvgl_app文件夹暂时留空。


4、移植文件更名

下面,我们把GUIlvglexamplesporting下的文件进行一个更名操作(其实不更名也可以,为了文件名看起来规范一些我们进行一个更名):

这是移植相关的几个文件,其中:

lv_port_disp:显示相关。

lv_port_indev:输入相关。

lv_port_fs:文件系统相关。

5、配置文件更名

把GUIlvgl下的lv_conf_template.h文件复制到GUI文件夹下并更名为lv_conf.h:

6、keil工程配置

(1)导入文件

打开keil工程,在工程下新建三个组,并导入文件:

导入完成后得到:

这里的lvgl_porting中我们暂时只导入lv_port_disp.c文件,这是显示相关的移植文件。本篇笔记先把显示打通,其它两个文件后续有机会再弄。


(2)包含头文件路径

下面包含头文件路径:

(3)修改堆栈大小

因为官方说明文档中推荐我们堆、栈大小设置为8k:

所以这里我们就按推荐进行设置:

(4)设置C99模式

lvgl需要编译器支持C99或更新的标准:

C99模式可进行如下设置:

(5)使能lv_conf.h的条件编译

进行上面的设置后我们首先进行编译,会报很多个错误:

这是因为需要lv_conf.h里的一些东西,打开lv_conf.h里的条件编译即可:

再次编译,编译通过:

(6)lvgl配置

我们可以对lvgl进行一些定制配置,这些配置内容在lv_conf.h文件中,下面进行一些关键配置:

  • 显示器宽度:#define LV_HOR_RES_MAX (240)

  • 显示器高度:#define LV_VER_RES_MAX (480)

  • 色彩深度: #define LV_COLOR_DEPTH 16

  • DPI: #define LV_DPI  100

  • 提供给lvgl的空间: #define LV_MEM_SIZE  (32U * 1024U)

其中调整LV_DPI  可以调整各控件间的紧凑,可根据实际情况进行更改;LV_MEM_SIZE  为lvgl可用空间,资源允许的情况下可以稍微设大些,这个设置过小的话,在跑一些稍微复杂的demo时界面就会刷不出来。

这里只是列出了几个常用的配置,lv_conf.h还有很多的配置,可根据实际情况进行配置。

(7)填充、修改lv_port_disp.c

lv_port_disp.c里面的内容主要有:



我们需要重点关注lv_port_disp_init显示接口初始化函数与disp_flush屏幕刷新两个函数。

其中,lv_port_disp_init函数里主要要选择一种写缓存的方式及设置显示分辨。我们选择第一种写缓存的方式,修改后的函数如:

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 your display drivers `flush_cb` to copy its content to your display.

     * The buffer has to be greater than 1 display row

     *

     * There are three buffering configurations:

     * 1. Create ONE buffer with some rows:

     *      LVGL will draw the display's content here and writes it to your display

     *

     * 2. Create TWO buffer with some rows:

     *      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. Create TWO screen-sized buffer:

     *      Similar to 2) but the buffer have to be screen sized. When LVGL is ready it will give the

     *      whole frame to display. This way you only need to change the frame buffer's address instead of

     *      copying the pixels.

     * */


    /* Example for 1) */

    static lv_disp_buf_t draw_buf_dsc_1;

    static lv_color_t draw_buf_1[LV_HOR_RES_MAX * 10];                          /*A buffer for 10 rows*/

    lv_disp_buf_init(&draw_buf_dsc_1, draw_buf_1, NULL, LV_HOR_RES_MAX * 10);   /*Initialize the display buffer*/


    // /* Example for 2) */

    // static lv_disp_buf_t draw_buf_dsc_2;

    // static lv_color_t draw_buf_2_1[LV_HOR_RES_MAX * 10];                        /*A buffer for 10 rows*/

    // static lv_color_t draw_buf_2_1[LV_HOR_RES_MAX * 10];                        /*An other buffer for 10 rows*/

    // lv_disp_buf_init(&draw_buf_dsc_2, draw_buf_2_1, draw_buf_2_1, LV_HOR_RES_MAX * 10);   /*Initialize the display buffer*/


    // /* Example for 3) */

    // static lv_disp_buf_t draw_buf_dsc_3;

    // static lv_color_t draw_buf_3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*A screen sized buffer*/

    // static lv_color_t draw_buf_3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];            /*An other screen sized buffer*/

    // lv_disp_buf_init(&draw_buf_dsc_3, draw_buf_3_1, draw_buf_3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);   /*Initialize the display buffer*/


    /*-----------------------------------

     * Register the display in LVGL

     *----------------------------------*/


    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 = 240;

    disp_drv.ver_res = 400;


    /*Used to copy the buffer's content to the display*/

    disp_drv.flush_cb = disp_flush;


    /*Set a display buffer*/

    disp_drv.buffer = &draw_buf_dsc_1;


#if LV_USE_GPU

    /*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/


    /*Blend two color array using opacity*/

    disp_drv.gpu_blend_cb = gpu_blend;


    /*Fill a memory array with a color*/

    disp_drv.gpu_fill_cb = gpu_fill;

#endif


    /*Finally register the driver*/

    lv_disp_drv_register(&disp_drv);

}


disp_flush需要调用底层lcd操作接口,这里修改为:

这里我们调用一个写像素点的函数,也可以直接调用一个显示的填充方形函数。

最后,需要再头文件中声明lv_port_disp_init函数:

(8)配置一个定时器为lvgl提供心跳

lvgl需要一个心跳节拍,可以使用系统滴答定时器,也可以使用其它定时器。我们这里的配置如下:

TIM4_Init(999, 71);     // 1ms进入一次中断,为lvgl提供心跳


void TIM4_IRQHandler(void)

{

 if(TIM_GetITStatus(TIM4,TIM_IT_Update))

 {

  lv_tick_inc(1);

 }

 TIM_ClearITPendingBit(TIM4,TIM_IT_Update); 

}


(9)测试官方demo

下面开始进行测试,至此我们的GUIlvgl_app文件夹里还没有任何内容,我们下载官方提供的demo来进行测试,同样地,下载官方demo:

官方给我们提供了很多demo:

复制lv_examples-7.10.1整个文件夹至GUIlvgl_app文件夹下并更名为lv_examples:

把GUIlvgl_applv_examples下的lv_ex_conf_template.h复制到GUI文件夹下并更名为lv_ex_conf.h,这是demo的配置文件:

想跑哪个demo就配置相关宏。

下面我们跑一个lv_demo_widgets的demo进行演示:

主函数:

运行测试:

至此,lvgl显示移植成功。要想演示其它综合demo或控件例子也同上面一样导入源文件、包含头文件、打开demo宏开关等步骤进行演示。

推荐阅读

史海拾趣

Galaxy Microelectronics公司的发展小趣事

江苏飞翼智能科技有限公司在无人机航模领域也有着深厚的积累。公司不仅自己购买零件组装各类航模,还积极参与国内航模比赛,凭借良好的飞机性能和一流的操控技术取得了不俗的成绩。尤其是涡喷航模,作为中高级的航模产品,其高难度的特技动作展示了飞翼智能在无人机技术方面的深厚功底。这些经验积累和技术创新为飞翼智能向无人机应用型企业转型奠定了坚实基础。

DL Instruments LLC公司的发展小趣事

DL Instruments的成功离不开其领导层的智慧与远见。David Woolston先生作为公司的创始人之一,凭借其丰富的行业经验和敏锐的市场洞察力,带领公司不断突破自我、超越竞争对手。他注重人才培养和技术创新,为公司的发展奠定了坚实的基础。

Crystal Semiconductor Corp公司的发展小趣事

Crystal Semiconductor Corp成立之初,面临着资金短缺、技术难题和市场竞争的多重压力。然而,公司的创始人凭借对半导体技术的深刻理解和敏锐的市场洞察力,成功吸引了一批志同道合的工程师和投资者。他们共同攻克了技术难关,推出了首款具有竞争力的晶体管产品,迅速在市场上占据了一席之地。

FARNELL公司的发展小趣事

FARNELL公司成立于1937年,由创始人在英国利兹创建。起初,公司专注于电子元器件的分销,凭借其优质的产品和可靠的供货能力,逐渐在电子行业中崭露头角。在那个时代,电子产品正处于快速发展阶段,FARNELL公司凭借其前瞻性的市场洞察力和灵活的供应策略,赢得了众多客户的信赖。

CMD公司的发展小趣事

CMD技术公司成立于1986年,当时的市场正处于计算机技术的飞速发展时期。公司初期主要聚焦于为DEC计算机系统开发SCSI主机适配器,这一决策在当时看来是富有远见的。随着个人电脑的普及,CMD的产品迅速获得了市场的认可,为后续的发展奠定了坚实的基础。

Cygnal Integrated Products Inc公司的发展小趣事

在20世纪90年代末,Cygnal Integrated Products Inc由几位热衷于微控制器技术的工程师创立。他们看到了嵌入式系统在电子产品中的巨大潜力,并决定专注于开发高性能、低功耗的微控制器。通过不断的技术创新和市场调研,Cygnal成功推出了其首款产品——一款集成了多种外设接口的8位微控制器,受到了市场的热烈欢迎。

问答坊 | AI 解惑

请教WinCE6R2实现手机功能

各位大哥,我们是使用PXA303+Wince6R2实现的手持设备,使用SimCom300D模块作为手机、通讯模块,请教大家如何实现: 1.接打电话 2.发送接收短消息 3.彩信 谢谢。…

查看全部问答>

VS2005 添加Dialog资源时有些软件提供的对话框资源 怎样使用

VS2005中,做WinCE的应用软件,使用S3C2440+3.5\'触摸屏,在添加Dialog资源时有些软件提供的对话框资源: IDD_POCKETPC_LANDSCAPE IDD_POCKETPC_PORTRAIT IDD_POCKETPC_SQUARE IDD_POCKETPC_PORTRAIT 有啥区别,怎样使用? …

查看全部问答>

诚聘驱动开始人员1位,过滤驱动开发。

诚聘驱动开始人员1位,过滤驱动开发。 共同创业,前途无量。 创业地点:山东威海 简历投放:pass86@gmail.com…

查看全部问答>

单片机驱动电磁阀

在单片机的一段要加上上拉电阻,然后和这个图组合就能够驱动起来300mA的电磁阀了(功率是3.5w,电压12V) 我c:/51驱动300mA电磁阀.bmp 我不知道怎么上图,有知道的给我说下,我上图! …

查看全部问答>

请教触摸屏函数DdsiTouchPanelGetPoint调用的流程?

请问触摸屏函数DdsiTouchPanelGetPoint是在什么情况下调用的? 是在用户按了触摸屏后,产生中断,然后调用的该函数吗? 我的NK运行后,没有按触摸屏,怎么系统一直在调用该函数,而触摸屏也没有响应?…

查看全部问答>

小信号的过零检测

       单位要设计了 一电压过零检测电路   需要从几毫伏 到几伏之间 频率小于1khz 的 电压中检测出零点  ,一直很迷茫  我的设计大致步骤如下  希望各位大侠帮帮我小信号  经AGC放大到5伏   &n ...…

查看全部问答>

看到一本书里有个运放电路,在输出输出端接了一个电阻,太理解这个电阻的作用

按照理想运放虚拟短路,虚拟断路的原理计算出Vout=-(Rf/R2)Vin,但里面多了一个电阻R4,不明白是什么作用,希望大家指点一下。 [ 本帖最后由 我很单片机 于 2011-8-4 13:20 编辑 ]…

查看全部问答>

帮忙分析下,串口调试助手怎么显示乱码,xiexie

外接晶振 8MHz /*********************************************** ****      AVR  DS18B20范例           *** **** MCU: ATmega16        &n ...…

查看全部问答>

绩效管理的五大关键因素

  1.高层管理者高度重视   绩效管理的导入是企业的一项大的组织变革,一方面绩效管理的引入涉及利益分配的问题,由于变革的诸多不确定性,大家对未来的预期不明确,绩效管理的推行将会受到来自各级管理者和员工的强大阻力,绩效管理工作的负责 ...…

查看全部问答>