在前面移植到Ubuntu虚拟机上进行测试过,但是测试程序里面只有显示器驱动,没有输入设备(鼠标、键盘、触摸板)的移植,今天将LittlevGL移植到我的一块Linux板子上去,板子带有一块800*480的屏幕以及电容触摸面板。说不准什么时候可能可以用上,毕竟安卓和QT对系统的性能和资源要求太高,使用LittlevGL这套图形库可能会在某些场景上使用到。
准备使用原来在虚拟机上测试的那套例程进行修改,因为framebuffer的操作都是一样的,直接修改Makefile,将其中的CC改成交叉编译器即可:
CC = /home/tangquan/workspace/docs/Linux-SDK/dragonboard/out/sun8iw5p1/dragonboard/common/buildroot/external-toolchain/bin/arm-linux-gnueabi-gcc
然后直接make编译即可得到开发板上可执行的文件,运行即可,效果和虚拟机上测试是一样的,但是无法触摸。下面进行输入设备的移植工作。
参考文章:https://blog.csdn.net/a694543965/article/details/79935086。
执行命令“cat /proc/bus/input/devices”查看本机的输入设备,找到其中关于触摸屏的信息:
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="ft5x_ts"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=ddrfreq_dsm event3
B: PROP=0
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=2650000 0
信息中的Handlers表示该输入设备的事件处理者,第一个是DDR内存(目的是在点击屏幕的时候调整DDR频率,一段时间不操作则降低DDR频率以节能)。第二个是event3,这个设备在“/dev/input/”中可以看到,我们可以操作event3这个设备来获得触摸屏的输入信息。
在"lv_drivers/indev/"文件夹中创建两个文件touchscreen.c和touchscreen.h,参考并修改参考文章中的内容,得到一个驱动设备:
#include "touchscreen.h"
#include "stdio.h"
#if USE_TOUCHSCREEN
#include
#include LV_DRV_INDEV_INCLUDE
#include LV_DRV_DELAY_INCLUDE
#include
#include
#include
#include
#include
#include
#include "unistd.h"
#include "pthread.h"
#include
pthread_t TouchScreenEventHandler_t;
void* TouchScreenEventHandler(void *args);
static bool left_button_down = false;
static int flags = 0;
static int16_t last_x = 0,last_x_tmp = 0;
static int16_t last_y = 0,last_y_tmp = 0;
void ts_init(void)
{
printf("Initialize touch screen\r\n");
//int pthread_create(pthread_t * tidp, const pthread_attr_t *attr, void *(*start_rtn)(void *), void *arg);
pthread_create(&TouchScreenEventHandler_t,NULL,TouchScreenEventHandler,(void*)0);
}
/**
* Get the current position and state of the touchpad
* @param data store the read data here
* @return false: because no ore data to be read
*/
bool ts_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
/*Store the collected data*/
data->point.x = last_x;
data->point.y = last_y;
data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
return false;
}
/**********************
* STATIC FUNCTIONS
**********************/
void* TouchScreenEventHandler(void *args)
{
int fd;
fd_set rds;
int ret;
struct input_event event;
struct timeval time;
struct input_absinfo absI;
fd = open( "/dev/input/event3", O_RDONLY );
if ( fd < 0 )
{
perror( "/dev/input/event3" );
return NULL;
}
//得到X轴的abs信息
ioctl(fd,EVIOCGABS(ABS_X),&absI);
printf("x abs lastest value=%d\n",absI.value);
printf("x abs min=%d\n",absI.minimum);
printf("x abs max=%d\n",absI.maximum);
//得到y轴的abs信息
ioctl(fd,EVIOCGABS(ABS_Y),&absI);
printf("y abs lastest value=%d\n",absI.value);
printf("y abs min=%d\n",absI.minimum);
printf("y abs max=%d\n",absI.maximum);
//得到按压轴的abs信息
ioctl(fd,EVIOCGABS(ABS_PRESSURE),&absI);
printf("pressure abs lastest value=%d\n",absI.value);
printf("pressure abs min=%d\n",absI.minimum);
printf("pressure abs max=%d\n",absI.maximum);
while ( 1 )
{
FD_ZERO( &rds );
FD_SET( fd, &rds );
/*调用select检查是否能够从/dev/input/event0设备读取数据*/
ret = select( fd + 1, &rds, NULL, NULL, NULL );
if ( ret < 0 )
{
perror( "select" );
return NULL;
}
/*能够读取到数据*/
else if ( FD_ISSET( fd, &rds ) )
{
ret = read( fd, &event, sizeof(struct input_event) );
time = event.time;
// printf( "timeS=%d,timeUS=%d,type=%d,code=%.2x,value=%d\n", time.tv_sec, time.tv_usec, event.type, event.code, event.value );
// printf("%d,%d\r\n",event.type,event.code);
if(event.type == EV_SYN)
{
if(event.code == SYN_REPORT)
{
if((flags & 0x03) == 0x03)
{
flags = 0x00;
last_x = last_x_tmp;
last_y = last_y_tmp;
if(!left_button_down)
{
left_button_down = true;
// printf("key:%d\r\n",left_button_down);
}
// printf("%d,%d\r\n",last_x,last_y);
}
else
{
left_button_down = false;
// printf("key:%d\r\n",left_button_down);
}
}
}
else if(event.type == EV_ABS)
{
if(event.code == ABS_MT_POSITION_X)
{
last_x_tmp = event.value;
flags |= 0x01;
// printf("%d,",last_x);
}
else if(event.code == ABS_MT_POSITION_Y)
{
last_y_tmp = event.value;
flags |= 0x02;
// printf("%d\r\n",last_y);
}
}
}
else
printf("a");
//usleep(100000);
}
/*关闭设备文件句柄*/
close( fd );
}
#endif
程序中创建了一个线程TouchScreenEventHandler用于读取触摸屏的数据,这里需要注意Linux系统的input子系统的输入数据的格式,例如这里的触摸屏应用中,一般是先读取到X轴的数据帧,然后再读取到Y轴的数据帧,然后读取到一个Event types为EV_SYN,Event code为SYN_REPORT的帧作为不同组数据间的分割,如果停止触摸,则在收到最后一组触摸数据以及数据间隔之后会再收到一个EV_SYN | SYN_REPORT帧,表示两次触摸事件间的间隔。程序中使用一个flags变量用于处理上诉的这些事件,并通过触摸事件模拟出按键按下的事件。通过全局变量的方式将TouchScreenEventHandler中的数据传出去,LittlevGL内核调用ts_read函数读取设备输入数据:
/**
* Get the current position and state of the touchpad
* @param data store the read data here
* @return false: because no ore data to be read
*/
bool ts_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
/*Store the collected data*/
data->point.x = last_x;
data->point.y = last_y;
data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
return false;
}
下面可以创建一个输入设备实体并注册到LittlevGL中去了:
//Add a touchscreen input device
ts_init();
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = ts_read; /*This function will be called periodically (by the library) to get the mouse position and state*/
lv_indev_drv_register(&indev_drv);
上面这段代码放在创建完显示设备之后即可,将LV_DEMO_SLIDE_SHOW设置为0以关闭自动滑动功能取消。运行之后效果就出来了:
流畅性还可以,做一些简单的应用是没有问题的。
注:可以使用tslib作为提供的接口函数创建出来一个输入设备来给LittlevGL使用,tslib的API参考:https://github.com/libts/tslib#the-libts-library。