LVGL GUI on RT-Smart MicroKernel Operating System

Created at 2022-12-01 15:28:39

Author: Rb

How to Start

1. Get to know about RT-Smart

RT-Thread Smart is a professional, high-performance, microkernel operating system for real-time applications. It offers an open source foundation for embedded devices in any market, including security (e.g., internet protocol cameras), industrial control, onboard devices, consumer electronics, and anything else using embedded technology (which is increasingly coming to mean "everything"). It's significant because, unlike traditional IoT operating systems, a microkernel operating system can fill the gap between a traditional real-time operating system (RTOS) and a comparatively large operating system like Linux to achieve the best balance between real-time performance, cost, security, startup speed, and more.

More details check out Here.

2. Built RT-Smart Environment

Download the RT-Smart user space application code:

git clone

Go to the userapps directory and clone the RT-Thread rt-smart branch

git clone -b rt-smart
More Details about environment built check out HERE.

3. Configure Toolchain

Running's script in the userapps/tools directory to download the corresponding toolchain and expand to the userapps/tools/gun_gcc directory. The toolchain name that follows can be an arm or riscv64.

Here we're taking the Allwinners D1s as an example, it is the RISCV-64 architecture.
Enter the following command:

python3 riscv64

Under the userapps directory, run the to configure the toolchain path, the currently supported parameter can be arm or riscv64

source riscv64

4. Get the LVGL Mainline Code

Go to the userapps directory, create a new media folder to store the LVGL-related code, and clone the LVGL mainline code to the local.

git clone

5. Kernel Driver Implementation

Display Part

At this part, you could refer to the implementation approach came up with drv_clcd.c in qemu-vexpress-a9. In user space, if you want to get and operate the LCD, the driver needs to implement at least two OPS functions: drv_clcd_init + drv_clcd_control.

In drv_clcd_control, we need to handle the following types of cmd:

  • RTGRAPHIC_CTRL_RECT_UPDATE: Notifies the graphics device to be updated
  • RTGRAPHIC_CTRL_GET_INFO: Get the basic information of the LCD, including pixel format, resolution, etc
  • FBIOGET_FSCREENINFO: Gets fixed parameter information for the FrameBuffer device. Fixed parameter information is using struct fb_fix_screeninfo to describe structs
  • FBIOGET_VSCREENINFO: Get variable parameter information of LCD, including screen pixels, dimensions, and other information

Touch Part:

If the user space wants to obtain the coordinates of the touch chip, the underlying driver needs to implement at least touch_ops: probe + init + read_point, that is, the function of probing, initializing, and reading the touch device.

6. User Space Interface Docking

The directory structure of user space directory is as follows, you'll be needing to create new folders named lv_rtt_port, packages under the media/lvgl/ directory.

├── lv_rtt_port
│   ├── SConscript
│   ├── lv_conf.h
│   ├── lv_port_disp.c
│   └── lv_port_indev.c
├── packages
│   ├── LVGL-latest
│   ├── lv_music_demo-latest
│   └── SConscript
├── SConscript
├── SConstruct
├── pkg_config.h
  • lv_rtt_port is a key point while doing the porting, it includes the display and touch interfaces, which mainly need to be written by ourselves.
  • LVGL-latest gets the latest code from and does not need to be modified.
  • lv_music_demo-latest is LVGL's music player demo, you could get the latest code from, no modification required.
  • The lv_conf.h file was modified from lvgl_conf_template.h in the lvgl directory. The configuration should be done as follows, and it depends on different screen parameters.
#define LV_COLOR_16_SWAP    1
#define LV_COLOR_DEPTH      32
/* music player demo */
#define LV_USE_DEMO_RTT_MUSIC       1
#define LV_FONT_MONTSERRAT_12       1
#define LV_FONT_MONTSERRAT_16       1

6.1 Display Interface

The core is in the lv_rtt_port, and the file that needs to be paid attention to is thelv_port_disp.c file, it's not rushing to get the Touch added at this part, let's get the Display works first.

In RT-Thread, the LCD device is mainly operated through rt_device_xxx, first find the LCD device through the rt_device_find function, and operate the LCD device through the device handle after finding the device.

/* LCD Device Init */
device = rt_device_find("lcd");
RT_ASSERT(device != RT_NULL);
if (rt_device_open(device, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
    rt_kprintf("open lcd devce fail\n");
rt_device_control(device, RTGRAPHIC_CTRL_GET_INFO, &info);
rt_device_control(device, FBIOGET_FSCREENINFO, &fb_info);

As long as the user space start obtaining the framebuffer, we could operate the LCD. Note that in user space we cannot directly use the framebuffer variable returned by RTGRAPHIC_CTRL_GET_INFO, but need to use FBIOGET_FSCREENINFO to get the smem_start (that's the initial address of the address space), because the initial address and length of the allocated address space will be padded to the variables of smem_start and smem_len in the fb_fix_screeninfo structure.

Thus, we can make the LCD to display different colors by filling the smem_start address with the color data.

6.2 Touch Interface

Now we're getting to the Touch Interface. The file of the lv_port_indev.c should be highlighted here, we're mainly using the rt_device_find to find the touch device, and operate the touch device through the device handle after finding the device.

#define POINT_NUMBER 1
static rt_device_t ts;
static struct rt_touch_data *read_data;
ts = rt_device_find("touch");
rt_device_open(ts, RT_DEVICE_FLAG_INT_RX);
read_data = (struct rt_touch_data *)rt_calloc(POINT_NUMBER, sizeof(struct rt_touch_data) * POINT_NUMBER);

Get coordinate function after calling the rt_device_read function, the coordinate information is saved in the parameter read_data, and then passed to the lv_indev_data_t structure of LVGL.

static bool touchpad_is_pressed(void)
    if (POINT_NUMBER == rt_device_read(ts, 0, read_data, POINT_NUMBER))
        if (read_data->event == RT_TOUCH_EVENT_MOVE)
            /* swap x and y */
            rt_uint16_t tmp_x = read_data->x_coordinate;
            rt_uint16_t tmp_y = read_data->y_coordinate;
            /* restore data */
            last_x = tmp_x;
            last_y = tmp_y;
            return true;
    return false;
static void touchpad_get_xy(rt_int16_t *x, rt_int16_t *y)
    *x = last_x;
    *y = last_y;
static void touchpad_read(lv_indev_drv_t *indev, lv_indev_data_t *data)
    if (touchpad_is_pressed())
        data->state = LV_INDEV_STATE_PRESSED;
        touchpad_get_xy(&data->point.x, &data->point.y);
        data->state = LV_INDEV_STATE_RELEASED;

7. Efforts

Here we're taking the RT-Thread Community Dev Boar Persimmon Pie M7 as an example, the compiled user space executable file is packaged and burned into the EMMC on the board using the xfel tool. Enter the executable file name in the serial terminal to start the user space program.


The final effect is as follows, with a screen resolution of 480*272 and RGB565 32-bit color depth, the frame rate of LVGL in user mode is maintained at 60fps as a whole, which meets the needs of use.


8. Docs


0 Answer
There is no answer, come and add the answer

Write Your Answer

Log in to publish your answer.,Click here to log in.