Integration into the LVGL project of a graphic library for microcontrollers

LVGL - Light and Versatile Graphics Library also known as LittleVGL.





The library supports a large number of microcontrollers such as STM32, ESP32 and others. So far I have been able to run a full demo program on ESP32 and STM32f429 Discovery. The library is open source, supports a large number of graphic elements with Dark and Light themes. Distributed under the MIT license. Can be used freely even in commercial products. You can watch an interactive Online Demo without installing on the device



The library supports connecting two types of display



  1. Directly via RGB interface where the buffer will be on the MCU side in internal RAM or external SDRAM
  2. Via an external display controller. In this case, the MCU can communicate with the display controller via the SPI or I2C bus. To improve performance, intermediate render buffers inside the MCU can also be used in this case.


Two typical hardware set-ups
MCU with TFT/LCD driver If your MCU has a TFT/LCD driver periphery then you can connect a display directly via RGB interface. In this case, the frame buffer can be in the internal RAM (if the MCU has enough RAM) or in the external RAM (if the MCU has a memory interface).



External display controller If the MCU doesn't have TFT/LCD driver interface then an external display controller (E.g. SSD1963, SSD1306, ILI9341) has to be used. In this case, the MCU can communicate with the display controller via Parallel port, SPI or sometimes I2C. The frame buffer is usually located in the display controller which saves a lot of RAM for the MCU.





Everything is very flexible in this regard. If you have a driver, but the library does not yet have a port for this driver, then you can easily integrate the library into your project yourself.The



easiest way, but also the slowest is to rewrite the drawing callback - my_flush_cb



void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            put_px(x, y, *color_p)
            color_p++;
        }
    }

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp);
}


put_px - This is your driver's pixel rendering. Due to the fact that rendering is pixel-by-pixel, this is slow. The library documentation details other more efficient integration methods.



Integrating LVGL into the project. The first is to initialize the library, display and input system.




   lv_init();
   tft_init();
   touchpad_init();

   lv_demo_widgets();  //     


Since the library has a task manager inside itself. Yes, this is a multi-threaded system, correct me if this is not so, then we need to increase the internal counter of the dispatcher by calling lv_tick_inc



void * tick_thread (void *args)
{
      while(1) {
        usleep(5*1000);   /*Sleep for 5 millisecond*/
        lv_tick_inc(5);      /*Tell LVGL that 5 milliseconds were elapsed*/
    }
}


Moreover, it is necessary to transfer to the function milliseconds that have passed since the moment of its last call, or in other words, the time since the moment of the previous iteration.



In order not to waste resources on an extra thread, you can call this function on a timer interrupt. STM32 has a SysTick timer for such purposes:




void systickInit (uint16_t frequency)
{
   RCC_ClocksTypeDef RCC_Clocks;
   RCC_GetClocksFreq (&RCC_Clocks);
   (void) SysTick_Config (RCC_Clocks.HCLK_Frequency / frequency);
}

extern "C" void SysTick_Handler (void)
 {
      lv_tick_inc(1);    // 1 ms
 }


SysTick timer
This timer is dedicated to real-time operating systems, but could also be used as a standard

downcounter. It features:



  • A 24-bit downcounter
  • Autoreload capability
  • Maskable system interrupt generation when the counter reaches 0
  • Programmable clock source.




It is also necessary to call lv_task_handler in the loop. It is recommended to jerk it every 5 ms to ensure good response. I tried increasing to 20ms and the system was still quite responsive and smooth. Can be left as an eternal loop or use Thread



while(1) {
  lv_task_handler();
  my_delay_ms(5);
}


The loops should be in different places. I made a mistake and stuffed lv_tick_inc and lv_task_handler into one loop. Here's what came of it - Brakes



When both methods were split into different streams at the correct intervals, everything worked correctly and quickly:





The library has the ability to customize the number of internal buffers:



  1. One buffer when LVGL draws the contents of the screen to a buffer and sends it to the display
  2. Two partial-screen buffers, while rendering in one buffer, the contents of the other buffer are sent to be displayed in the background
  3. Two full-screen buffers


The site has a converter for fonts and images . You can safely add your font to the project or your icon in the menu. Moreover, you can optionally load pictures from external storage, such as CD-CARD, or from an array of bytes located in the internal Flash memory.



How to use the generated file in LittlevGL?
For C arrays

Copy the result C file into your LittlevGL project

In a C file of your application declare the image as: LV_IMG_DECLARE(my_image_name);

Set the image for an lv_img object: lv_img_set_src(img1, &my_image_name);

For external binary files (e.g. SD card)

Set up a new driver. To learn more read the Tutorial.

Set the image for an lv_img object: lv_img_set_src(img1, ยซS:/path/to/imageยป);



Another important and nice feature of this library is that you can use the Eclipse IDE on Linux and Windows to debug it.







It's nice that it is well documented for the OpenSource library. There are many examples and ports. The library has grown to a fairly large community.



I launched a port for ESP32. Even when using mapping for SPI pins i.e. not default ones, at which the best baud rate is obtained, everything worked without slowdowns:



ESP32 ST7789 LVGL

ESP32 ILI9341 LVGL



Related materials
docs.lvgl.io/latest/en/html/porting/sys.html

Basic systick configuration on the STM32




All Articles