【Avnet | NXP FRDM-MCXN947试用活动】RT-Thread移植LVGL
感谢参与本次活动,今天我们就在分享一下RTT移植LVGL【前言】
RTT是国产的开源操作系统,LVGL作为一款开源的GUI框架,RTT提供LVGL的配置,可以减少手工添加海量包含文件工程量。虽然MCUXPresso提供了LVGL的工程示例,但是目前在RTT上移植LVGL的文章比较少。作者经过好几天的摸索,完成了整个工程的移植工程,在此分享如下:
【开发硬件】
1、FRDM-MCXN947开发板。
2、LCD屏:BOARD\_LCD\_S035【开发环境】
MDK 5.38
ENV工具
【基础工程生成】
1、源码下载。RTT与NXP已经提供了基于RTT的BSP,下载地址为 https://github.com/RT-Thread/rt-thread.git rt-thread
在文件下打开命令行输入: git clone https://github.com/RT-Thread/rt-thread.git rt-thread,就可以把RTT的源码下载到本地。
2、进入\rt-thread\bsp\nxp\mcx\mcxn\frdm-mcxn947,打开合命令行pkgs --update,同步工程。
3、执行scons --target=mdk5,生成mdk5的工程。
到此基础工程就生成了,下面将在此基础上进行外设的驱动、LVGL的配置。
【外设的驱动】
LCD屏的驱动,BOARD\_LCD\_S035的主控芯片为st7796,8080接口。为兼容官方LCD接口,开发板上的FlexIO LCD --J8为屏的接口,原理图如下:
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131316u6uivdmuemssim6z.png)
在NXP的官方示例中有现成的LCD屏动。因此只需要把LCD的驱动添加进工程,便可以驱动成功。
驱动用到的文件有lcd_impl_flexio.c/、st7796_lcd.c/h、
新建一个vide.c进行lcd屏的初始化
```
static lcd_impl_flexio_t s_lcd_impl;
st7796_lcd_t s_lcd = {
.config =
{
.direction = ST7796_DIR_0,
.pix_fmt = ST7796_RGB565,
.bgr_mode= 1,
.inversion = 0,
.mirrored= 0,
.vflip = 1,
},
.cb =
{
.reset_cb = lcd_impl_reset,
.write_cmd_cb= lcd_impl_write_cmd,
.write_data_cb = lcd_impl_write_data,
},
.user_data = &s_lcd_impl,
};
void display_init()
{
BOARD_InitLCDFXIOPins();
lcd_impl_init(&s_lcd_impl);
st7796_lcd_init(&s_lcd);
}
void display_window_fill_buf(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t* color)
{
st7796_lcd_load(&s_lcd, (uint8_t*)color, xs, xe, ys,ye);
}
```
此文件实现的功能为,将st7796实例化,同时封装了供lvgl写入一个buff的功能函数display_window_fill_buf
在MDK工程中,添加一个分组mylvgl,把这两个文件添加进去:
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131420v34drhyr14diielp.png)
到此LCD的初始化就完成了。
【LVGL的配置】
1、在env的配置工具中,打开LVGL选项,
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131507kt1fz9zh6wdu2t6l.png)
保存后,在线升级工程与重新生成MDK5工程。
2、在mdk工程中的mylvgl中添加lv_port_disp.c,以及lvgl_conf.h
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131524mc28euece8m8bmmv.png)
在lv_port_disp.c中添加lvgl的显示驱动代码如下:
```
#include <lvgl.h>
#include "video.h"
#define MY_DISP_HOR_RES 320
#define MY_DISP_VER_RES 480
#define DISP_BUFFER_LINES (480/5)
/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;
/*Descriptor of a display driver*/
static lv_disp_drv_t disp_drv;
/*Static or global buffer(s). The second buffer is optional*/
static lv_color_t buf_1;
/*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)
{
display_window_fill_buf((uint16_t)(area->x1), (uint16_t)(area->y1), (uint16_t)(area->x2), (uint16_t)(area->y2),(uint16_t *)(color_p));
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
void lv_port_disp_init(void)
{
/*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
lv_disp_draw_buf_init(&disp_buf, buf_1, RT_NULL, MY_DISP_HOR_RES * DISP_BUFFER_LINES);
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set the resolution of the display*/
disp_drv.hor_res = 320;
disp_drv.ver_res = 480;
/*Set a display buffer*/
disp_drv.draw_buf = &disp_buf;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
```
在代码中主要是定义好屏的像素点,以及添加向屏写入一个区域的接口函数display_window_fill_buf。
到此,显示驱动就移植完成。
【触摸驱动】
1、屏的触摸驱动为gt911,在RTT中有现成的gt911的驱动,打开env配置工具,配置如下:
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131542c7t96iy4mzadvnt7.png)
2、GT911为i2c接口,通过查看屏的原理图,接入的IO为P4_0、P4_1上,同时INT接到了P4_6上面。这样需要把屏的驱动配置到i2c2之上。
3、重新生成代码后,我们打开rtconfig.h添加配置如下:
```
#define BSP_USING_I2C
#define BSP_USING_I2C1
#define BSP_USING_I2C2
#define BSP_USING_TOUCH
#define RT_TOUCH_PIN_IRQ
```
4、添加一个分组,把gt911.c/touch.c添加进来,同时把对应的头文件路径也添加进工程。
5、添加lv_port_indev.c,进行触摸的初始化:
```
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-03-09 Rbb666 The first version
*/
#include <lvgl.h>
#include <rtdevice.h>
#define BSP_USING_TOUCH
#define DBG_TAG "lv_port_indev"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "touch.h"
#ifdef BSP_USING_TOUCH
#include "gt911.h"
#include "drv_pin.h"
static rt_device_t touch_dev;
static lv_indev_t *touch_indev;
struct rt_touch_data *read_data;
volatile static rt_uint8_t touch_detect_flag = 0;
static void touchpad_read(lv_indev_drv_t *indev, lv_indev_data_t *data)
{
if (touch_detect_flag != 1)
return;
rt_device_read(touch_dev, 0, read_data, 1);
if (read_data->event == RT_TOUCH_EVENT_NONE)
return;
data->point.x = read_data->x_coordinate;
data->point.y = read_data->y_coordinate;
if (read_data->event == RT_TOUCH_EVENT_DOWN)
data->state = LV_INDEV_STATE_PR;
if (read_data->event == RT_TOUCH_EVENT_MOVE)
data->state = LV_INDEV_STATE_PR;
if (read_data->event == RT_TOUCH_EVENT_UP)
data->state = LV_INDEV_STATE_REL;
touch_detect_flag = 0;
rt_device_control(touch_dev, RT_TOUCH_CTRL_ENABLE_INT, RT_NULL);
}
static rt_err_t rx_callback(rt_device_t dev, rt_size_t size)
{
touch_detect_flag = 1;
rt_device_control(dev, RT_TOUCH_CTRL_DISABLE_INT, RT_NULL);
return 0;
}
rt_err_t gt911_probe(rt_uint16_t x, rt_uint16_t y)
{
void *id;
touch_dev = rt_device_find("gt911");
if (touch_dev == RT_NULL)
{
rt_kprintf("can't find device gt911\n");
return -1;
}
if (rt_device_open(touch_dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
{
rt_kprintf("open device failed!");
return -1;
}
id = rt_malloc(sizeof(rt_uint8_t) * 8);
rt_device_control(touch_dev, RT_TOUCH_CTRL_GET_ID, id);
rt_uint8_t *read_id = (rt_uint8_t *)id;
rt_kprintf("id = GT%d%d%d \n", read_id - '0', read_id - '0', read_id - '0');
rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_X_RANGE, &x);/* if possible you can set your x y coordinate*/
rt_device_control(touch_dev, RT_TOUCH_CTRL_SET_Y_RANGE, &y);
rt_device_control(touch_dev, RT_TOUCH_CTRL_GET_INFO, id);
rt_kprintf("range_x = %d \n", (*(struct rt_touch_info *)id).range_x);
rt_kprintf("range_y = %d \n", (*(struct rt_touch_info *)id).range_y);
rt_kprintf("point_num = %d \n", (*(struct rt_touch_info *)id).point_num);
rt_free(id);
rt_device_set_rx_indicate(touch_dev, rx_callback);
read_data = (struct rt_touch_data *)rt_calloc(1, sizeof(struct rt_touch_data));
if (!read_data)
{
return -RT_ENOMEM;
}
return RT_EOK;
}
#define RST_PIN ((4*32)+7)
#define INT_PIN ((4*32)+6)
rt_err_t rt_hw_gt911_register(void)
{
struct rt_touch_config cfg;
rt_base_t int_pin = INT_PIN;
rt_base_t rst_pin = NULL;
cfg.dev_name = "i2c2";
cfg.irq_pin.pin = int_pin;
cfg.irq_pin.mode = PIN_MODE_INPUT_PULLDOWN;
cfg.user_data = &rst_pin;
rt_hw_gt911_init("gt911", &cfg);
gt911_probe(320, 480);
return RT_EOK;
}
#endif
void lv_port_indev_init(void)
{
#ifdef BSP_USING_TOUCH
static lv_indev_drv_t indev_drv; /* Descriptor of a input device driver */
lv_indev_drv_init(&indev_drv); /* Basic initialization */
indev_drv.type = LV_INDEV_TYPE_POINTER;/* Touch pad is a pointer-like device */
indev_drv.read_cb = touchpad_read; /* Set your driver function */
/* Register the driver in LVGL and save the created input device object */
touch_indev = lv_indev_drv_register(&indev_drv);
/* Register touch device */
rt_err_t res = rt_hw_gt911_register();
RT_ASSERT(res == RT_EOK);
#endif
}
```
代码中,需要指定GT9110的i2c为i2c2,同时添加RST、INIT_PIN为P4_7、P4_6。但是由于可能RST跟LCD是共用的,如果把RST也传进去,就会影响LCD,所以传进去时,我修改成null。
到此,触摸的驱动就完成。
【界面的设计】
NXP的LVGL设计工具gui_guider是一优秀的设计工具。
打开工具后生成一个320*480的界面,然后导入rtthead工程到
frdm-mcxn947目录下面:
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131605nmifnfefdov3g89z.png)
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131623qlhctss1ggxjhc4q.png)
新建gui_guider分配,把custom、generated文件夹中所有的.c文件添加进工程分组,同时把头文件添加进工程路径中。
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131637qzfekd1fc04ge222.png)
在lv_user_gui.c中添加代码如下,定位lvgl初始到gui-guider的初始化码之中:
```
#include "rtconfig.h"
#include "lvgl.h"
#include "events_init.h"
#include "gui_guider.h"
lv_ui guider_ui;
void lv_user_gui_init(void)
{
/* display demo; you may replace with your LVGL application at here */
setup_ui(&guider_ui);
events_init(&guider_ui);
}
```
到此工程全部完成。下载代码到开发板,即可实现如下效果:
!(https://www.eefocus.com/forum/data/attachment/forum/202412/02/131657k1tmmslftmoolmol.png)
页:
[1]