Author: Rb
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.
Download the RT-Smart user space application code:
git clone https://github.com/RT-Thread/userapps.git
Go to the userapps directory and clone the RT-Thread rt-smart branch
git clone -b rt-smart https://gitee.com/rtthread/rt-thread.git
More Details about environment built check out HERE.
Running get_toolchain.py'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 get_toolchain.py riscv64
Under the userapps directory, run the smart-env.sh to configure the toolchain path, the currently supported parameter can be arm or riscv64
source smart-env.sh riscv64
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 https://github.com/lvgl/lvgl.git
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:
struct fb_fix_screeninfo
to describe structsTouch 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.
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.
lvgl
├── 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 https://github.com/lvgl/lvgl
and does not need to be modified.https://github.com/RT-Thread-packages/lv_demo_music.git
, no modification required.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
#define LV_USE_PERF_MONITOR 1
/* music player demo */
#define LV_USE_DEMO_RTT_MUSIC 1
#define LV_DEMO_RTT_MUSIC_AUTO_PLAY 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_COLOR_SCREEN_TRANSP 1
————————————————
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");
return;
}
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.
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);
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
}
————————————————
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.