This post was last edited by qiao--- on 2024-1-18 01:30
Preface
In the last issue, we successfully lit up the RGB_TFT screen, and can display various Chinese characters and graphics on this screen. However, manual display inevitably makes the displayed graphical interface a bit inconvenient and troublesome, because you need to find and design the graphics yourself. Therefore, this time we will port the lvgl graphics library.
First, let's introduce LVGL. LVGL is an open source embedded graphics library for creating graphical user interfaces (GUIs). It provides a wealth of graphical elements and controls that can implement various interactive user interfaces on embedded systems. LVGL supports multiple platforms and display devices and is highly customizable and flexible. It is widely used in embedded systems, IoT devices, industrial control and other fields. The full name of LVGL is "LittlevGL", which is the abbreviation of "Little Video Graphics Library".
1. Download the LVGL source package
First, here is the official website of lvgl. The source code package can be downloaded from the official website of lvgl, LVGL - Light and Versatile Embedded Graphics Library . I downloaded it from the github link of the official website. The downloaded file directory is shown in the figure below
The most important one is src, because it contains the source code of lvgl, and the highlight of our porting is also in this folder. Some other folders are some porting documents and application examples of lvgl, and those who are interested can read them.
Open the src folder and take a look at what is in the src folder that we need to transplant, as shown in the figure below.
After my research, the functions of the files contained in these directories in lvgl are as shown in the figure below:
-
lv_core: This subdirectory contains the core code of LVGL, such as event handling, memory management, object management, etc. These codes are the basis for building LVGL and are responsible for handling the basic functions of the user interface.
-
lv_draw: This subdirectory contains the LVGL drawing engine, which is responsible for drawing various graphic elements and controls on the display device. These codes usually include functions such as pixel-level drawing and color management.
-
lv_font: This subdirectory contains the font library supported by LVGL, which is used to display text in the user interface. These fonts can be fixed-width or variable-width, and can support different languages and character sets.
-
lv_hal: This subdirectory contains the LVGL hardware abstraction layer (HAL), which is responsible for interacting with display devices, input devices, etc. These codes usually need to be customized according to the specific hardware platform.
-
lv_misc: This subdirectory contains some auxiliary functions of LVGL, such as color conversion, file operations, etc.
-
lv_objx: This subdirectory contains various LVGL controls and objects, such as buttons, labels, lists, etc. These controls are the building blocks of the user interface and can be used to create various interactive interfaces.
The porting folder in the example folder is also the highlight of our porting, because this folder contains some files related to lvgl's device input and display input. As shown in the figure below
2. Transplant lvgl source code
Open the RGB_TFT project we created before. Create a GUI directory, create an lvgl folder under it and copy the lvgl source code and porting folder to this folder, rename the lv_conf_template.h file in the lvgl source code to lv_conf.h and copy it to the lvgl directory. As shown below. I also copied the demos folder to facilitate the porting of the lvgl built-in routines.
Open the project, add all the files in the src folder to the project, and also add the files in the porting folder to the project. As shown in the following figure
3. Modify the code
Change if 0 in lv_port_disp.c, lv_port_disp.h, lv_port_indev.c, lv_port_indev.h, and lv_conf.h to if 1. lv_port_fs.c is not used for the time being, so do not modify it yet.
Now let's compile the original project and find many errors. Let's modify them one by one:
(1) Remove _template because we have modified the source file before.
(2) Remove ¡/¡/.
(3) Change #include "../../lvgl.h" to #include "../lvgl.h"
Then modify the lv_port_disp.c file to define the length and width of the screen pixels
#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 160
Modify the screen refresh function
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);
}
Then we compile it and there will be no problem. Now all we need to do is add a time base for lvgl. I use timer 6 to add a time base for lvgl. We write the driver time base code for timer 6 as follows:
#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);
}
Next we need to add the lvgl task processing function lv_task_handler() in the while of the main function;
This way lvgl can run.
We write a simple interface as follows, which contains three lvgl buttons.
4. Power-on test
We compile the written code and burn it into the board, and we can see that our lvgl is running normally.
The effect diagram is as follows:
Summary : With this successful transplant of lvgl, we can now create the interface we want. However, this is a bare metal transplant of lvgl, and the task is not easy to manage. In the next issue, we will transplant lvgl with an operating system.