TFT-LCD移植LVGL详细过程记录

news2025/1/15 6:51:52

TFT-LCD移植LVGL

LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用。

LVGL更多介绍:https://zhuanlan.zhihu.com/p/406294618

本次实验将LVGL移植到STM32F103ZET6中,并编译通过,记录一下移植过程

在CubeMX中修改栈大小

因为LVGL要求MCU的堆栈最少是2K,2K = 2048 = 0x800,所以要在CubeMX中修改堆栈大小,为什么不直接在代码中修改呢,因为在代码中修改的话,而CubeMX中没有修改,下一次用CubeMX生成工程后又会覆盖掉代码中修改的地方,所以需要在CubeMX中修改

使用上次TFT-LCD触摸的文件,打开后在下图位置中修改,最小堆栈大小设置为0x800,然后生成工程代码

在这里插入图片描述

在keil工程里也可查看修改的位置

在这里插入图片描述

在keil中选择C99模式

LVGL要求开启C99模式,在C/C++选项中勾选即可

在这里插入图片描述

去官网或Github下载LVGL的源码和例程

官网地址:https://lvgl.io/

Github下载地址:https://github.com/lvgl

打开会比较慢,多试几次

在这里插入图片描述

在这里插入图片描述

下载好源码和例程,这里用V7版本的

在这里插入图片描述

建立LVGL的文件夹

LVGL官方代码里是相对路径,所以一定要按步骤建好文件夹

在工程目录下新建一个GUI的文件夹

在这里插入图片描述

GUI文件夹里再建三个子文件夹

在这里插入图片描述

lvgl:放源码,将下载的源码 lvgl-release-v7压缩包复制到该文件夹,并解压缩

在这里插入图片描述

lvgl_driver:放显示和触摸的驱动

在解压完的源码文件中,有个examples的文件夹,点击打开

在这里插入图片描述

再打开porting文件夹

在这里插入图片描述

这些就是显示和触摸的驱动文件

在这里插入图片描述

将显示和触摸的.c和.h文件拷贝到 lvgl_driver文件夹中,因为没有用到文件系统,所以可以不用拷贝,需要将文件名的_template去掉,不然后面加入keil工程后编译会出错

在这里插入图片描述

lvgl_example:放例程,将官方例程 lv_examples-release-v7复制到该文件夹并解压缩

在这里插入图片描述

将配置文件剪切到GUI根目录

打开lvgl的文件夹,找到 lv_conf_template.h文件

在这里插入图片描述

剪切粘贴到GUI文件夹的目录下,并修改文件名,将_template去掉

在这里插入图片描述

然后打开 lv_conf.h 文件,修改预编译选项,将0改为1,然后保存

在这里插入图片描述

打开 lvgl_examples 的文件夹,找到 lv_ex_conf_template.h 文件

在这里插入图片描述

剪切粘贴到GUI文件夹的目录下,同样要修改文件名,将_template去掉

在这里插入图片描述

同理,将 lv_ex_conf.h 的预编译选项的0改为1,保存

在这里插入图片描述

至此,LVGL的工程文件夹已经创建好

在这里插入图片描述

keil工程创建lvgl的文件夹

在这里插入图片描述

打开lvgl->src,将下面文件夹里的.c文件全都添加到keil工程的lvgl文件夹中

在这里插入图片描述

添加完的lvgl文件夹如下

在这里插入图片描述

然后编译一次,没有出现错误,发现有警告,这些警告是源码里的,尽量不要去修改源码

在这里插入图片描述

在这里插入图片描述

发现是111的警告,可以在keil里设置,屏蔽这些警告,需要在C/C++选项中的下图位置添加这条语句:–diag_suppress=111

在这里插入图片描述

然后再次编译,发现没有出现警告

在这里插入图片描述

修改配置文件

打开lv_disp.c源文件,找到 lv_conf.h文件,这个就是配置文件,要修改屏幕的最大分辨率,因为手上屏幕是240x320的,所以水平分辨率改为240,垂直分辨率保持默认

