STM32进行LVGL裸机移植

news2024/11/16 17:46:43

本文的移植参考的是正点原子的课程《手把手教你学LVGL图形界面编程》

基于该课程和《LVGL开发指南_V1.3》“第二章 LVGL 无操作系统移植”,然后结合自身的实际情况进行整理。

先根据自己的习惯,创建基础的单片机工程,然后在APP业务层和DRIVER驱动层之间加上MIDDLEWARE层,在这一层中加入lvgl。

另外,这里还有句话:

所以如果用到lvgl,尽可能使用裸机开发。

基于LVGL版本V8.2。

先看下移植要求

这个图形缓冲区的意义是,不会来一个点就刷新一次,而是至少缓冲到一行之后再刷新,防止画面不断闪烁卡顿,效率更高。所以才要求>水平分辨率像素。缓冲区其实就是个数组,数组大小,至少是一行像素所占用字节的大小,假如屏幕大小是300*240,所用的颜色是16位的,也就是一个像素点占两个字节,那么数组大小就要求至少是300*2=600字节大小。

如果想要更流畅,就建议大于1/10屏幕总像素。也就是300*240/10*2=14400字节,也就是15k字节左右。(貌似对内存要求有点高,那么点内存好像不够用呀???待解决。)

另外注意要勾选C99,要不然编译时会报很多错误。

优化LVGL运行效果的方法

关键点其实就是:缩短图像刷新所需要的时间。

注意:内部SRAM会比外部SRAM快很多,原则上只有内部SRAM不够用的情况下才会使用外部的SRAM。

接下来开始移植……

1、获取处理好的源码

不要重复造轮子,无需自己精简文件。

直接下载正点原子的A盘里的“程序源码”,打开“扩展例程”里的“LVGL例程”,打开以下路径:LVGL例程1 无操作系统移植\Middlewares,将内部的LVGL拿过来放在自己所建的基础工程的MIDDLEWARE目录下,并且,删除LVGL中的GUI_APP,因为我们实际开发中不必使用例程来查看效果。

2、添加到工程

直接查看《LVGL开发指南_V1.3》“第二章 LVGL 无操作系统移植”这里开始的一段内容:

第31页,搜索关键字:3. 添加工程分组、 LVGL 源文件
只需到这里即可结束。
注意,keil只有两级目录,新建分组时可以直接输入全路径
最后的结果显示如下:
这里写下来,后面直接拿来复制即可。
Middlewares/lvgl/example/porting
Middlewares/lvgl/src/core
Middlewares/lvgl/src/draw
Middlewares/lvgl/src/extra
Middlewares/lvgl/src/font
Middlewares/lvgl/src/gpu
Middlewares/lvgl/src/hal
Middlewares/lvgl/src/misc
Middlewares/lvgl/src/widgets

3、配置显示屏驱动

该部分配置步骤如下:

注意,因为上面使用的是正点原子整理过后的文件,所以,很多地方已经是现成的,我们要做的就是在调用我们自己函数的地方给替换掉即可。

不过,为了加深印象,还是做个简单的过程记录吧。

第一步(现成的):

修改条件编译指令,对应的头文件也要改

第二步(需要我们操作):

将lcd的驱动头文件包含到刚才的lvgl显示文件lv_port_disp_template.c中

第三步(需要我们操作):

在lv_port_disp_template.c里找到lv_port_disp_init函数,里面调用了disp_init函数,跳转到disp_init函数中,写入我们自己的屏幕初始化函数,并设置为横屏(可选)

第四步(需要修改分辨率大小):

配置图形单缓冲、双缓冲(要用DMA)或者全缓冲;

单缓冲为最常用的,V8.2版本双缓冲相对单缓冲提升效果不明显,全缓冲对内存要求太高。

缓冲策略是:缓冲区大小是屏幕横向分辨率*10,也就是一次刷新10行。如果效果不太理想,可以尝试增大一次刷新的行数,也就是修改10这个参数。

注意,这里的MY_DISP_HOR_RES宏定义表示水平分辨率大小,有的版本没有给我们定义,所以需要我们在该文件前面自行定义一下。

