本帖最后由 qiao--- 于 2024-1-18 01:30 编辑
前言
上期我们成功点亮了RGB_TFT屏幕,可以在这个屏幕上显示各种汉字和图形,但是手动的显示难免显示的图形界面有点不方便也有点麻烦,因为需要自己去找图形且设计,因此这次我们就来移植一下lvgl图形库。
首先来介绍一下LVGL,LVGL是一个开源的嵌入式图形库,用于创建图形用户界面(GUI)。它提供了丰富的图形元素和控件,可以在嵌入式系统上实现各种交互式用户界面。LVGL支持多种平台和显示设备,并具有高度可定制性和灵活性。它广泛应用于嵌入式系统、物联网设备、工业控制等领域。LVGL的全称是“LittlevGL”,是“Little Video Graphics Library”的缩写。
1.下载LVGL源码包
先给出lvgl的官网,源码包可以在lvgl的官方网站上下载,LVGL - Light and Versatile Embedded Graphics Library。我是从官网的github链接下载的,下载后的文件目录如下图所示
其中比较重要的就是src,因为里面装了lvgl的源码,我们移植的重头戏也是这个文件夹里面的。其他的一些文件夹就是lvgl的一些移植文档和应用示例,有兴趣的贴友可以翻阅一下。
打开src文件夹,可以看一下我们需要移植的src文件夹里面有什么,如下图所示。
经过我的查阅,lvgl中的这些目录包含的文件的作用如下图所示:
-
lv_core:这个子目录包含了LVGL的核心代码,例如事件处理、内存管理、对象管理等。这些代码是构建LVGL的基础,负责处理用户界面的基本功能。
-
lv_draw:这个子目录包含了LVGL的绘图引擎,负责在显示设备上绘制各种图形元素和控件。这些代码通常包括了像像素级别的绘制、颜色管理等功能。
-
lv_font:这个子目录包含了LVGL支持的字体库,用于在用户界面中显示文本。这些字体可以是固定宽度或变宽度的,可以支持不同的语言和字符集。
-
lv_hal:这个子目录包含了LVGL的硬件抽象层(HAL),负责与显示设备、输入设备等硬件进行交互。这些代码通常需要根据具体的硬件平台进行定制。
-
lv_misc:这个子目录包含了LVGL的一些辅助功能,例如颜色转换、文件操作等。
-
lv_objx:这个子目录包含了LVGL的各种控件和对象,例如按钮、标签、列表等。这些控件是用户界面的构建块,可以用来创建各种交互式界面。
在example文件夹下中其中的porting文件夹也是我们移植的重头戏,因为这个文件夹中包含着lvgl的设备输入和显示输入的一些相关文件。如下图所示
2.移植lvgl源码
打开我们之前的创建的RGB_TFT的工程。创建一个GUI的目录,在其下面创建一个lvgl文件夹并复制lvgl的源码和porting文件夹到这个文件夹内,将lvgl源码中的lv_conf_template.h文件改名为lv_conf.h复制到lvgl目录下。如下图。我把demos文件夹也复制过去了,方便移植lvgl自带的例程。
打开工程,将src文件夹的所有文件添加到工程中,将porting文件夹中的文件也添加到工程中。如下图所示
3.修改代码
将 lv_port_disp.c 、 lv_port_disp.h 、 lv_port_indev.c 、 lv_port_indev.h、 lv_conf.h中if 0改成 if 1,lv_port_fs.c暂时用不到,先不修改。
此时我们来编译一下原工程,发现有很多报错,我们逐一修改:
(1)将_template去掉,因为我们之前有修改过源文件
(2)将…/…/去掉
(3)将#include "../../lvgl.h"改成#include "../lvgl.h"
紧接着修改lv_port_disp.c文件,定义屏幕像素的长和宽
#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 160
修改刷屏函数
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
uint16_t x,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)*/
Gui_DrawPoint(x,y,lv_color_to16(*color_p));
color_p++;
}
}
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
紧接着我们编译一下就没有问题了,现在我们需要做的就是为lvgl添加时基,我是用的定时器6为lvgl添加的时基。我们写好定时器6的驱动时基代码如下:
#include "lvgl_timer.h"
#include "acm32g103_coreboard.h"
#include "lvgl.h"
TIM_HandleTypeDef TIM6_Handler;
//中断处理函数执行的内容
void HAL_TIM_Updeate_Event_Callback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
HAL_IncTick();
lv_tick_inc(1);
}
}
void TIM6_MSP_Pre_Init(TIM_HandleTypeDef * htim)
{
HAL_TIMER_MSP_Init(&TIM6_Handler);
}
void TIM6_MSP_Post_Init(void)
{
/*
do nothing here
*/
}
void TIM6_Init(void)
{
uint32_t timer_clock;
timer_clock = HAL_RCC_GetPCLK1Freq(); // TIM6 is in APB1
if (HAL_RCC_GetHCLKFreq() != timer_clock) // if hclk/pclk != 1, then timer clk = pclk * 2
{
timer_clock = timer_clock << 1;
}
TIM6_Handler.Instance = TIM6;
TIM6_Handler.Init.ARRPreLoadEn = TIM_ARR_PRELOAD_ENABLE;
TIM6_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM6_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM6_Handler.Init.RepetitionCounter = 0;
TIM6_Handler.Init.Prescaler = (timer_clock/TIM_CLOCK_FREQ) - 1;
TIM6_Handler.Init.Period = (TIM_CLOCK_FREQ/1000)*1 - 1; // 1ms
TIM6_MSP_Pre_Init(&TIM6_Handler);
HAL_TIMER_Base_Init(&TIM6_Handler);
HAL_TIM_ENABLE_IT(&TIM6_Handler, TIMER_INT_EN_UPD);
TIM6_MSP_Post_Init();
HAL_TIMER_Base_Start(TIM6_Handler.Instance);
}
紧接着我们需要main函数的while中添加lvgl任务处理函数lv_task_handler();
这样lvgl就可以运行了。
我们编写一个简单的界面如下,界面编写的是3个lvgl按钮。
4.上电测试
我们将写好的代码编译烧进板子,就可以看到我们的lvgl正常运行了。
效果图如下所示:
总结:通过本次成功移植lvgl,现在我们可以创造我们想要的界面。但是这是裸机移植lvgl,任务不是很好管理,下一期我们移植带操作系统的lvgl。