在这里插入图片描述

往下一点就是颜色格式设置和总线位数设置,颜色格式默认RGB565的,所以不用改,手上屏幕是16位总线的,所以总线位数也不用改,这需要根据具体屏幕硬件来设置

在这里插入图片描述

LVGL显示界面需要内存,如果显示的东西多,则需要的内存就大,下面这里就是设置分给LVGL内存的大小,如果显示的内容多,则32可改为其他值,这里使用默认

在这里插入图片描述

191行的是设置是否使用GPU,默认是1开启,改为0,选择不使用

在这里插入图片描述

211行是设置是否使用文件系统,默认是1使用,改为0,选择不使用

在这里插入图片描述

最后编译一次,没有错误则进行下一步

定时器中断回调函数中调用 LVGL 心跳函数 lv_tick_inc

首先包含lvgl的头文件路径

在这里插入图片描述

在MyApplication.h头文件中添加 lvgl.h 头文件

在这里插入图片描述

在定时器中断回调函数中调用lvgl的心跳函数 lv_tick_inc(),定时器每隔一定时间就调用该函数,控制 lvgl 刷新界面,lv_tick_inc函数需要传入参数,参数就是定时器定时时间,比如定时5ms,那就传入5,定时1ms,那就传入1

在这里插入图片描述

修改显示驱动

在keil工程中新创建一个驱动的文件夹 lvgl_driver,并添加显示驱动源文件 lv_port_disp.c,

在这里插入图片描述

打开lv_port_disp.c文件,修改预编译选项,0改为1,修改引入头文件的名称,lv_port_disp_template.h 改为 lv_port_disp.h

在这里插入图片描述

打开 lv_port_disp.h头文件,预编译0改为1,"lvgl/lvgl.h"改为 “…/lvgl/lvgl.h”,编译器才能找到该头文件路径

在这里插入图片描述

再回到 lv_port_disp.c 源文件中,找到 disp_init() 函数,这里可以放自己写的TFT-LCD屏幕驱动

在这里插入图片描述

当然使用自己的函数需要引入对应的头文件

在这里插入图片描述

执行完disp_init函数后,会进行缓存的定义,lvgl给出了三种定义的方法,如下图,方法1最简单,方法2使用了双重缓存,方法3是根据屏幕大小定义缓存,这次使用比较简单的第1种方法,把另外两种注释掉即可

第一种方法中的缓存大小 LV_HOR_RES_MAX * 10 改为 LV_HOR_RES_MAX * LV_HOR_RES_MAX / 10,设置缓存大一点

在这里插入图片描述

再往下就是设置当前屏幕的尺寸大小的,将水平的480改为240,跟屏幕匹配;前面源码里设置的是屏幕最大的分辨率,这里设置的尺寸大小不能大于前面设置的最大分辨率

在这里插入图片描述

接下来修改disp_flush函数,下面是该函数没有被修改过的,可以看出该函数的功能就是设置一个窗口,然后往窗口里写入像素点的值,写入的操作默认被注释掉了,写完一个像素点后,像素点指针加1,继续写下一个像素点

/* 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)
{
    /*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++;						   //像素点指针加1
        }
    }

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

修改后的disp_flush函数如下,挂起STM32的systick时钟是为了提高GUI的刷新速度,在写完像素点数据后,再开启systick时钟

/* 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)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

    uint16_t x,y;
	
	//挂起systick,提高GUI刷新速率
	HAL_SuspendTick();
	
	//设置窗口,参数:X轴起始位,Y轴起始位,长度,宽度
	TFT_LCD.LCD_SetWindows(area->x1,area->y1,area->x2-area->x1+1,area->y2-area->y1+1); 
	
    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)*/
		   LCD_Write_DATA(color_p->full);		//调用函数写入像素点数据
            color_p++;
      }
    }

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
		
	//恢复systick
	HAL_ResumeTick();
}

修改触摸驱动

在lvgl_driver文件夹中,添加触摸驱动源文件 lv_port_indev.c

