Getting Started with LvGL V9 with STM32F429-disco Part3: LvGL Integration

In this guide, we shall integrate LvGL and initialize the LCD, touch LvGL itself.

In this guide, we shall cover the following:

  • TFT integration.
  • Touch integration.
  • LvGL integration.
  • Code download.
  • Results.

List of the previous parts:

1. TFT Integration:

Create new source and header file with name of LCDController.c and LCDController.h respectively,

Within the header file:

The contents of the header can be found here.

#ifndef INC_LCDCONTROLLER_H_
#define INC_LCDCONTROLLER_H_


#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/

#include "lvgl.h"

/*********************
 *      DEFINES
 *********************/

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

/**********************
 * GLOBAL PROTOTYPES
 **********************/
/* Initialize low level display driver */
void lv_port_disp_init(void);

/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_enable_update(void);

/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
 */
void disp_disable_update(void);

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

#ifdef __cplusplus
} /*extern "C"*/
#endif



#endif /* INC_LCDCONTROLLER_H_ */

In the source file which can be found here.

The content of the source file as following:

/*********************
 *      INCLUDES
 *********************/
#include "LCDController.h"
#include <stdbool.h>
#include "ILI9341.h"
#include "stm32f4xx.h"
#include "stdint.h"
#include "LCD_Pins.h"
/*********************
 *      DEFINES
 *********************/
#define MY_DISP_HOR_RES 240


#define MY_DISP_VER_RES 320


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

/**********************
 *  STATIC VARIABLES
 **********************/
lv_display_t * disp;                        /*Descriptor of a display driver*/

lv_color_t buf_1[MY_DISP_HOR_RES *64];      /*A buffer for 10 rows*/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void disp_init(void);

static void disp_flush(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p);


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


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

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

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/
    disp=lv_display_create(MY_DISP_HOR_RES,MY_DISP_VER_RES);

    	/*Set the display flush callback*/
    lv_display_set_flush_cb(disp, &disp_flush);


      /* display buffer initialization */
    lv_display_set_buffers(disp, &buf_1, NULL, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL);
}

/**********************
 *   STATIC NCTIONS
 **********************/

/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
    ILI9341_Init();
    ILI9341_setRotation(3);


}

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;
}


static void disp_flush(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
{

	setAddrWindow(area->x1, area->y1, area->x2, area->y2);
	int height = area->y2 - area->y1 + 1;
	int width = area->x2 - area->x1 + 1;

	ILI9341_DrawBitmap(width, height, (uint8_t *)color_p);


    /*IMPORTANT!!!
     *Inform the graphics library that you are ready with the flushing*/

}

void SPI5_TX_Finished(void)
{

	lv_disp_flush_ready(disp);
	LCD_CS_HIGH();
}

Explanation:

#include "LCDController.h"
#include <stdbool.h>
#include "ILI9341.h"
#include "stm32f4xx.h"
#include "stdint.h"
#include "LCD_Pins.h"

These includes are needed to make the lcd works.

#define MY_DISP_HOR_RES 240


#define MY_DISP_VER_RES 320

Our display resolution.

lv_display_t * disp;                        /*Descriptor of a display driver*/

lv_color_t buf_1[MY_DISP_HOR_RES *64];      /*A buffer for 10 rows*/

Display driver and the LCD buffer.

static void disp_init(void);

static void disp_flush(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p);

Static declaration of function prototype.

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

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/
    disp=lv_display_create(MY_DISP_HOR_RES,MY_DISP_VER_RES);

    	/*Set the display flush callback*/
    lv_display_set_flush_cb(disp, &disp_flush);


      /* display buffer initialization */
    lv_display_set_buffers(disp, &buf_1, NULL, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL);
}

/**********************
 *   STATIC NCTIONS
 **********************/

/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
    ILI9341_Init();
    ILI9341_setRotation(3);


}

This shall initialize the LCD and LvGL buffer.

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;
}

Here is to enable flush of the buffer after the data has been drawn.

