文章目录
- 一、无操作系统
- 1.源码获取:
- 2.输出设备配置(屏幕配置)
- 几个关键函数
- 屏幕初始化函数:
- 显示刷新(画点)函数
- 3.输入配置(触摸)
- 4.提供时基
- 二、RT-Thread
- 1.新建工程
- 2.添加软件包,屏幕相关文件以及添加编译路径
- 3.输出设备配置(屏幕配置)
- 4.输入设备配置(Touch设备配置)
LVGL移植
前言:本次LVGL移植分为有操作系统和无操作系统两种方式。操作系统使用的是RT-Thread
硬件使用野火STM32F407霸天虎开发板,屏幕使用nt35510显示IC,触摸使用gt917s芯片
一、无操作系统
开发环境使用wsl环境下的VScode+arm-none-eabi-gcc编译+openocd烧录+arm-none-eabi-gdb调试,具体可以参考https://blog.csdn.net/weixin_51954217/article/details/129350873?spm=1001.2014.3001.5501
1.源码获取:
LVGL核心图形库github地址:https://github.com/lvgl/lvgl
解压后目录如下:红框圈起来的文件是必须的文件,其他的可删可不删
将文件夹复制到工程目录下
我们主要修改lvgl/examples/porting
文件夹下的lv_port_disp_templete.c/.h
文件和lv_port_indev_templete.c/.h
文件以及lvgl
文件夹下的lv_conf_templete.h
文件
这几个文件的作用如下:
- lv_port_disp_templete.c/.h:输出设备接口文件,在这里使用的是LCD屏幕,将屏幕的初始化函数以及画点函数对接起来,使LVGL库能够画图。
- lv_port_indev_templete.c/.h:输入设备接口文件,在这里使用的是touch电容触摸,将触摸反馈与LVGL的接口对接,使LVGL能够对触摸做出反应。
2.输出设备配置(屏幕配置)
-
将
lv_port_disp_templete.c/.h
文件分别重命名为lv_port_disp.c/.h
-
将这两个文件里的条件编译#if 0改为#if 1表示启用这两个文件
-
lv_port_disp_templete.h改名为lv_port_disp.h后记得包含的头文件也改成lv_port_disp.h。
-
包含自己的显示屏头文件
lcd.h
-
修改显示屏的水平宽度和垂直宽度为实际屏幕的尺寸,我的水平方向450,垂直方向800
几个关键函数
-
void lv_port_disp_init(void)
//通过调用该函数初始化屏幕 -
static void disp_init(void)
//将屏幕的初始化代码放在里面 -
static void disp_flush(lv_disp_t * disp_drv, const lv_area_t * area, lv_color_t * px_map)
//我们需要补全显示刷新(画点)函数
屏幕初始化函数:
我的屏幕初始化函数为LCD_Init()
,在lcd.h中,作用是初始化屏幕相关的IO口和屏幕驱动方式FSMC的初始化,以及屏幕初始化参数的写入等。
static void disp_init(void)
{
/*You code here*/
LCD_Init();
}
显示刷新(画点)函数
其中LCD_SetPointPixel
函数用于将屏幕x , y点设置为十六位的color这个颜色
static void disp_flush(lv_disp_t * disp_drv, const lv_area_t * area, lv_color_t * px_map)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *px_map)*/
/* 下面自己的代码 */
uint16_t color=0;
color |= px_map->red<<11;
color |= px_map->green<<5;
color |= px_map->blue;
LCD_SetPointPixel(x, y, color);
/* 上面是自己的代码 */
px_map++;
}
}
}
注意:LVGL有三种刷新方式,都是新建数组的方式缓存要显示的内容
方式1和方式2都是新建一个屏幕长度*10的数组存储数据,只不过方式2新建了两个。方式3位屏幕长度*屏幕宽度*2的数组,因为方式3太大了,会出现超内存的情况,需要将方式3注释掉,再在方式1 2 中选择一个,将另一个注释掉
至此,LVGL有关显示的函数就已经配置好了,只需要在主函数中调用lv_port_disp_init
即可初始化屏幕。
3.输入配置(触摸)
输入设备有很多种,比如鼠标、触摸IC等等,可以把不用的方式的代码去掉,只保留用到的代码。
输入设备相关的函数在lv_port_indev_templete.c/.h
文件中
-
与输入设备文件相同,将#if 0 改为#if1
-
将文件名中的_templete去掉,文件名改为
lv_port_indev.c/.h
-
包含自己的touch设备头文件touch.h
-
因为只用到了touch设备,所以可以将mouse和encode相关代码删除
/** * @file lv_port_indev_templ.c * */ /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/ #if 1 /********************* * INCLUDES *********************/ #include "lv_port_indev.h" #include "touch.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void touchpad_init(void); //Initialize your touchpad static void touchpad_read(lv_indev_t * indev, lv_indev_data_t * data); //Read the touchpad static bool touchpad_is_pressed(void); //Return true is the touchpad is pressed static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y); //Get the x and y coordinates if the touchpad /********************** * STATIC VARIABLES **********************/ lv_indev_t * indev_touchpad; /********************** * 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); } /********************** * STATIC FUNCTIONS **********************/ /*------------------ * Touchpad * -----------------*/ /*Initialize your touchpad*/ static void touchpad_init(void) { /*Your code comes here*/ GTP_Init_Panel(); } /*Will be called by the library to read the touchpad*/ static void touchpad_read(lv_indev_t * indev_drv, lv_indev_data_t * data) { static lv_coord_t last_x = 0; static lv_coord_t last_y = 0; /*Save the pressed coordinates and the state*/ if(touchpad_is_pressed()) { touchpad_get_xy(&last_x, &last_y); data->state = LV_INDEV_STATE_PR; } else { data->state = LV_INDEV_STATE_REL; } /*Set the last pressed coordinates*/ data->point.x = last_x; data->point.y = last_y; } /*Return true is the touchpad is pressed*/ static bool touchpad_is_pressed(void) { /*Your code comes here*/ //实现touchpad_is_pressed函数 if(GTP_Scan(0)) { return true; } return false; } /*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) = tp_dev.x[0]; (*y) = tp_dev.y[0]; } #else /*Enable this file at the top*/ /*This dummy typedef exists purely to silence -Wpedantic.*/ typedef int keep_pedantic_happy; #endif
关键函数
static void touchpad_init(void)
将自己的touch初始化函数放在里面static bool touchpad_is_pressed(void)
补全该函数,使touch设备按下时返回truestatic void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
补全该函数使其能够返回touch坐标的函数
函数实现:
static void touchpad_init(void)
static void touchpad_init(void)
{
/*Your code comes here*/
GTP_Init_Panel();
}
将自己的touch.h中的touch初始化函数放在里面
static bool touchpad_is_pressed(void)
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
//实现touchpad_is_pressed函数
if(GTP_Scan(0))
{
return true;
}
return false;
}
实现一个touch扫描函数,当电容屏被触摸时返回真值
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
(*x) = tp_dev.x[0];
(*y) = tp_dev.y[0];
}
将touch设备获取的坐标返回给x,y
4.提供时基
与操作系统类似,LVGL也需要时基,相当于LVGL的心跳,API为LV_ATTRIBUTE_TICK_INC void lv_tick_inc(uint32_t tick_period)
需要将其放在定时器中断函数里,多少毫秒产生一次中断就在参数里填写几。这里将其放在SysTick系统滴答定时器的中断函数里,就不需要为其开一个单独的定时器了
二、RT-Thread
开发环境使用RT-Thread Studio
参考RT-Thread官方LVGL移植步骤https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/packages-manual/lvgl-docs/indev-touch-port
1.新建工程
首先基于芯片新建一个RT-Thread标准版工程
2.添加软件包,屏幕相关文件以及添加编译路径
添加LVGL软件包与触摸芯片软件包
ctrl+s保存后等待软件包应用到工程中,应用完成后可以在packages目录下看到新导入的lvgl与触摸ic软件包
可以看到LVGL软件包中没有lv_port_disp_templete.c/.h
和lv_port_indev_templete.c/.h
文件,需要从文件目录中复制过来
右键LVGL目录->打开资源所在目录,将examples->porting文件夹下的lv_port_disp_templete.c/.h
和lv_port_indev_templete.c/.h
文件复制到applications目录下
将自己的屏幕相关代码拷贝到工程的applications目录下,我在applications目录下新建了app_driver文件夹,在该文件夹下又新建了inc与src文件夹分贝存放头文件与源文件。将刚才复制的lv_port_disp_templete.c/.h
和lv_port_indev_templete.c/.h
文件也分别改名后放到inc与src目录下。
注意:因为我们直接复制的文件到工程中,所以编译器中没有保存我们复制的文件的路径,编译的时候会存在找不到文件的情况,需要将路径添加到工程路径中
方法:
添加完成后应用即可
3.输出设备配置(屏幕配置)
-
将
lv_port_disp_templete.c/.h
文件分别重命名为lv_port_disp.c/.h
-
将这两个文件里的条件编译#if 0改为#if 1表示启用这两个文件
-
lv_port_disp_templete.h改名为lv_port_disp.h后记得包含的头文件也改成lv_port_disp.h。
-
包含自己的显示屏头文件
lcd.h
-
修改显示屏的水平宽度和垂直宽度为实际屏幕的尺寸,我的水平方向450,垂直方向800
步骤与无操作系统的一样,步骤吧参考无操作系统的,因为屏幕没有接入rtthread的设备框架,后期有时间会接入设备框架。
4.输入设备配置(Touch设备配置)
touch设备接入参考rtthread官方文档进行的修改,因为gt917s这个软件包对接了rtthread的touch设备框架,所以在这里我们需要使用rtthread的touch设备框架的api
lvgl中原先需要我们实现的touchpad_init(); static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)都用不到被我注释掉了。参考官方教程后我移植的程序如下,实测是可以运行的。注意LVGL版本是8.2.0。
注意:需要在rtthread设置中打开i2c1并使能touch设备的中断(如下图所示),在board.h中取消掉i2c1的注释,并且指定i2c的接口为touch芯片所用到的i2c接口
/**
* @file lv_port_indev_templ.c
*
*/
/*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_indev.h"
#include "lvgl.h"
#include "touch.h"
#include "rtdbg.h"
#include "gt917s.h"
#include "rtdevice.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_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;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_indev_init(void)
{
/**
* Here you will find example implementation of input devices supported by LittelvGL:
* - Touchpad
* - Mouse (with cursor support)
* - Keypad (supports GUI usage only with key)
* - Encoder (supports GUI usage only with: left, right, push)
* - Button (external buttons to press points on the screen)
*
* The `..._read()` function are only examples.
* You should shape them according to your hardware
*/
static lv_indev_drv_t indev_drv;
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad if you have*/
// touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*------------------
* Touchpad
* -----------------*/
rt_touch_t touch_dev;
static int lv_hw_touch_init(void)
{
struct rt_touch_config cfg;
cfg.dev_name = "i2c1";/* 使用的I2C设备名 */
//#ifdef BSP_USING_TOUCH_GT917S
rt_hw_gt917s_init("touch", &cfg); /* 软件包提供的初始化函数 */
//#endif /* BSP_USING_TOUCH_FT6X36 */
touch_dev = rt_device_find("touch");
if (rt_device_open(touch_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
{
LOG_E("Can't open touch device:%s", "touch");
return -RT_ERROR;
}
return RT_EOK;
}
INIT_COMPONENT_EXPORT(lv_hw_touch_init);
/*Initialize your touchpad*/
//static void touchpad_init(void)
//{
/*Your code comes here*/
// GTP_Init_Panel();
// rt_hw_gt917s_init();
//}
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
// static lv_coord_t last_x = 0;
// static lv_coord_t last_y = 0;
//
// /*Save the pressed coordinates and the state*/
// if(touchpad_is_pressed()) {
// touchpad_get_xy(&last_x, &last_y);
// data->state = LV_INDEV_STATE_PR;
// } else {
// data->state = LV_INDEV_STATE_REL;
// }
//
// /*Set the last pressed coordinates*/
// data->point.x = last_x;
// data->point.y = last_y;
struct rt_touch_data *read_data;
/* 可以将内存分配这个步骤改为全局变量,以提高读取效率 */
read_data = (struct rt_touch_data *)rt_calloc(1, sizeof(struct rt_touch_data));
rt_device_read(touch_dev, 0, read_data, 1);
/* 如果没有触摸事件,直接返回 */
if (read_data->event == RT_TOUCH_EVENT_NONE)
return;
/* 这里需要注意的是:触摸驱动的原点可能和LCD的原点不一致,所以需要我们进行一些处理 */
//#ifdef BSP_USING_TOUCH_FT6X36
data->point.x = read_data->y_coordinate;
data->point.y = 800 - read_data->x_coordinate;
//#endif /* BSP_USING_TOUCH_FT6X36 */
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;
}
/*Return true is the touchpad is pressed*/
//static bool touchpad_is_pressed(void)
//{
/*Your code comes here*/
// if(GTP_Scan(0))
// {
// return true;
// }
// return false;
//}
/*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) = tp_dev.x[0];
// (*y) = tp_dev.y[0];
//}
//static void input_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
//{
// struct rt_touch_data *read_data;
// /* 可以将内存分配这个步骤改为全局变量,以提高读取效率 */
// read_data = (struct rt_touch_data *)rt_calloc(1, sizeof(struct rt_touch_data));
//
// rt_device_read(touch_dev, 0, read_data, 1);
//
// /* 如果没有触摸事件,直接返回 */
// if (read_data->event == RT_TOUCH_EVENT_NONE)
// return;
//
// /* 这里需要注意的是:触摸驱动的原点可能和LCD的原点不一致,所以需要我们进行一些处理 */
#ifdef BSP_USING_TOUCH_FT6X36
// data->point.x = read_data->y_coordinate;
// data->point.y = 800 - read_data->x_coordinate;
#endif /* BSP_USING_TOUCH_FT6X36 */
//
// 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;
//}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
注意在gt917s.c中有int8_t和int16_t两种类型会报错,需要改为rt_uint8_t 和 rt_uint16_t
#include "lvgl/lvgl.h"需要改为#include “lvgl.h”
除此之外肯定还有许多没有涉及到的小错误,需要自己根据报错修改。最后展示一下效果