STM32F407移植LVGL(V8.3版本)

一、LVGL简述

1.丰富且强大的模块化图形组件:按钮、图表、列表、滑条、图片等
2.高级图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果
3.支持多种输入设备:触摸屏、键盘、编码器、按键等
4.配置可裁剪,最低资源占用:64K Flash,16K RAM
5.基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等
6.支持操作系统、外置内存、以及硬件加速
7.支持模拟器仿真,可以无硬件进行开发

二、准备STM32工程(裸机)

1.硬件要求
芯片资源:Flash>128K,RAM>64K;(LVGL至少占用:Flash>64K,RAM>16K)
显示屏:建议使用16位色深的彩屏, 1.44寸、2.8寸、4.3寸等等
不建议使用常用的0.96寸OLED屏,指甲大小的单色屏,耗100K资源去撑它,没搞头
2.STM32工程要求
堆栈大小:Heap、Stack,设置为:0x1000; 
准备:画点函数,用于后面注册LVGL的显示功能;
准备:触摸检测函数 (返回:0-未按下、1-按下)、坐标获取函数,用于注册LVGL的触屏功能;

三、下载LVGL

v8.3版本,网上教程资源众多、移植简单,是目前最广泛使用的版本
官方下载链接:https://github.com/lvgl/lvgl

在这里插入图片描述
需要用到的是上图中的3个文件夹 + 2个h文件

四、源文件裁剪

1.在keil工程目录下 Middlewares 文件夹中新建LVGL文件夹,把上面的3个文件夹 + 2个h文件放进去
“lv_conf_template.h”,是LVGL配置参数的重要文件。
2.原文件名:“lv_conf_template.h”,修改为: “lv_conf.h”;
3.删除不需要的文件夹
打开文件夹:“LVGL / examples”:只保留 porting 文件夹,其它的文件夹和文件,都删除掉
4、修改 porting 里面的文件名称
6个文件的名称,都删除 “_template” 字样

这个 “LVGL” 文件夹,以后可以复制给各类的工程使用,不限于STM32的工程,通用

五、STM32工程添加 LVGL 文件

1.打开Keil,在工程里,添加4个文件夹(Groups);
在这里插入图片描述
2.为每一个文件夹组(Groups),添加需要的文件
在这里插入图片描述

3.添加头文件

打勾C99,并,添加3个头文件路径:

添加:LVGL 文件夹的路径

添加:LVGL\src 文件夹的路径

添加:LVGL\examples\porting 文件夹的路径

4.编译
在这里插入图片描述
六、注册显示
1.启用 lv_conf.h
双击打开 lv_conf.h,对以下内容进行修改,以启用此文件
第15行,原:#if 0,修改为:#if 1
2、启用 lv_port_disp.h
双击打开 lv_port_disp.h,修改以下内容,以启用此文件:
第7行,原:#if 0, 修改为:#if 1
第22行,原:“lvgl/lvgl.h", 修改为:”lvgl.h"
3、启用 lv_port_disp.c
双击打开 lv_port_disp.c,修改以下内容,以启用此文件:
第7行,原:#if 0, 修改为:#if 1
第12行,原"lv_port_disp_template.h", 修改为:“lv_port_disp.h”
4、添加 LCD 驱动的头文件
在 lv_port_disp.c中:
第14行,插入你的LCD驱动文件,如:#include “./BSP/LCD/lcd.h”,写上你的h文件。
第20行、第25行,是显示屏的宽、高度。查看你的显示屏参数,填写实际像素即可。
插入LCD的头文件,目的是为了让这个c文件,能调用LCD的画点函数;
注意一个:LVGL默认使用横屏的方式,这一点要注意,别写反了。
5、选择创建缓存的方式,3选1
还是在 lv_port_disp.c 中,
第86行到101行,LVGL 提供了创建显示缓冲区的3种方式,这里,必须3选1。
绝大多数情况下,使用第1种方法,即:只创建1个缓冲区;
注释掉第90~101行,即:不使用第2和第3种方法;
6、关联 画点函数
还是在 lv_port_disp.c 中,向下滚动,找到disp_flush( )函数:
第173行,替换你的 LCD 的画点函数; 参数:x坐标、y坐标、16位颜色值。
小编用的画点函数:lcd_draw_point(x, y, color_p->full);
这里给LVGL一个画点函数后, LVGL就能完成需要的显示操作了。