比如:

#define MY_DISP_HOR_RES 320

另外还有个垂直分辨率的宏MY_DISP_VER_RES,也可以一并定义了。

第五步(需要我们操作):

将刚才的那个函数再往下拉就能找到分辨率设置的地方

分别对应水平分辨率和垂直分辨率,直接填入即可。

第六步,配置打点函数

在刚才的文件中,找到函数disp_flush,在里面调用我们自己的打点函数。

打点函数通常有5个参数,也就是起始坐标,结束坐标,要打点的颜色。

从disp_flush的形参中获取当前的参数值即可,如下所示:

lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);

最后,在main函数中初始化时调用函数lv_port_disp_init进行初始化即可。

4、配置输入设备(可选)

如果不需要输入设备,直接保持条件编译指令为#if 0即可。

如果需要,则按步骤配置即可。

本人因开发工程时暂不需要输入设备,所以此步骤暂且略过。

5、 为LVGL提供任务处理的时基

在自己的定时器驱动中创建一个1ms的定时器,然后在自己的app_timer的中断处理函数中,调用LVGL的 lv_tick_inc 函数:lv_tick_inc(1); /* lvgl 1ms 心跳 */

该函数需要包含如下头文件:#include "lvgl.h"

直接调用即可,无需其他内容。

6、main函数中需要的内容

包含必要的头文件。

#include "lvgl.h"
#include "lv_port_indev_template.h"(可选)
#include "lv_port_disp_template.h"

在main函数中初始化时对lvgl进行初始化;

lv_init();                          /* lvgl系统初始化 */
lv_port_disp_init();         /* lvgl显示接口初始化,放在lv_init()的后面 */
lv_port_indev_init();       /* lvgl输入接口初始化,放在lv_init()的后面 */

定时处理 LVGL 任务。用户需要每隔几毫秒调用一次 lv_timer_handler 函数,以处理 LVGL 相关的任务,该函数可以放在 while 循环中,但延时不宜过大,需要确保 5 毫秒以内。

这里有个问题,那就是正点原子教程中直接delay延时了5ms,我们实际使用时,不可能直接在主循环里面进行延时的,所以我们就直接调用即可。

7、编写测试代码

lv_obj_t *switch_obj = lv_switch_create(lv_src_act());

lv_obj_set_size(switch_obj, 120, 60);

lv_obj_align(switch_obj, LV_ALIGN_CENTER, 0, 0);

如果没问题,可以看到界面中会有个切换按钮

注意:

编译时,如果提示内存问题,可以尝试调整LVGL管理的内存空间。

因为LVGL默认管理的内存空间是48K字节,这其实是蛮大的,如果你单片机的内存都没有48K,那LVGL就肯定会报错。

如何调整?

在lv_conf.h中可以找到MEMORY SETTINGS部分的内容,将48的值改小一些,至少保证自己的单片机能提供这么多内存空间。

配置文件lv_conf.h

lv_conf.h 是一个用户级别的文件,它不属于内核的部分,因此,在不同的工程中,该文件有可能存在差异。

lv_conf.h 文件具有两大功能:

(1) 配置功能:内存、屏幕刷新周期、输入设备的读取周期,等等;
(2) 裁剪功能:使能 / 失能某些功能,有效地优化 Flash 的分配。
lv_conf.h 文件的内容可划分为 10 个板块,如下表所示:
配置项还是蛮多的,这里记录下常用的配置项
总开关
#if 1 /*Set it to "1" to enable content*/
颜色设置
/* 颜色深度: 1(每像素1字节), 8(RGB332), 16(RGB565), 32(ARGB8888) */
#define LV_COLOR_DEPTH                      16
内存设置
/* 0: 使用内置的 `lv_mem_alloc()` 和 `lv_mem_free()`*/
#define LV_MEM_CUSTOM                       0
HAL设置
/* 输入设备的读取周期(以毫秒为单位) */
#define LV_INDEV_DEF_READ_PERIOD            4     /*[ms]*/
字库设置
/* 始终设置默认字体 */
#define LV_FONT_DEFAULT                     &lv_font_montserrat_14
后续控件和特别功能等等,如果不需要就置0.
这几部分正点原子为了教学方便,好多都开了,实际中可选择性关闭。

