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);//恢复systickHAL_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和ydata->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和ydata->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屏幕上显示触摸屏的坐标值的语句删除

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

TFT-LCD移植LVGL详细过程记录相关推荐

  1. VS调试dll详细过程记录

    VS调试dll详细过程记录 还可以参考博客: https://blog.csdn.net/u014738665/article/details/79779632 在我们写的程序中有时候调用dll,并且 ...

  2. wintogo详细过程记录

    wintogo详细过程记录 大概步骤 详细过程 大概步骤 下载win10镜像 用DAEMON Tools Lite软件加载win10镜像 用wintogo软件制作win10系统 安装启动win10系统 ...

  3. arduino tft 方向_ESP32在Arduino环境下玩转 LVGL,ESP32移植LVGL详细教程

    微信关注 "DLGG创客DIY"设为"星标",重磅干货,第一时间送达. ❝ 转载自慕容流年 https://me.csdn.net/qq_41868901 ❞ ...

  4. STM32 移植FreeModbus详细过程

    modbus是一个非常好的串口协议(当然也能用在网口上),它简洁.规范.强大.可以满足大部分的工业.嵌入式需求.我写的这个四个寄存器都可以用(输入寄存器.保持寄存器.线圈寄存器.离散寄存器).不像别的 ...

  5. Ubuntu搭建DTN2仿真平台(详细过程记录)

    基于Ubuntu搭建DTN2仿真平台 软件安装 软件下载 安装过程 基础运用 软件安装 编译安装 dtn2 之前要保证 gcc 编译器是 3.3 版本以上(但其实如果用低于4.9版本的gcc编译mak ...

  6. 2440移植Mplayer详细过程(最简便的方法)以及报错解决

    移植环境(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容) 1,开发板:韦东山JZ2440 2,linux 版本:linux-3.4.2 3,系统版本:Ubuntu9.10 4,交叉编译环境:ar ...

  7. 在Windows 10(Win10)下安装“NVIDIA图形驱动程序”、“NVIDIA控制面板”、CUDA Toolkit、cuDNN Archive的详细过程记录

    目标:安装CUDA Toolkit和cuDNN Archive 目录 一. 安装NVIDIA驱动程序和NVIDIA 控制面板 二.安装CUDA Toolkit 三.安装cuDNN Archive 一. ...

  8. STM32F767ZI-NUCLEO移植运行micropython过程记录

    注意,本教程移植microPython是通过烧写hex文件实现的,网上其他教程很多是介绍使用USB DFU方式(设备boot0至高电平,通过DfuSeDemo烧写),由于自己还不熟没有使用这种方式,后 ...

  9. Houdini湖边小屋-屋顶细分详细过程记录

    b站UP主七里雪凝的湖边小屋教程--P3/P4[Houdini]萌新的<湖边小屋>教程拆解与实现,殊途同归!_哔哩哔哩_bilibili 目录 内容概括 详细步骤 1 [Facet节点]让 ...

最新文章

  1. 微信朋友圈里的十五种类型,笑死了!
  2. 【剑指offer-Java版】33把数组排成最小的数
  3. python自学什么书比较好-有什么好的自学 Python 的书籍推荐?
  4. 对页面制定区域进行打印,以及打印不显示页脚URL的方法
  5. Codeforces 920D Tanks (看题解)
  6. 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之完成登录功能...
  7. easyUI 添加CheckBox选择到DataGrid
  8. 学习组合模式,转载一段有关组合模式的详解
  9. SpringMVC框架----SessionAttribute注解
  10. https://en.wikipedia.org/wiki/Linux Foundation
  11. 输入三角形的三c语言程序,输入三角形的三边 a,b,c,计算三角形的面积的公式是 C++...
  12. springcloud配置负载均衡 及方式_Springcloud-Ribbon负载均衡NODO
  13. wsdl文件 服务器地址,webservice 的wsdl文件详解
  14. SL8530A DC-DC 2.6V~100V宽输入电压升压型LED面板RGB调光恒流驱动芯片
  15. P1004 方格取数
  16. 高通---IGV:从安装到使用
  17. 2020,好看视频的创作生态棋局
  18. @03Python基础语法
  19. 防沉迷与身份证系统挂钩 网游要实名认证
  20. 我国火箭回收历程介绍

热门文章

  1. JVM系列-第5章-堆
  2. 如何提高网站ip流量
  3. 四川对口高考计算机本科收分,四川对口升学高考分数线
  4. C语言操作EXCEL文件(读写)
  5. linux中vfs和fs区别,解析Linux中的 VFS 文件系统机制(rootfs)一
  6. 内容推荐算法简单实现(余弦公式)
  7. 赴菲律宾流程简介(首次)
  8. 蒋欣代言国民好燕麦品牌西麦,携手麦出欣姿态
  9. Codeforces Round #634 (Div. 3)ABCD
  10. 301与302的区别,301的坑