LVGL V0.01版本移植到STM32F4
目录
LVGL简介
LVGL特点
LVGL运行的硬件要求
LVGL相关网址
LVGL源码下载
LVGL移植需求
LVGL移植过程
添加源码
添加源码头文件路径
修改堆栈大小
C99的支持
修改工程
编译工程
更改lv_conf文件
更改lv_port_disp_template.c接口文件
修改 lv_conf.h
显示实现
实验结果
LVGL简介
LittlevGL是一个免费的开源图形库,提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素、漂亮的视觉效果和低内存占用。
LVGL特点
- 强大的构建模组:按钮、图表、列表、滑块、图像等
- 先进的图形:动画、反锯齿、半透明、平滑滚动
- 多样的输入设备:触摸板、鼠标、键盘、编码器等
- 多显示器支持:支持同时使用多个TFT或单色显示器
- 多语言支持:格式文字编码
- 硬件无关:可用于任UTF-8意微控制器或显示器
- 可裁剪:用于小内存(64 KB FLASH,16 KB RAM)操作
- 外部支持:操作系统、外部存储以及GPU
- 单帧缓存:即可实现先进的图形效果
- C语言编写:以最大化兼容(C++ 兼容)
- 模拟器:无需嵌入式硬件就可以在电脑上开始GUI设计
- 文档:在线及离线
- 免费开源:基于MIT协议
LVGL运行的硬件要求
名称 |
最小值 |
推荐值 |
Arch |
>= 16位的微控制器或处理器 |
|
Clock |
> 16 MHz 时钟速度 |
|
Flash/ROM |
> 64 kB |
> 180 kB |
Static RAM |
~2 kB,取决于使用的功能和对象类型 |
|
Stack |
> 2 kB |
> 8 kB |
Heap |
> 4 KB |
> 32 kB |
Compiler |
C99或以上 |
LVGL相关网址
- LVGL官网:LVGL - Light and Versatile Embedded Graphics Library
- LVGL代码库地址(Simulator、Source、Examples、Esp32、MicroPython):https://github.com/lvgl
- LVGL源码下载地址:https://github.com/lvgl/lvgl
LVGL源码下载
- 在源码链接中下载一份源码,LVGL已经更新迭代了很多个版本,这里我们选择8.0.1版本来移植。
注意:不同版本之间有很大的不同,尽量使用与本文相同的版本,否则不予解释。
3. 下载后得到如下目录结构
4. 源码各文件作用
- github:github的配置文件,忽略即可。
- docs:说明文档。
- examples:示例代码。
- scripts:配置脚本,Linux平台会用到,Windows忽略即可。
- src:源代码。
- tests:测试代码。
- zephyr:忽略即可。
LVGL移植需求
STM32工程,该工程应具备功能
- LCD显示(必备)
- TOUCH触摸(可选)
- DMA刷屏(可选)
- 内存管理(可选)
- 操作系统(可选)
注意:本示例中使用1.3寸TFTLCD,分辨率为240*240,使用SPI驱动。采用DMA双缓冲刷屏。
LVGL移植过程
添加源码
- 在工程目录下创建LVGL文件夹,如下图所示
2. 将LVGL的源码目录(lvgl-8.0.1\src)复制到工程中的LVGL目录中
3. 将LVGL的驱动接口文件目录(lvgl-8.0.1\examples\porting)复制到本工程中的LVGL文件夹中。其 中共六个文件,disp为显示接口驱动文件(LCD),fs为文件系统接口驱动文件(FATFS),indev为输入设备接口驱动文件(TOUCH)。
4. 将LVGL源码中(lvgl-8.0.1)的lv_conf_template.h更名为lv_conf.h,随后将其与lvgl.h一同拷贝到本地工程中的LVGL目录下。
完成后LVGL目录下的文件如下图所示:
5. 打开工程,在工程的目录结构中添加二个文件夹LVGL_SRC和LVGL_PORT,并把源码添加到其中。(可根据需要进行裁剪)。
添加src目录下的文件如下图:
添加porting目录文件如下图
lv_port_disp_template.c:显示接口文件
lv_port_indev_template.c:输入设备接口文件
本示例只使用lv_port_disp_template.c这个显示的模板。
添加源码头文件路径
修改堆栈大小
如果工程中没有内存管理,则需要修改启动文件中的堆栈。根据官方推荐我们可以把堆栈修改为4K,假如使用的功能比较多,还需要再适当增大。
C99的支持
LVGL的源码需要C99的支持,否则编译无法通过。
修改工程
编译工程
出现错误:
双击错误定位到错误位置发现,该代码包含的头文件路径层级错误,根据目录层级修改。(原来是../../lv_conf,更改为../ lv_conf)
更改lv_conf文件
lv_conf的条件编译没有打开,去到此文件下打开该文件修改保存即可。
再次编译,已没有错误。
更改lv_port_disp_template.c接口文件
1. 默认lv_port_disp_template.c和lv_port_disp_template.h的条件编译是关闭的,我们需要把他打开并修改包含目录层级。
把头文件更改如下:
2. 更改lv_port_disp_templ.c 文件中lv_port_disp_init驱动函数。此函数提供了三种写缓存方式,保留其中一种即可,本示例采用方式二。更改如下。
3. 更改屏的大小。
4. 添加DMA实现代码,并在disp_init函数中调用DMA的初始化函数(如果不使用DMA,disp_flush函数中只需要保留LCD_Color_Fill、lv_disp_flush_ready这两个函数即可)。注意添加需要的头文件。如#include "lcd.h"
1)添加一个全局变量static lv_disp_drv_t *disp_drv_p;
2)把以下代码添加到lv_port_disp_template.c
/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Init
* 功能说明 : SPI3 DMA1初始化
* 形 参 : 无
* 返 回 值 : 无
* 备 注 : DMA1_Stream7搬运显示数据到SPI3的DR寄存器
*********************************************************************************************************/
static void DisPlay_SPI_DMA_Init()
{DMA_InitTypeDef DMA_InitStructure = {0};NVIC_InitTypeDef NVIC_InitStruct = {0};RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能DMA_DeInit(DMA1_Stream7);while(DMA_GetCmdStatus(DMA1_Stream7) != DISABLE) {} //等待DMA可配置/* 配置 DMA Stream */DMA_InitStructure.DMA_Channel = DMA_Channel_0; //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned int)&SPI3->DR; //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (unsigned int)buf_2_1; //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式DMA_InitStructure.DMA_BufferSize = sizeof(buf_2_2); //数据传输量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //使用普通模式DMA_InitStructure.DMA_Priority = DMA_Priority_High; //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不使用fifoDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //fifo全容量DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输DMA_Init(DMA1_Stream7, &DMA_InitStructure); //初始化DMA StreamSPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); // SPI3使能DMA发送NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream7_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStruct);DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);DMA_Cmd(DMA1_Stream7, DISABLE);
}
/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Enable
* 功能说明 : 配置DMA并启动一次传输
* 形 参 : buf:需要搬运的数据的指针;size:搬运的数据量
* 返 回 值 : 无
* 备 注 : 无
*********************************************************************************************************/
void DisPlay_SPI_DMA_Enable(void *buf, unsigned int size)
{DMA1_Stream7->CR &= ~(0x01);while((DMA1_Stream7->CR&0X1)){}DMA1_Stream7->M0AR = (unsigned int)buf;DMA1_Stream7->NDTR = size;DMA1_Stream7->CR |= (0x01);
}
/*********************************************************************************************************
* 函 数 名 : DMA1_Stream7_IRQHandler
* 功能说明 : DMA1_Stream7发送完成中断
* 形 参 : 无
* 返 回 值 : 无
* 备 注 : 无
*********************************************************************************************************/
void DMA1_Stream7_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7) != RESET)//if(DMA1->HISR & (1<<27)){DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);//DMA1->HIFCR |= (1<<27);LCD_CS = 1;SPI3->DR; lv_disp_flush_ready(disp_drv_p); /* tell lvgl that flushing is done */}
}
5. 在disp_init函数中调用DMA的初始化函数。
6. 修改disp_flush函数如下
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{unsigned int size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;disp_drv_p = disp_drv;LCD_Address_Set(area->x1, area->y1, area->x2, area->y2+1); LCD_CS = 0;DisPlay_SPI_DMA_Enable(color_p, size);
}
lv_port_disp_templ.c 文件最终更改后如下:
#if 1
#include "lv_port_disp_template.h"
#include "lcd.h"#define MY_DISP_HOR_RES 240 //屏的大小
#define MY_DISP_VER_RES 240
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_VER_RES * 10]; /*An other buffer for 10 rows*/static lv_disp_drv_t *disp_drv_p;static void disp_init(void);static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Init
* 功能说明 : SPI3 DMA1初始化
* 形 参 : 无
* 返 回 值 : 无
* 备 注 : DMA1_Stream7搬运显示数据到SPI3的DR寄存器
*********************************************************************************************************/
static void DisPlay_SPI_DMA_Init()
{DMA_InitTypeDef DMA_InitStructure = {0};NVIC_InitTypeDef NVIC_InitStruct = {0};RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //DMA1时钟使能DMA_DeInit(DMA1_Stream7);while(DMA_GetCmdStatus(DMA1_Stream7) != DISABLE) {} //等待DMA可配置/* 配置 DMA Stream */DMA_InitStructure.DMA_Channel = DMA_Channel_0; //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (unsigned int)&SPI3->DR; //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (unsigned int)buf_2_1; //DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式DMA_InitStructure.DMA_BufferSize = sizeof(buf_2_2); //数据传输量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //使用普通模式DMA_InitStructure.DMA_Priority = DMA_Priority_High; //中等优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不使用fifoDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //fifo全容量DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输DMA_Init(DMA1_Stream7, &DMA_InitStructure); //初始化DMA StreamSPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); // SPI3使能DMA发送NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream7_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStruct);DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE);DMA_Cmd(DMA1_Stream7, DISABLE);
}
/*********************************************************************************************************
* 函 数 名 : DisPlay_SPI_DMA_Enable
* 功能说明 : 配置DMA并启动一次传输
* 形 参 : buf:需要搬运的数据的指针;size:搬运的数据量
* 返 回 值 : 无
* 备 注 : 无
*********************************************************************************************************/
void DisPlay_SPI_DMA_Enable(void *buf, unsigned int size)
{DMA1_Stream7->CR &= ~(0x01);while((DMA1_Stream7->CR&0X1)){}DMA1_Stream7->M0AR = (unsigned int)buf;DMA1_Stream7->NDTR = size;DMA1_Stream7->CR |= (0x01);
}
/*********************************************************************************************************
* 函 数 名 : DMA1_Stream7_IRQHandler
* 功能说明 : DMA1_Stream7发送完成中断
* 形 参 : 无
* 返 回 值 : 无
* 备 注 : 无
*********************************************************************************************************/
void DMA1_Stream7_IRQHandler(void)
{if(DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7) != RESET)//if(DMA1->HISR & (1<<27)){DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);//DMA1->HIFCR |= (1<<27);LCD_CS = 1;SPI3->DR; lv_disp_flush_ready(disp_drv_p); /* tell lvgl that flushing is done */}
}void lv_port_disp_init(void)
{disp_init();static lv_disp_draw_buf_t draw_buf_dsc_2;lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); static lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); disp_drv.hor_res = MY_DISP_HOR_RES;disp_drv.ver_res = MY_DISP_VER_RES;disp_drv.flush_cb = disp_flush;disp_drv.draw_buf = &draw_buf_dsc_2;lv_disp_drv_register(&disp_drv);
}static void disp_init(void)
{/*You code here*/DisPlay_SPI_DMA_Init();
}static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{unsigned int size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1) * 2;disp_drv_p = disp_drv;LCD_Address_Set(area->x1, area->y1, area->x2, area->y2+1); LCD_CS = 0;DisPlay_SPI_DMA_Enable(color_p, size);
}#else
typedef int keep_pedantic_happy;
#endif
修改 lv_conf.h
根据需求修改lv_conf.h中的宏定义
- LV_COLOR_DEPTH:屏幕的色彩深度,支持1bit、8bit、16bit、32bit。
- LV_COLOR_16_SWAP:字节交换,DMA刷屏的时候需要置1。
- LV_MEM_SIZE:GUI可支配的内存空间,根据使用的功能调节。
- LV_FONT_MONTSERRAT_*:字体大小,太小会很模糊。
如果需要更改默认字体大小,则把相应的宏设置为1.
显示实现
1. 配置一个定时器为LVGL提供1ms的时钟心跳,该定时器的中断服务函数中调用lv_tick_inc(1);即可。本示例使用基本定时器7.
2. 主程序中循环调用lv_task_handler函数处理事件
3. 编写显示程序
#include "lvgl.h"
#include "lv_port_disp_template.h"
void Lvgl_Lable_Demo(void)
{lv_obj_t *scr = lv_scr_act();lv_obj_t * label1 = lv_label_create(scr);lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP); lv_label_set_recolor(label1, true); lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the lines to the center ""XYD NN."); lv_obj_set_width(label1, 150); lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0); lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40); lv_obj_t * label2 = lv_label_create(scr);lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_obj_set_width(label2, 150); lv_label_set_text(label2, "My Name is Chenxingxing. "); lv_obj_align(label2, LV_ALIGN_CENTER, 0, 40);
}
4. main函数调用
int main()
{…..Lcd_Init();lv_init(); // 初始化lvgllv_port_disp_init(); // 显示初始化Lvgl_Lable_Demo();….while(1){lv_task_handler();Delay_Ms(1);}
}
实验结果
LVGL V0.01版本移植到STM32F4相关推荐
- 开源证券交易撮合引擎 jOpenExchg V0.01 预览版
项目名称:Java Open Exchange Project (jOpenExchg / jopenexchg) 项目当前版本:V0.01 项目简介: 基于GPL Licence的Java的开源证券 ...
- Node.js v0.10版本发布
Node.js研发团队发布了node.js v0.10版本,它是个基于Javascript.用于构建高性能异步服务器的平台.该版本主要更新如下:更易于使用的数据流处理模块,通过域更好地处理错误,此外还 ...
- Nacos发布 v0.2 版本,无缝支持 Spring Cloud 微服务生态及高可用集群模式
2019独角兽企业重金招聘Python工程师标准>>> 近日,阿里巴巴新开源项目Nacos 发布了 v0.2 版本,该版本开始支持完整的Spring生态技术栈,这包括 Spring ...
- linux运维面板_phpstudy linux web面板(小皮面板)V0.2版本正式发布
小皮面板是什么? 小皮面板,是由phpStudy官方团队针对Linux服务器开发推出的一款服务器环境搭建以及管理工具. 可以通过Web端方便.快速的搭建和管理服务器环境,并加入了强大的安全防护功能,提 ...
- 开源开放 | 图数据交互可视化分析框架 InteractiveGraph v0.3 版本发布
图数据交互可视化分析框架 InteractiveGraph 日前发布 v0.3 版本,下载地址:https://github.com/grapheco/InteractiveGraph/release ...
- 巨高兴,自己的“万能数据库查询分析器”中英文 3.01版本 已经在国内6大软件下载网站发布
几个月前开始构思,寻思着从"万能数据库查询分析器"中英文 3.01 版本开始起,一方面,去除原来试用31天的时间限制,用户永久试用:另一方面,尝试提供免费注册的方式,只要用户协助完 ...
- php 开发 web面板,phpstudy linux web面板(小皮面板)V0.2版本正式发布
小皮面板是什么? 小皮面板,是由phpStudy官方团队针对Linux服务器开发推出的一款服务器环境搭建以及管理工具. 可以通过Web端方便.快速的搭建和管理服务器环境,并加入了强大的安全防护功能,提 ...
- 图数据交互可视化分析框架InteractiveGraph v0.3版本发布
图数据交互可视化分析框架 InteractiveGraph日前发布v0.3版本,下载地址:https://github.com/grapheco/InteractiveGraph/releases/t ...
- 2012 12 02 FL2440开发板的U-boot-2010.09版本移植(二)片上系统SoC初始化移植
-------------------------------------------------------- 在"<2012 10 02> FL2440开发板的U-boot- ...
最新文章
- 2020年春季学期信号与系统课程作业参考答案-第十四次作业
- android studio 插件开发 FindByTag插件 局部情况下取代ButterKnife插件
- jQuery遍历之next()、nextAll()方法使用实例
- PE关于导入表(IAT)知识复习
- uefi多linux系统启动盘,DIY制作无需格BIOS+UEFI双启动U盘工具|支持syslinux+grub+boomgr+grub2多启动...
- cmake 编译安装方法
- Linux文本处理tr命令笔记
- 对 app_offline.htm 的几点错误认识
- Zookeeper - zookeeper安装与配置
- [转帖]从 2G 到 5G,手机上网话语权的三次改变
- 五子棋软件测试自学,初学者如何从零开始自学五子棋
- 可视化html css布局,一个开源可视化布局项目,在线生成纯css布局,可阅读的代码...
- 2022软件测试技能 Apache JMeter 性能测试从入门到精通
- 怎么注册quora?
- python列表输出序号_Python中打印列表的序号和内容
- SCI投稿:MDPI旗下期刊Mathematics投稿经历
- MacBook重启之后,外接显示器不亮的解决方法
- 建筑群子系统的设计步骤
- Ext js 4 全选和反选
- Java如何实现定时任务?
热门文章
- 动规(11)-踩方格
- 几何变换详解:平移、缩放、旋转
- Syscall的实现
- 【微信小程序+echarts点亮中国地图】微信小程序echarts中国地图点亮功能
- python os.urandom()函数和十六进制\xhh的一些认识
- 吐血整理一个月——终于把所有Python库整理齐了.....
- java编程电话号码查询_java课程设计电话号码查询系统(15页)-原创力文档
- AG9311/AG9310 Type-C转HDMI设计方案|替代AG9310/AG9311芯片|GSV2201可完全替代兼容AG9310/AG9311
- 从0开始建设saas - 系统对接中的问题整理
- Python基本数据类型--列表