static void disp_flush(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
{

	setAddrWindow(area->x1, area->y1, area->x2, area->y2);
	int height = area->y2 - area->y1 + 1;
	int width = area->x2 - area->x1 + 1;

	ILI9341_DrawBitmap(width, height, (uint8_t *)color_p);


    /*IMPORTANT!!!
     *Inform the graphics library that you are ready with the flushing*/

}

void SPI5_TX_Finished(void)
{

	lv_disp_flush_ready(disp);
	LCD_CS_HIGH();
}

This for the display flush.

It shall first set the region to be drawn by the LvGL, push the data to the LCD using SPI in DMA mode then SPI5_TX_Finished shall be called once the data has been sent.

For more information, please refer to this.

2. Touch Integration:

Create new source and header file with name of TouchController.c and TouchController.h respectively.

Within the header file:

#ifndef TOUCHCONTROLLER_H_
#define TOUCHCONTROLLER_H_

/*********************
 *      INCLUDES
 *********************/
#include "lvgl.h"

/*********************
 *      DEFINES
 *********************/

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

/**********************
 * GLOBAL PROTOTYPES
 **********************/
void lv_port_indev_init(void);


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



#endif /* TOUCHCONTROLLER_H_ */

In the source file:

/*********************
 *      INCLUDES
 *********************/
#include "lvgl.h"
#include "TouchController.h"
#include "STMPE811.h"
#include "ILI9341.h"
/*********************
 *      DEFINES
 *********************/
#define GUI_WIDTH 240
#define GUI_HEIGHT 320
/**********************
 *      TYPEDEFS
 **********************/


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

static void touchpad_init(void);
static void touchpad_read(lv_indev_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;

struct {
	lv_coord_t x;
	lv_coord_t y;
}get_xy;

uint16_t intx, inty;



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

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

void lv_port_indev_init(void)
{

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

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

    /*Register a touchpad input device*/
    indev_touchpad = lv_indev_create();
    lv_indev_set_type(indev_touchpad, LV_INDEV_TYPE_POINTER);
    lv_indev_set_read_cb(indev_touchpad, touchpad_read);


}

/*Initialize your touchpad*/
static void touchpad_init(void)
{
	STMPE811_Touch_Enable();
}



uint8_t ret_value;
static int32_t _x = 0, _y = 0;
int16_t xDiff, yDiff, xr, yr;
uint16_t x_raw, y_raw;

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{



	if(isToched()==no_touch)
	{
		ret_value=false;
	}
	else
	{
			getTouchValue(&x_raw,&y_raw);
			/* Y value first correction */
			y_raw -= 300;

			/* Y value second correction */
			yr = y_raw / 11;

			/* Return y_raw position value */
			if(yr <= 0) yr = 0;
			else if (yr > GUI_HEIGHT) yr = GUI_HEIGHT - 1;

			y_raw = yr;

			/* X value first correction */
			if(x_raw <= 3720) x_raw = 3770 - x_raw;
			else  x_raw = 3770 - x_raw;

			/* X value second correction */
			xr = x_raw / 15;

			/* Return X position value */
			if(xr <= 0) xr = 0;
			else if (xr > GUI_WIDTH) xr = GUI_WIDTH - 1;

			x_raw = xr;
			if(x_raw>_x){xDiff=x_raw - _x;}
			else {xDiff=_x - x_raw;}

			if(y_raw>_y){yDiff=y_raw - _y;}
			else {yDiff=_y - y_raw;}


			//xDiff = x_raw > _x? (x_raw - _x): (_x - x_raw);
			//yDiff = y_raw > _y? (y_raw - _y): (_y - y_raw);

			if (xDiff + yDiff > 5)
			{
				_x = x_raw;
				_y = y_raw;
			}

			get_xy.x = GUI_WIDTH-_x;
			get_xy.y = GUI_HEIGHT-_y;
			ret_value= true;
	}
	return ret_value;
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_t * indev_drv, lv_indev_data_t * data)
{

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {

        data->state = LV_INDEV_STATE_PR;

    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }

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


/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/

    (*x) = 0;
    (*y) = 0;
}



Please refer to this and this for more details about the touch integration.

3. LvGL Integration:

First, head to this github repository.

Then:

Make sure to select the latest version (V9.1 when this guide is written) and download as zip file.

After the download, extract the zip file and rename it to be lvgl only.

In STM32CubeIDE. create new folder with name of Drivers as following:

Copy the lvgl folder to the driver folder.

Create new header file within the driver folder with name of lv_conf.h.

Copy the content of this header to lv_conf.h.

In the header file, enable LvGL by setting this to 1 (line 15):

#if 1 /*Set it to "1" to enable content*/

In line 30, set the color depth to 16:

#define LV_COLOR_DEPTH 16

In line 946, enable widget demo:

#define LV_USE_DEMO_WIDGETS 1

Save the header file.

Now, we shall include the lvgl to the project

Right click on the project and select Properties.

Then:

And:

Click on apply and close.

Now, in delay.c source file:

Include the lvgl.h header file:

#include "lvgl.h"

Within the systick interrupt handler, call lv_tick_inc(1) which means increase the lv tick by 1ms:

lv_tick_inc(1);

In main.c file:

#include "LCD_Pins.h"
#include "ILI9341.h"
#include "i2c3.h"
#include "STMPE811.h"
#include "LCDController.h"
#include "demos/lv_demos.h"
#include "TouchController.h"



int main(void)
{
	delay_init(180000000);

	i2c3_init();

	LCD_Pin_Init();
	LCD_SPI_Init();
	lv_init();
	lv_port_disp_init();
	lv_port_indev_init();
	lv_demo_widgets();

	while(1)
	{
		lv_timer_handler();
		delay(5);

	}
}

3. Code Download:

You may download the entire project from here:

4. Demo:

Happy coding 😉

6 Comments

  • MassimoM Posted July 15, 2024 12:59 pm

    There are some strange behaviour, can’t reproduce. E.G. in many file I got werror in #include “lvgl/lvgl.h” so must set “LV_LVGL_H_INCLUDE_SIMPLE” general define. But I got nothing on the screen.
    Can you give again the wole project? The above link doesn’t work.
    Thanks for the effort.

  • MassimoM Posted July 19, 2024 8:47 am

    HI.

    Got some problems, but the download link (for the entire project) doesn’t work.
    Can you correct it?

  • SAAD Posted November 17, 2024 8:40 am

    Hello, is there a code for the arduino ide LTDC and FMC? Thank you for sharing the codes

    • Husamuldeen Posted November 19, 2024 6:14 am

      Hi,
      we encourage students to use STM32CubeIDE to further develop their skills.
      By using arduino ide, you are limiting yourself to really bad code practices.

Add Comment

Your email address will not be published. Required fields are marked *