在这里插入图片描述

然后与显示驱动一样,修改源文件的预编译选项,修改引入头文件的名称

在这里插入图片描述

lv_port_indev.h头文件也是修改预编译选项,修改引入的头文件路径

在这里插入图片描述

然后回到 lv_port_indev.c源文件中,里面是一些功能的驱动函数,如触摸、鼠标、键盘、编码器和按钮功能,需要什么功能根据实际情况选择,本次移植使用简单点的触摸功能,其他功能函数都可删掉

在这里插入图片描述

修改后的lv_port_indev.c源文件,用lvgl自带的初始化函数,然后对touchpad_read函数进行修改

/**
 * @file lv_port_indev_templ.c
 *
 */

 /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev.h"
#include "MyApplication.h"

static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

void lv_port_indev_init(void)
{
    /* Here you will find example implementation of input devices supported by LittelvGL:
     *  - Touchpad
     *  - Mouse (with cursor support)
     *  - Keypad (supports GUI usage only with key)
     *  - Encoder (supports GUI usage only with: left, right, push)
     *  - Button (external buttons to press points on the screen)
     *
     *  The `..._read()` function are only examples.
     *  You should shape them according to your hardware
     */

    lv_indev_drv_t indev_drv;

    /*------------------
     * Touchpad
     * -----------------*/

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    lv_indev_drv_register(&indev_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/* Will be called by the library to read the touchpad */
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
	//当前坐标
    static uint16_t last_x = 0;
	static uint16_t last_y = 0;
	
	//如果触摸导致坐标更新
	if(Touch.Touch_Flag == TRUE)
	{		
		Touch.Touch_Flag = FALSE;
		
		//把新坐标Touch.LCD_X和Touch.LCD_Y赋给lvgl的坐标结构体的x和y
		data->point.x = Touch.LCD_X;
		data->point.y = Touch.LCD_Y;
		//更改状态,lvgl获取坐标
		data->state = LV_INDEV_STATE_PR;
		
		//更新当前坐标
		last_x = data->point.x;
		last_y = data->point.y;
	}
	else	//如果没有触摸
	{
		//把上一次坐标赋给lvgl坐标结构体的x和y
		data->point.x = last_x;
		data->point.y = last_y;
		//更改状态,lvgl获取坐标
		data->state = LV_INDEV_STATE_REL;
	}
	
	return false;
}

#else /* Enable this file at the top */

/* This dummy typedef exists purely to silence -Wpedantic. */
typedef int keep_pedantic_happy;
#endif

系统运行主函数中判断屏幕是否被触摸,如果触摸了,则更新标志位

/*
	* @name   Run
	* @brief  系统运行
	* @param  None
	* @retval None      
*/
static void Run()
{		
	//获取坐标板坐标
	if(Touch.Scan() == TRUE)
	{
		//通过该标志位知道屏幕是否被触摸更新坐标
		Touch.Touch_Flag = TRUE;
	}
}

包含lvgl的头文件

在自己工程的公共头文件MyApplication.h中添加lvgl的头文件

在这里插入图片描述

在设置中添加头文件路径,包括源文件路径和驱动路径

在这里插入图片描述

显示驱动和触摸驱动都是有初始化函数的,但它们的头文件都没有声明初始化函数,如果要调用这些函数的话是会有警告的,所以要先进行声明

在这里插入图片描述

在这里插入图片描述

在自己的初始化函数中调用lvgl的初始化函数lv_init(),显示驱动函数lv_port_disp_init(),触摸驱动函数lv_port_indev_init()

在这里插入图片描述

把前面触摸屏扫描函数中在LCD屏幕上显示触摸屏的坐标值的语句删除
在这里插入图片描述

移植基本完成,进行编译,没有报错

在这里插入图片描述

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

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

相关文章

第六章 图论 16 AcWing 1558. 加油站

第六章 图论 16 AcWing 1558. 加油站 原题链接 AcWing 1558. 加油站 算法标签 图论 最短路 枚举 思路 枚举加油站位置&#xff0c;对于每个加油站位置进行dijkstra&#xff0c;选择符合要求1的最小距离最大值&#xff08;要求2&#xff09;与的距离和最小值&#xff08;要…

(九)Java算法:快速排序(详细图解)

目录一、前言1.1、概念1.2、算法过程二、maven依赖三、流程解析3.1、全部数据分区3.2、左边数据分区3.3、右边数据分区四、编码实现结语一、前言 1.1、概念 快速排序&#xff1a;用数组的第一个数作为基准数据&#xff0c;然后将所有比它小的数都放到它左边&#xff0c;所有比…

使用 Spring Boot 设置 Hibernate Envers

Hibernate Envers是一个实现持久实体的审核和版本控制的模块。审计和版本控制是构建生产级Spring 启动微服务的关键组件。Hibernate Envers与Spring Boot无缝集成以实现相同的目标。 在这篇文章中&#xff0c;我们将在我们的Spring Boot Starter应用程序中集成Hibernate Envers…

生信工作流框架搭建 | 02-nextflow 实战

目录生信工作流框架搭建 | 02-nextflow前情提要开始使用依赖安装核心概念一个fastqc的示例&#xff0c;加深理解快速搭建你的程序你需要仔细阅读的&#xff1a;可以快速浏览&#xff08;但需要知道大概有什么&#xff0c;以便后来查览&#xff09;&#xff1a;报错&#xff01;…

IPD-需求管理流程

一、产品需求管理模型 在确定客户需求时,要考虑影响用户购买标准的八类基本需求($APPEALS),并基于客户视角进行详细分解,形成有针对性的产品。 1.1、需求管理业务流程 二、需求收集流程 2.1、需求收集的来源 路标规划:通过市场管理流程分析,落实到路标规划中的需求…

基于Paddle的手写数字识别模型

百度飞桨(paddlepaddle)是百度的开源深度学习平台&#xff0c;今天就利用paddle来编写入门级的手写数字模型&#xff0e; 一&#xff0c;准备数据 下载数据集&#xff0c;这里我们使用的是MNIST数据集 # 下载原始的 MNIST 数据集并进行解压 wget https://paddle-imagenet-mode…

12.数组的初始化和引用

数组的初始化 定义数组的时候&#xff0c;顺便给数组的元素赋予初值&#xff0c;即开辟空间的同时并且给数组元素赋值 一维数组的初始化 a. 全部初始化 int a[5] {2,4,7,8,5}; 代表的意思&#xff1a;a[0] 2 , a[1] 4 , a[2] 7 , a[3] 8, a[4] 5; b. 部分初始化 int …

Clever Internet Suite for Delphi, C++Builder

为Internet应用程序添加即时SSL/TLS安全性&#xff0c;并实现许多有用的Internet相关功能。 聪明的互联网套件允许您添加下载、上传和提交互联网资源;发送和接收MIME消息;HTTP、FTP、SMTP、POP3、IMAP和NNTP客户端/服务器解决方案;带有数字证书的SSL/TLS通道支持您的VCL应用程序…

电脑分辨率怎么调?电脑分辨率怎么调合适

​无论是笔记本电脑的用户&#xff0c;还是说台式电脑的用户&#xff0c;在使用电脑的时候&#xff0c;如果电脑分辨率调整的不对&#xff0c;很容易造成显示与观感方面的模糊。电脑分辨率怎么调&#xff1f;电脑分辨率怎么调最佳&#xff1f;本篇文章&#xff0c;小编就来教教…

ASEMI肖特基二极管1N5822参数,1N5822特征,1N5822应用

编辑-Z ASEMI肖特基二极管1N5822参数&#xff1a; 型号&#xff1a;1N5822 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;40V 最大RMS电桥输入电压&#xff08;VRMS&#xff09;&#xff1a;28V 最大直流阻断电压&#xff08;VDC&#xff09;&#xff1a…

三、简单了解kafka设计原理

系列文章目录 文章目录系列文章目录一、Kafka核心总控制器Controller二、kafka高性能简单理解一、Kafka核心总控制器Controller 在Kafka集群中会有一个或者多个broker&#xff0c;其中有一个broker会被选举为控制器&#xff08;Kafka Controller&#xff09;&#xff0c;它负责…

[ZJCTF 2019]Login--动态调试--详细版

前言 主要是因为太菜了&#xff0c;看了别人的exp&#xff0c;还是懵懵懂懂的&#xff0c;都是静态分析&#xff0c;不明白为会在改密码的时候会导致最后的getshell。今天给它动态分析整一个&#xff0c;看看到底哪里出错了。 基本原理 网上有很多介绍的&#xff0c;在这里说…

Linux学习——01 gcc编译器

一、程序构建过程 高级语言的代码无法被计算机执行&#xff0c;需要将高级语言代码编译成汇编语言&#xff0c;然后再将汇编语言翻译成机器指令&#xff0c;最后通过链接生成最后的可执行文件&#xff0c;此时该文件才可以被计算机执行。总共有四步&#xff1a; 1.1 预编译&a…

[02] BLEMotion-Kit 基于QMI8658传感器使用加速度计进行倾斜检测

文章目录1. 先修知识2. 原理&#xff08;单轴为例&#xff09;2.1 单轴倾斜2.2 双轴倾斜2.3 三轴倾斜1. 先修知识 2. 原理&#xff08;单轴为例&#xff09; 首先我们要知道的是&#xff1a;当目标轴(本例中为X轴)与地球表面平行时,传感器处于 0g 场。顺时针或逆时针旋转90 将…

springboot+java大学生西部计划志愿者岗位补助管理系统

本课题要求实现一套大学生西部计划管理系&#xff0c;系统主要包括系统个人中心、志愿者管理、岗位信息管理、补助信息管理、交流论坛、系统管理等功能模块。 为完善志愿者、岗位信息&#xff0c;应当建立健全志愿者的补助和管理机制&#xff0c;建立有效的激励机制&#xff0c…

Android Studio无法连接设备,一直显示Loading Devices...

不知道什么时候做了啥&#xff0c;从某个时间点之后&#xff0c;电脑就特别容易断开adb&#xff0c;有时候重启电脑都不管用。 一直显示"Loading Devices..."&#xff0c;拔插设备&#xff0c;重启Android Studio都没用&#xff0c;甚至重启电脑有时候也不行。 反正…

全部售罄!1,000 多个Sports Land NFT 在 24 小时内被抢空!

现在还来得及&#xff0c;抓紧时间&#xff01;&#x1f440; 在不到24小时的时间里&#xff0c;来自《Sports Land&#xff1a;足球爱好者》作品集&#xff08;2022 年 11 月 16 日发布&#xff09;的1000 多个可穿戴 NFT 已被售出&#xff01; 祝贺 Hermit Crab Game Studio …

bootstrap学习(一)

&#xff08;1&#xff09;bootstrap第一个程序 &#xff08;2&#xff09;bootstrap排版 &#xff08;1&#xff09;bootstrap第一个程序 创建boot文件夹方置bootstrap所需要的文件目录&#xff0c;拷贝过来 创建base目录&#xff0c;创建html页面&#xff1a; 引入css&#…

python复杂网络分析库NetworkX

文章目录1.Networkx简介2.图的类型&#xff08;Graphs&#xff09;3.图的创建&#xff08;Graph Creation&#xff09;4.图的属性&#xff08;Graph Reporting&#xff09;5.图算法&#xff08;Algorithms&#xff09;6.图的绘制&#xff08;Drawing&#xff09;7.数据结构8.图…

A股api交易接口文档怎么使用?

A股api交易接口是在股票量化交易中常用到的一种量化工具&#xff0c;对于它的用法&#xff0c;小编针对性的以文档的例子说明&#xff1a; 交易接口API 功能概述&#xff1a; 名称 功能 基本函数 Init API 初始化 Deinit API 反初始化 Logon 登录交易账户 Logoff 登…