至此,显示部分的修改、注册,已完成。点击编译:0 Erros。

七、注册 触摸屏

1、启用 “lv_port_indev.h”
打开"lv_port_indev.h", 修改以下内容,以启动此文件:
第8行,原:#if 0, 修改成:#if 1
第20行,原:“lvgl / lvgl.h”, 修改成:“lvgl.h”
2、启动 “lv_port_indev.c”
打开"lv_port_indev.c", 修改以下内容,以启动此文件:
第 7行,原:#if 0, 修改为:#if 1
第12行,原:“lv_port_indev_template.h", 修改为:“lv_port_indev.h”
第13行,原:“…/…/lvgl.h”,修改为:“lvgl.h”
3、添加 触屏 的驱动头文件
还是在 “lv_port_indev.c” 中:
第14行,插入:#include “触摸屏的头文件”,小编这边是:
#include “./BSP/TOUCH/touch.h”
目的是为了让这个c文件,能调用触屏的:触摸状态检测函数、坐标获取函数;
4、注释掉不需要的输入任务注册
还是在 “lv_port_indev.c” 中,
向下滚动至大约70行,找到输入注册函数:lv_port_indev_init( ),
函数内有5种输入方式的任务注册:触屏、鼠标、键盘、编码器、物理按键;
保留触摸屏输入的任务注册;
其它4种输入任务的注册,注释掉,;
5、添加 触摸检测函数
还是在 “lv_port_indev.c” 中,
向下滚动到大约209行,找到触摸检测函数:touchpad_is_pressed(),
这个是:触屏状态检测函数,函数返回:0-未按下、1-按下;
第213行,原 return false, 注释掉;
第212行,原来是空行,插入:
tp_dev.scan(0);
if (tp_dev.sta & TP_PRES_DOWN) /* 触摸屏被按下 */
{
return true;
}
else
return false;
6、添加 坐标获取函数
还是在 “lv_port_indev.c” 中,

在刚才触摸检测函数的下方,找到坐标获取函数:touchpad_get_xy();
本函数的作为:使LVGL能够获取到触摸按下时的x、y坐标;
第221行, 修改为:
tp_dev.scan(0);
(*x) = tp_dev.x[0];
(*y) = tp_dev.y[0];

7、额外的测试预埋
在刚才的那个 touchpad_get_xy( ) 函数中,增加加一行画点操作:
222行下方,插入新行,画点操作:lcd_draw_point(x, y, BLACK);
这样操作的目的,是令LVGL在获取坐标时,也在这个坐标上画一个黑点

八、添加 LVGL 的文件引用

之前的几个部分,已修改完成了LVGL显示、触摸支持。
现在,正式在工程中“应用” LVGL。
1、给工程,添加 LVGL 的头文件
打开 main.c,在顶部, #include 三个头文件(复制下面三行):

#include “lvgl.h” // 它为整个LVGL提供了更完整的头文件引用
#include “lv_port_disp.h” // LVGL的显示支持
#include “lv_port_indev.h” // LVGL的触屏支持

2.初始化LCD、触摸屏
3、初始化LVGL、显示、触屏
在硬件的初始化代码之后,进行LVGL的初始化(复制下面3行):

lv_init(); // LVGL 初始化
lv_port_disp_init(); // 注册LVGL的显示任务
lv_port_indev_init(); // 注册LVGL的触屏检测任务

4、显示按钮控件、文本控件
在LVGL的初始化之后,添加LVGL控件 ,以测试LVGL的显示:

添加一个按钮

为按钮添加文本

添加一个独立的标签文本

    // 按钮
    lv_obj_t *myBtn = lv_btn_create(lv_scr_act());                               // 创建按钮; 父对象:当前活动屏幕
    lv_obj_set_pos(myBtn, 10, 10);                                               // 设置坐标
    lv_obj_set_size(myBtn, 120, 50);                                             // 设置大小

    // 按钮上的文本
    lv_obj_t *label_btn = lv_label_create(myBtn);                                // 创建文本标签,父对象:上面的btn按钮
    lv_obj_align(label_btn, LV_ALIGN_CENTER, 0, 0);                              // 对齐于:父对象
    lv_label_set_text(label_btn, "Test");                                        // 设置标签的文本

    // 独立的标签
    lv_obj_t *myLabel = lv_label_create(lv_scr_act());                           // 创建文本标签; 父对象:当前活动屏幕
    lv_label_set_text(myLabel, "Hello world!");                                  // 设置标签的文本
    lv_obj_align(myLabel, LV_ALIGN_CENTER, 0, 0);                                // 对齐于:父对象
    lv_obj_align_to(myBtn, myLabel, LV_ALIGN_OUT_TOP_MID, 0, -20);               // 对齐于:某对象