补充::::::::

外部SRAM、自定义的内存管理算法

需要时再来研究

第9讲 基础篇-LVGL移植(外部SRAM)_哔哩哔哩_bilibili

第10讲 基础篇-LVGL移植(内存管理)_哔哩哔哩_bilibili

DMA2D

DAM2D需要硬件支持,如果板子不支持DMA2D,则无法使用。

首先了解下什么是DMA2D:STM32的“GPU”——DMA2D实例详解 - 知乎

根据上面的文章可知,DMA2D可以理解成专门用来处理2D图像的DMA,类似于“GPU”。

因为STM32F103和STM32F407没有该外设,所以此处不赘述。

F429单片机就支持:STM32F429的图形加速器DMA2D的基础知识

以后有需要再研究。

第11讲 基础篇-LVGL移植(DMA2D)_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

cdm解决‘ping‘ 或者nslookup不是内部或外部命令,也不是可运行的程序或批处理文件的问题

当我们在执行cmd时,会出现不是内部或外部命令,也不是可运行的程序的提示。 搜索环境变量 点开高级 >> 环境变量 打开Path,看是否在Path变量值中存在以下项目: %SystemRoot%/system32; %SystemRoot%; %SystemRoot%/Syste…

【JVM系列】- 探索·运行时数据区的私有结构

探索运行时数据区的私有结构 文章目录 探索运行时数据区的私有结构运行时数据区的结构与概念认识线程了解守护线程和普通线程JVM系统线程 程序计数器(PC寄存器)概述PC寄存器的特点PC寄存器的作用 透过案例了解寄存器为什么需要用PC寄存器来存放字节码的指…

C语言基础-循环与数组

目录 循环 while 循环: for 循环: do while 循环: 中断循环: break continue: 数组 数组:用来装一组数的类型。声明形式如下: 定义数组类型变量: 下标:即各元素…

初出茅庐的小李博客之ESP8266获取自己B站粉丝数据

获取方式 ESP8266发起HTTP请求解析json数据 获取粉丝API: https://api.bilibili.com/x/relation/stat?vmid349513188API浏览器测试返回结果 {"code": 0,"message": "0","ttl": 1,"data": {"mid": 349513188, …

Python+Appium实现自动化测试

一、环境准备 1.脚本语言:Python3.x IDE:安装Pycharm 2.安装Java JDK 、Android SDK 3.adb环境,path添加E:\Software\Android_SDK\platform-tools 4.安装Appium for windows,官网地址 Redirecting 点击下载按钮会到GitHub的…

【safetensor】介绍和基础代码

Hugging Face, EleutherAI, StabilityAI 用的多 介绍 文件形式 header,体现其特性。如果强行将pickle或者空软连接 打开,会出现报错。解决详见:debug 连接到其他教程结构和参数 安装 with pip:Copied pip install safetensors with con…

阶段六-Day05-MyBatis3

一、多表查询(面试题) 1. 介绍 多表查询是在企业中必不可少的,无论多么简单的项目里通常会出现多表查询的操作。因为只要是关系型数据库,在设计表时都需要按照范式进行设计,为了减少数据冗余,都会拆成多个…

String、StringBuffer、StringBuilder 适合的应用场景

文章目录 String适用场景示例代码 StringBuffer适用场景示例代码 StringBuilder适用场景示例代码 性能比较总结 🎉欢迎来到Java学习路线专栏~String、StringBuffer、StringBuilder 适合的应用场景 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页&#xff…

《视觉SLAM十四讲》公式推导(二)

CH3-5 四元数表示旋转 三维空间中任意点均可用一个纯虚四元数表示即 p [ 0 , v ] T \boldsymbol{p}[0,\boldsymbol{v}]^T p[0,v]T,经一个单位四元数 q \boldsymbol{q} q的旋转后,得到 p ′ \boldsymbol{p} p′,则 p ′ q p q − 1 (3-5-1)…

