LVGL learning stm32f407-board-lvgl v8.3 transplantation

LVGL learning stm32f407-board-lvglv8.3 transplantation

There is a problem with the transplantation process, please refer to the tutorial or video of punctual atom

Hardware platform

  1. STM32F407ZGT6 core board
  2. 3.2 inch screen
    insert image description here

LVGL

LVGL (Light and Versatile Graphics Library) is a free and open-source graphics library that provides the ability to create embedded GUIs with easy-to-use graphic elements, beautiful visual effects, and low memory footprint.
insert image description here
LVGL is a free open source graphics library with rich components, advanced graphics features, support for multiple input devices, multi-language and independent of hardware. Next, let's take a look at the main features of the LVGL graphics user library:

  1. Powerful building blocks: buttons, charts, lists, sliders, images, and more.
  2. With advanced graphics properties: Advanced graphics with animation, anti-aliasing, opacity, smooth scrolling.
  3. Support various input devices: such as touch, mouse, keyboard, encoder.
  4. Multilingual support: UTF-8 encoding.
  5. Multi-monitor support: It can use multiple TFT or monochrome monitors at the same time.
  6. Supports multiple styling properties: it has fully customizable graphic elements with CSS-like styles.
  7. Independent of hardware: it works with any microcontroller or display.
  8. Scalability: It is able to run with small memory (minimum 64 kB Flash, 16 kB RAM for MCU).
  9. Supports OS, external storage and GPU (not required).
  10. With advanced graphics effects: single frame buffer operation is possible.
  11. Written in pure C: Written in C for maximum compatibility.

transplant work

  1. Get LVGL source code
    Download from LVGL official GitHub website (https://github.com/lvgl/lvgl/).
    insert image description here
  2. Change the lv_conf_template.h file name in the file to the lv_conf.h file name
    insert image description here
  3. .Open the lv_conf.h file and modify the conditional compilation instructions, as shown in the source code below.
    insert image description here
  4. Open the examples folder, except the porting folder, users can delete other files and folders. insert image description here
    Rename the porting folder to lvgl_driver, and modify the file name.
    insert image description here
    insert image description here
  5. Prepare a bare-metal project, including lcd, touch drivers, which can be used normally, and create an LVGL folder.
    insert image description here
  6. In the project, create lvgl/src, lvgl/config/, lvgl/port, lvgl/app
    insert image description here
  7. Add the lvgl source code file
    lvgl/config mainly lvgl.h lv_conf.h configuration file
    insert image description here
    lvgl/port mainly lv_port_disp.c, lv_port_indev.c files, display and input interface file
    insert image description here
    lvgl/src mainly source code files of lvgl components , it is more important to add all the C files under the src folder, including those under the subfolder, it is more cumbersome to add. (You don’t need to add all of them, please refer to the tutorial of punctual atoms. Here, I added all of them.)
    insert image description herelvgl/app is mainly the source code of lvgl case demonstration
    insert image description here
  8. Add the file path (refer to the punctual atom tutorial, some tutorials here will add a lot of paths, but the punctual atom tutorial does not have many paths, I also refer to it, it is recommended to refer to the punctual atom folder structure) If you use my file
    result It can be compiled and passed in this way, but I should modify the location of the header file later, which may be different from the writing method in the lvgl source code, for reference only.
    insert image description here
    The project structure is as follows
    insert image description here
    insert image description here
  9. Added
    a 1ms heartbeat for some related files lvgl by calling timer 3
//==================================================================================================
//  实现功能:定时器3中断服务函数
//  函数说明: TIM3_IRQHandler
//  函数备注: 
//--------------------------------------------------------------------------------------------------
//  |   -   |   -   |   0   |   1   |   2   |   3   |   4   |   5   |   6   |   7   |   8   |   9   
//==================================================================================================

void TIM3_IRQHandler(void)
{
    
    
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
    
    
        lv_tick_inc(1);//lvgl的1ms中断
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
}

Modify the disp code

/**
 * @file lv_port_disp_templ.c
 *
 */

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

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_disp.h"
#include <stdbool.h>
#include "lcd.h"
/*********************
 *      DEFINES
 *********************/
//#ifndef MY_DISP_HOR_RES
//    #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
//    #define MY_DISP_HOR_RES    320
//#endif

//#ifndef MY_DISP_VER_RES
//    #warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
//    #define MY_DISP_VER_RES    240
//#endif

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
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);
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
//        const lv_area_t * fill_area, lv_color_t color);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_disp_init(void)
{
    
    
    /*-------------------------
     * Initialize your display
     * -----------------------*/
//    disp_init();

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/

    /**
     * LVGL requires a buffer where it internally draws the widgets.
     * Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
     * The buffer has to be greater than 1 display row
     *
     * There are 3 buffering configurations:
     * 1. Create ONE buffer:
     *      LVGL will draw the display's content here and writes it to your display
     *
     * 2. Create TWO buffer:
     *      LVGL will draw the display's content to a buffer and writes it your display.
     *      You should use DMA to write the buffer's content to the display.
     *      It will enable LVGL to draw the next part of the screen to the other buffer while
     *      the data is being sent form the first buffer. It makes rendering and flushing parallel.
     *
     * 3. Double buffering
     *      Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
     *      This way LVGL will always provide the whole rendered screen in `flush_cb`
     *      and you only need to change the frame buffer's address.
     */

    /* Example for 1) */
    static lv_disp_draw_buf_t draw_buf_dsc_1;
    static lv_color_t buf_1[LV_HOR_RES_MAX * 10];                          /*A buffer for 10 rows*/
    lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, LV_HOR_RES_MAX * 10);   /*Initialize the display buffer*/

    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = lcddev.width;
    disp_drv.ver_res = lcddev.height;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;

    /*Set a display buffer*/
    disp_drv.draw_buf = &draw_buf_dsc_1;

    /*Required for Example 3)*/
    //disp_drv.full_refresh = 1

    /* Fill a memory array with a color if you have GPU.
     * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
     * But if you have a different GPU you can use with this callback.*/
    //disp_drv.gpu_fill_cb = gpu_fill;

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

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