Program Size: Code=226890 RO-data=31090 RW-data=676 ZI-data=68388
FLASH 占用 = Code + RO-data + RW-data =226890+31090+676=258656=252KB
RAM 占用 = RW-data + ZI-data =676+68388=69064=67KB

九、LVGL 心跳、任务刷新

根据官方移植文档的要求,我们还要处理两个关于时间的问题:

间隔精准地,调用时基函数:lv_tick_inc(),俗称心跳,让LVGL精准地知道时间的流逝;

间隔5ms左右,调用周期性任务函数:lv_timer_handler() ,它的作用是检查所有注册任务的时间戳,执行那些已经到期的任务,如:屏显更新、动画更新、触控、定时器事件等;

1、给LVGL一个心跳时基

LVGL心跳函数(时基函数):lv_tick_inc(),每隔1ms调用一次;

这个函数对于图形界面的流畅运行比较重要,它令 LVGL 知道执行任务时流逝的时间。

如果 lv_tick_inc() 调用间隔不准确,可能会导致显示卡顿、任务处理不及时。

特别地,不建议使用滴答时钟SysTick产生这个时基,因为它常常需要被用于RTOS等。

建议使用TIM产生1ms中断,设置它的中断为高优先级,通过中断函数调用LVGL心跳时基。

可以使用TIM6产生1ms中断
2、每隔5ms左右,调用任务刷新函数:lv_timer_handler()
这个函数的作用:让LVGL检查所有已注册任务的时间戳,执行那些已经到期的任务,如刷屏、检测触摸等;
官方描述:大约5ms左右、在while循环中调用;
在main.c的while中,每隔5ms调用:lv_timer_handler()

十、移植成功

触摸、显示正常
在这里插入图片描述

十一、控件事件的添加和相应处理

LVGL的学习,可以大概地为分两部分:界面绘制、事件处理。

1、界面的绘制
因为LVGL的界面绘制,更常规的操作:使用可视化工具进行设计,再把界面工程移植回STM32。
2、事件处理
可视化工具,能帮我们处理好:控件生成、布局、屏幕切换等;
但是,不能处理STM32上的事,如按下按钮,发送某CAN通信等;
这些,都是需要回到Keil或者CubeIDE里,自行增加编写的。
示范:事件的添加、编写响应处理函数。
以按钮的点击为例,示范:事件添加、响应处理。
回到main函数,在添加按钮的那三行代码下方,增加一行,为控件添加事件:
lv_obj_add_event_cb(myBtn, myBtn_event, LV_EVENT_CLICKED, NULL);
myBtn:控件的名称(不限于按钮);
myBtn_event:事件响应时,LVGL调用的处理函数 (等一会儿要手动编写这个函数);
LV_EVENT_CLICKED:点击事件; 不同的控件,有不同的事件类型;
NULL:传递给回调函数的可选用户数据,这里暂时不用;
然后,开始编写刚才说的那个事件回调函数。
在main函数的上方,编写回调函数:myBtn_event();

// 按钮的事件回调函数
static void myBtn_event(lv_event_t *event)
{
    lv_obj_t *btn = lv_event_get_target(event);                    // 获得调用这个回调函数的对象
    if (event->code == LV_EVENT_CLICKED)
    {
        static uint8_t cnt = 0;
        cnt++;
        lv_obj_t *label = lv_obj_get_child(btn, NULL);             // 获取第1个子对象(我们在设计时,已安排了它的第1个子对象是一个label对象)
        lv_label_set_text_fmt(label, "Button: %d", cnt);           // 设置标签的文本,写法类似printf
    }
}

在这里插入图片描述

十二、显示中文

如何显示中文呢?如果使用代码的方法,不外乎两个方法:
LVGL源文件中,自带了一个中文字库。
网上有各种的字模转换方法、程序。

在这里插入图片描述

移植实现点击按钮控制LED1

LVGL按钮控制LED

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值