STM32F4_中英文显示

目录 1. 液晶显示逻辑 2. 汉字显示原理 3. 实验程序 3.1 main.c 3.2 text.c 3.3 text.h 3.4 fontupd.c 3.5 fontupd.h 1. 液晶显示逻辑 字符编码: 由于计算机只能识别 0 和 1,文字也只能以 0 和 1 的形式在计算机里存储,所以我们需要…

算法通过村第十六关-滑动窗口|青铜笔记|滑动很简单

文章目录 前言滑动窗口的基本思想入门题目练习子数组最大平均数最长连续递增序列 总结 前言 提示:我宁愿做自己,做卑微的自己,也不愿做别人,无论那会多么快乐。 --《美丽新世界》 我们在数组和链表的部分就已经接触到了双指针的思…

IMX6ULL开发——第一个驱动程序

实现第一个应用程序&#xff1a;在IMX6ULL开发板上运行驱动程序hello_drv_test hello_drv_test #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h>/** ./hel…

生产者消费者模式(c++实现)

生产者消费者模式思路就是&#xff1a;一批专门生产资源的线程 和一批专门处理资源的线程以及一个线程安全的任务队列组成的 。并且当任务队列满的时候阻塞生产线程&#xff0c;任务队列为空的时候阻塞消费线程。 要实现一个生产者消费者队列 1。需要实现线程同步&#xff0c;…

react的setState做了什么

1、为什么需要setState setState的作用是帮助我们更改数据的同时并且通知视图进行渲染。因为React并不会绑定视图和state&#xff0c;需要我们手动去更新视图。 2、setState什么时候是同步的&#xff0c;什么时候是异步的 setState这个方法在调用的时候是同步的&#xff0c;…

如果面试问到你redis的常用数据类型,你怎么和面试官聊上十分钟?

最近组长把一些简历推到我这边让我帮他面试几份&#xff0c;问到这种基础题目时收到的回答总是不太理想 1、最简单的回答&#xff1a; Redis存储的是key-value结构的数据&#xff0c;其中key是字符串类型&#xff0c;value有5种常用的数据类型&#xff1a; 字符串 string哈希 …

为什么spring默认采用单例bean

概 述 熟悉 Spring开发的朋友都知道 Spring 提供了 5种 scope&#xff0c;分别是&#xff1a; singleton: 单例模式&#xff0c;当spring创建applicationContext容器的时候&#xff0c;spring会欲初始化所有的该作用域实例&#xff0c;加上lazy-init就可以避免预处理&#xf…

磁盘分区如何分? 电脑磁盘分区免费软件指南!

列出并比较顶级免费磁盘分区管理器软件&#xff0c;以选择适用于 Windows 的最佳分区软件&#xff1a; 系统分区在现代计算机设备中起着非常重要的作用。它们可以存储数据&#xff0c;使系统文件远离用户数据&#xff0c;并在同一台设备上安装多个操作系统。但是&#xff0c;这…

MyBatis-Plus 实战教程一

这里写目录标题 简介快速上手数据库建立创建实体类修改参数引入依赖测试常见注解介绍TableNameTableIdTableField 常见配置仓库地址 简介 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;…

qt-C++笔记之信号与槽

qt-C笔记之信号与槽 code review! 本文抄自公众号&#xff1a;嵌入式小生 文章目录 qt-C笔记之信号与槽一.信号2.1.信号的发出2.2.信号的处理 二.槽函数2.1.带有默认参数的信号和槽函数2.2.使用QObject::connect()将信号连接到槽函数的三种方法2.2.1.第一种方法&#xff1a;使…

整理MongoDB文档:身份验证

整理MongoDB文档:身份验证 个人博客&#xff0c;求关注。 文章概叙 本文主要讲MongoDB在单机状态下的账户配置。理解了MongoDB的语法&#xff0c;对于如何配置用户权限会知道怎么配置&#xff0c;但是请注意给谁配置什么权限才是最重要的。 最小权限原则 系统的每个程序或者…