/*Initialize your display and the required peripherals.*/
//static void disp_init(void)
//{
    
    
//    /*You code here*/
//}

volatile bool disp_flush_enabled = true;

/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_enable_update(void)
{
    
    
    disp_flush_enabled = true;
}

/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_disable_update(void)
{
    
    
    disp_flush_enabled = false;
}

/*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)
{
    
    

    LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);
    lv_disp_flush_ready(disp_drv);
}

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

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

Basically, just modify the disp_flush function, and port different screen display interfaces

Modify the indev code and configure the touch screen driver

/**
 * @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 "../lvgl.h"
#include "touch.h"
/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);

/**********************
 *  STATIC VARIABLES
 **********************/
lv_indev_t * indev_touchpad;

static int32_t encoder_diff;
static lv_indev_state_t encoder_state;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_indev_init(void)
{
    
    
    static lv_indev_drv_t indev_drv;

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

    /*Initialize your touchpad if you have*/
    touchpad_init();

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

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

/*Initialize your touchpad*/
static void touchpad_init(void)
{
    
    
    /*Your code comes here*/
    tp_dev.init();
    if (0 == (tp_dev.touchtype & 0x80))
    {
    
    
        TP_Adjust();
        TP_Save_Adjdata();
    }
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    
    
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) 
   {
    
    
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    }
    else {
    
    
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;
}

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
    
    
    
    if(tp_dev.sta & TP_PRES_DOWN)
      return true;
    return false;

}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    
    
    (*x) = tp_dev.x[0];
    (*y) = tp_dev.y[0];
}
#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
  1. Note: porting LVGL must enable C99 mode
    insert image description here

Main function configuration

Referring to the actual project, many header files here are included, all in main.h

int main(void)
{
    
     
	Hareware_Iint();
    printf("Hareware_Iint			[OK] \r\n");
    
    lv_init();			  // lvgl系统初始化
	lv_port_disp_init();  // lvgl显示接口初始化,放在lv_init()的后面
	lv_port_indev_init(); // lvgl输入接口初始化,放在lv_init()的后面
    //lv_example_event_1();
    //lv_example_get_started_3();
    //lv_example_anim_timeline_1();
    //lv_demo_benchmark();
    //lv_example_event_3();
    //lv_example_chart_5();
    //lv_example_list_2();
    //lv_example_roller_3();
    lv_example_meter_1();
	while (1)
	{
    
    
        tp_dev.scan(0);
		lv_task_handler(); // lvgl的事务处理
	}

}

transplant effect

insert image description here
insert image description here
insert image description here

insert image description here

Practical tools | GUI-Guider usage sharing

Tool usage tutorial article link

GUI Guider is a host computer GUI design tool developed by NXP for LVGL. It can design LVGL GUI pages by dragging and dropping controls to speed up GUI design.

The designed GUI page can be simulated and run on the PC. After the design is confirmed, the C code can be generated and integrated into the MCU project.
insert image description here
Let’s talk about the specific use later, let’s see the effect first

lv_ui guider_ui;

int main(void)
{
    
     
	Hareware_Iint();
    printf("Hareware_Iint			[OK] \r\n");
    
    lv_init();			  // lvgl系统初始化
	lv_port_disp_init();  // lvgl显示接口初始化,放在lv_init()的后面
	lv_port_indev_init(); // lvgl输入接口初始化,放在lv_init()的后面
    //lv_example_event_1();
    //lv_example_get_started_3();
    //lv_example_anim_timeline_1();
    //lv_demo_benchmark();
    //lv_example_event_3();
    //lv_example_chart_5();
    //lv_example_list_2();
    //lv_example_roller_3();
    //lv_example_meter_1();
    setup_ui(&guider_ui);
    events_init(&guider_ui);
	while (1)
	{
    
    
        tp_dev.scan(0);
		lv_task_handler(); // lvgl的事务处理
	}


}

Simulation effect
insert image description here
Actual transplant effect
insert image description here

code reference

stm32f407-lvgl-board
insert image description here

Guess you like

Origin blog.csdn.net/weixin_43599390/article/details/132531583