【Linux应用编程笔记】GPIO
【Linux应用编程笔记】输入设备
本系列使用的开发板为正点原子阿尔法IMX6ULL开发板,及根据正点原子所的提供教程学习
学习tslib库的使用
一个专门为触摸屏设备开发的开源Linux应用层函数库,可作为Qt的触摸屏输入插件,为Qt提供触摸输入支持。
tslib为触摸屏驱动和应用层之间的适配层,它把应用程序中读取触摸屏struct input_event类型数据并进行解析的操作过程进行了封装,向使用者提供了等装好的API接口。
下载 tslib 源码包
将下载好的源码包拷贝至Ubuntu系统中解压,创建一个目录用来安装tslib。
进入解压后的源码包目录中准备编译tslib源码,编译前记得先对交叉编译工具的环境进行设置:
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
然后对tslib源码工程进行设置:
./configure --host=arm-poky-linux-gnueabi --prefix=xx //这里的“xx”是安装路径
–host 选项用于指定交叉编译得到的库文件是运行在哪个平台,通常将–host 设置为交叉编译器名称的前缀,譬如 arm-pokylinux-gnueabi-gcc 前缀就是 arm-poky-linux-gnueabi;–prefix 选项则用于指定库文件的安装路径
接着make,然后再make install就可以了
这里使用的根文件系统是挂载的BusyBox(之前跟着教程做的),内核和设备树使用的是出厂系统
。
一些小工具,用于测试触摸屏。这个文件目录下的所有可执行文件要拷贝到开发板上的/bin目录下。
里面就一个配置文件ts.conf,需要拷贝到开发板的/etc目录下
只有一个头文件tslib.h,包含了一些结构体数据结构以及API接口的声明,所以使用tslib提供的API就要包含这个头文件
这里包含了编译tslib源码所得到的库文件,默认为动态库文件,也可以通过配置使其生成静态库文件,ts目录下存放的是一些插件库。这个目录下的所有库文件都要拷贝到开发板的/lib目录下。
教程上说可忽略。
使用tslib前需要配置一些环境变量,因为它工作时需要依赖于一些环境变量
export TSLIB_CONSOLEDEVICE=none //配置控制台设备文件名,直接为none即可
export TSLIB_FBDEVICE=/dev/fb0 //配置显示设备的名称
export TSLIB_TSDEVICE=/dev/input/event1 //配置触摸屏对应的设备节点
export TSLIB_CONFFILE=/etc/ts.conf //配置ts.con文件的路径
export TSLIB_PLUGINDIR=/lib/ts //配置插件所在的路径
配置到这就可以使用tslib提供的测试工具了,比如:
ts_print:单点触摸工具
ts_print_mt:多点触摸工具
ts_print:在终端打印触摸点信息
... ...
如果不想每次开机都把环境变量配置敲一遍就配置一下/etc/profile(没有这个文件就自己创建一个)
,也可以不配置
# /etc/profile
# 这是一个示例profile脚本
# No core files by default
ulimit -S -c 0 > /dev/null 2>&1
USER="`id -un`"
LOGNAME=$USER
PS1='[\u@\h \W]\# '
PATH=$PATH
HOSTNAME=`/bin/hostname`
export USER LOGNAME PS1 PATH
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
export TSLIB_TSDEVICE=/dev/input/event1
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/lib/ts
tslib的基本使用步骤:
#include "tslib.h"
struct tsdev *ts_open(const char *dev_name, int nonblock);
dev_name:触摸屏的设备节点
nonblock:
0:阻塞方式打开触摸屏设备
非0:非阻塞方式打开触摸屏设备
返回值:
成功:返回一个 struct tsdev *指针,指向触摸屏设备句柄
失败:NULL
struct tsdev *ts_setup(const char *dev_name, int nonblock)
dev_name:触摸屏的设备节点,
当设置为NULL时函数内部会读取TSLIB_TSDEVICE 环境变量,
获取该环境变量的内容以得知触摸屏的设备节点(就是之前设置的环境变量)
nonblock:
0:阻塞方式打开触摸屏设备
非0:非阻塞方式打开触摸屏设备
教程上说这个函数能对触摸屏设备进行配置,但是没有说怎么配置,也可能没找到,先空着
#include "tslib.h"
int ts_config(struct tsdev *ts)
ts:指向触摸屏句柄,就是打开成功返回的那个
返回值:
成功:0
失败:-1
配置就是解析ts.conf文件中的配置信息,加载相应的插件。
#include "tslib.h"
//读取单点触摸数据
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
samp:一个 struct ts_sample *类型的指针,指向一个struct ts_sample对象,获取到的数据会存这里面
//读取多点触摸数据
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
max_slots:触摸屏设备支持的最大触摸点数
samp :是一个 struct ts_sample_mt **类型的指针,多点触摸,每个触摸点的信息struct ts_sample_mt 数据结构来描述。
一个触摸点的数据就是struct ts_sample,这堆触摸点合起来就是struct ts_sample_mt 数组
ts:指向触摸屏设备句柄,就是打开成功返回的那个
nr:指定了 ts_read 函数要从设备中读取多少字节的数据。如果设备中没有足够的数据可供读取,则 ts_read 函数将等待,直到有足够的数据可供读取或者发生错误。,设置为1即可。
int ts_close(struct tsdev *);
//函数指针,只需要传递一个struct tsdev类型的指针就好了
struct ts_sample {
int x; //X 坐标
int y; //Y 坐标
unsigned int pressure; //按压力大小
struct timeval tv; //时间
};
struct ts_sample_mt {
/* ABS_MT_* event codes. linux/include/uapi/linux/input-event-codes.h
* has the definitions.
*/
int x; //X 坐标
int y; //Y 坐标
unsigned int pressure; //按压力大小
int slot; //触摸点 slot
int tracking_id; //ID
int tool_type;
int tool_x;
int tool_y;
unsigned int touch_major;
unsigned int width_major;
unsigned int touch_minor;
unsigned int width_minor;
int orientation;
int distance;
int blob_id;
struct timeval tv; //时间
/* BTN_TOUCH state */
short pen_down; //BTN_TOUCH 的状态
/* valid is set != 0 if this sample
* contains new data; see below for the
* bits that get set.
* valid is set to 0 otherwise
*/
short valid; //此次样本是否有效标志 触摸点数据是否发生更新
};
#include
#include
#include //包含 tslib.h 头文件
int main(int argc, char *argv[])
{
struct tsdev *ts = NULL;
struct ts_sample samp;
int pressure = 0;//用于保存上一次的按压力,初始为 0,表示松开
/* 打开并配置触摸屏设备 */
ts = ts_setup(NULL, 0);
if (NULL == ts) {
fprintf(stderr, "ts_setup error");
exit(EXIT_FAILURE);
}
/* 读数据 */
for ( ; ; ) {
if (0 > ts_read(ts, &samp, 1)) {
fprintf(stderr, "ts_read error");
ts_close(ts);
exit(EXIT_FAILURE);
}
if (samp.pressure) {//按压力>0
if (pressure) //若上一次的按压力>0
printf("移动(%d, %d)\n", samp.x, samp.y);
else
printf("按下(%d, %d)\n", samp.x, samp.y);
}
else
printf("松开\n");//打印坐标
pressure = samp.pressure;
}
ts_close(ts);
exit(EXIT_SUCCESS);
}
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
struct tsdev *ts = NULL;
struct ts_sample_mt *mt_ptr = NULL;
struct input_absinfo slot;
int max_slots;
unsigned int pressure[12] = {0}; //用于保存每一个触摸点上一次的按压力,初始为 0,表示松开
int i;
/* 打开并配置触摸屏设备 */
ts = ts_setup(NULL, 0);
if (NULL == ts) {
fprintf(stderr, "ts_setup error");
exit(EXIT_FAILURE);
}
/* 获取触摸屏支持的最大触摸点数 */
if (0 > ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot)) {
perror("ioctl error");
ts_close(ts);
exit(EXIT_FAILURE);
}
max_slots = slot.maximum + 1 - slot.minimum;
printf("max_slots: %d\n", max_slots);
/* 内存分配 */
mt_ptr = calloc(max_slots, sizeof(struct ts_sample_mt));
/* 读数据 */
for ( ; ; ) {
if (0 > ts_read_mt(ts, &mt_ptr, max_slots, 1)) {
perror("ts_read_mt error");
ts_close(ts);
free(mt_ptr);
exit(EXIT_FAILURE);
}
for (i = 0; i < max_slots; i++) {
if (mt_ptr[i].valid) {//有效表示有更新!
if (mt_ptr[i].pressure) { //如果按压力>0
if (pressure[mt_ptr[i].slot])//如果上一次的按压力>0
printf("slot<%d>, 移动(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y);
else
printf("slot<%d>, 按下(%d, %d)\n", mt_ptr[i].slot, mt_ptr[i].x, mt_ptr[i].y);
}
else
printf("slot<%d>, 松开\n", mt_ptr[i].slot);
pressure[mt_ptr[i].slot] = mt_ptr[i].pressure;
}
}
}
/* 关闭设备、释放内存、退出 */
ts_close(ts);
free(mt_ptr);
exit(EXIT_SUCCESS);
}
${CC} -I /home/xx/linux/tools/tslib/include -L /home/xx/linux/tools/tslib/lib -lts -o LCDAPP LCD.c
-I:可以让编译器在指定的目录中搜索头文件,以便正确地编译源代码文件。
/home/xx/linux/tools/tslib/include:tslib 库的头文件所在的目录。
-L:可以让编译器在指定的目录中搜索库文件,以便正确地链接库函数并生成可执行文件。
/home/xx/linux/tools/tslib/lib:tslib 库的库文件所在的目录。
-lts:指定了需要链接的库文件名,其中 -l 表示链接库文件,ts 表示库文件名为 libts.so。Linux 中,动态库文件的命名方式为 lib+名字+.so
-o:指定了编译生成的可执行文件的名称,其中LCDAPP是可执行文件的名称。
LCD.c:这个参数指定了需要编译的源代码文件的名称,其中 LCD.c 是 C 语言源代码文件的名称。