作者杨源鑫(也是我们的校园代理)
经授权转公众号嵌入式开发圈有些许修改。
什么是input子系统不管是什么操作系统都有一个程序用于管理各种输入设备哪些是输入设备比如电脑键盘、鼠标智能手机上的触摸屏按键。都是输入设备。那么操作系统怎么管理这些输入设备这里以最常用的Linux操作系统进行讲解。
在Linux内核中有非常多用于管理诸多设备的子系统比如显示系统输入子系统音频子系统电源管理子系统时钟管理子系统等等本节我们重点关注输入子系统。
输入子系统是在内核里实现因为设备经常要通过特定的硬件接口被访问 (例如串口 ps/2 usb等等 )这些硬件接口由内核保护和管理。内核给用户导出一套固定的与硬件无关的 input API供用户空间程序使用。
在Linux input子系统中分三块进行管理分别是 input core(输入系统核心层) drivers(输入系统驱动层)和 event handlers(输入系统事件层)可能你感觉太抽象看下图4-5-9就清楚了。
先从应用程序角度认识input子系统我们可以从以下这个文件看到对应的设备。
打开Linux终端输入命令cat /proc/bus/input/devices可以看到类似下面的内容。
I: Bus0003 Vendor046d Productc018 Version0111N: Name" USB Optical Mouse"P: Physusb-0000:00:1d.1-2/input0S: Sysfs/class/input/input24U: UniqH: Handlersmouse1 event2B: EV7B: KEY70000 0 0 0 0 0 0 0 0B: REL103
这些devices主要是用来描述注册在input子系统的设备文件可能有鼠标键盘触摸屏重力传感器温度传感器等等写驱动的时候通过内核提供的input设备注册设备相关的接口后这些信息都会保存到对应的文件里。
那么input子系统如何描述输入设备呢
Linux系统为我们提供了这个输入系统操作相关的头文件:
#include
在这个文件中可以找到这个结构体:
//用于描述一个输入事件struct input_event {struct timeval time;__u16 type;__u16 code;__s32 value;};
在这里我们看到input_event结构体中还嵌套了另一个结构体struct timeval time;
先解读struct timeval time它在time.h中定义如下
struct timeval{__time_t tv_sec; /* Seconds. */__suseconds_t tv_usec; /*Microseconds. */};
其中tv_sec为Epoch到创建struct timeval时的秒数tv_usec为微秒数即秒后面的零头。
type域是被报告事件的类型例如一个 key press或者 button press relative motion(比如移动鼠标 )或者 absolute motion(比如移动游戏杆 )
code域告诉你是哪一个key或者坐标轴在被操作
value域告诉你设备现在的状态或者运动情况是什么。
最主要的事件有以下三种: 相对事件(例如鼠标)绝对事件(例如触摸屏)键盘事件。
例如鼠标我们在移动鼠标的时候鼠标就是一个相对事件所以type的类型也就是底层上报给用户的事件为相对事件类型code表示的就是相对于鼠标当前的位置的X或者Y的坐标value也就是相对于当前的位置偏移了多少。
事件类型(type)在input.h分类如下
/** Event types*/#define EV_SYN 0x00 //同步事件,就是将结果上报给系统的过程#define EV_KEY 0x01 //按键事件#define EV_REL 0x02 //相对事件#define EV_ABS 0x03 //绝对事件本节我们来实现一个input控制鼠标的应用程序。所以还会用到以下事件/** Relative axes*///在这里我们暂时只会用REL_X和REL_Y这两个参数#define REL_X 0x00 //相对X坐标#define REL_Y 0x01 //相对Y坐标
我们可以使用cat命令来测试当前的鼠标事件到底属于哪一个事件节点如图4-5-10所示
只需切换到/dev/input找到对应的事件节点再使用cat eventx(事件节点)然后移动鼠标就可以看到数据打印啦但是这些数据我们显然是看不懂的不过可以使用测试程序将鼠标的值读出来。
接下来我们写个程序mouse.c来看看如何读取鼠标事件
#include #include #include #include #include /*struct input_event {struct timeval time;__u16 type;__u16 code;__s32 value;};*//*Event types#define EV_SYN 0x00#define EV_KEY 0x01#define EV_REL 0x02#define EV_ABS 0x03*//*Relative axes#define REL_X 0x00#define REL_Y 0x01#define REL_Z 0x02#define REL_MAX 0x0f#define REL_CNT (REL_MAX1)*///event8 mouse//event9 keyboardint main(void){//1、定义一个结构体变量用来描述input事件struct input_event event_mouse ;//2、打开input设备的事件节点我的电脑对应的鼠标事件的节点是event3//读者的电脑的设备节点可能和我的不一样可以使用cat命令去获取然后//不断尝试int fd open("/dev/input/event4",O_RDWR);int value ;int type ;int buffer[10]{0};if(-1 fd){printf("open mouse event fair!\n");return -1 ;} while(1){//3、读事件read(fd ,//4、判断事件类型并打印键码switch(event_mouse.type){//同步事件case EV_SYN:printf("sync!\n");break ;case EV_REL://鼠标事件XY相对位移//code表示相对位移X或者Y当判断是X时打印X的相对位移value//当判断是Y时打印Y的相对位移valueif(event_mouse.code REL_X){ printf("event_mouse.code_X:%d\n",event_mouse.code); printf("event_mouse.value_X:%d\n",event_mouse.value); }if(event_mouse.code REL_Y){printf("event_mouse.code_Y:%d\n",event_mouse.code); printf("event_mouse.value_Y:%d\n",event_mouse.value); }defalut:break ;}} return 0 ;}
运行结果如图4-5-11所示。 当我们不断移动鼠标的时候这些值将会被打印出来。
请思考一个问题既然我们移动鼠标能够打印数值那能不能够写一个程序控制鼠标自动移动呢肯定可以下面我们写个程序让鼠标自己画一个正方形上代码:
#include #include #include #include #include //event8 mouse//event9 keyboardint main(void){//1、定义一个结构体变量用来描述input事件struct input_event event_mouse ;//2、打开input设备的事件节点 我的电脑鼠标事件的节点是event3int fd open("/dev/input/event3",O_RDWR);int value ;int type ;int i ;int buffer[10]{0};if(-1 fd){printf("open mouse event fair!\n");return -1 ;} while(1){//3、写事件for(i 0 ; i < 20 ; i){event_mouse.type EV_REL ; event_mouse.code REL_X ;event_mouse.value i ; write(fd, event_mouse.code 0 ;event_mouse.value 0;event_mouse.type EV_SYN ;write(fd, usleep(50000);}for(i 0 ; i -20 ; i--){event_mouse.type EV_REL ; event_mouse.code REL_X ;event_mouse.value i ; write(fd, event_mouse.code 0 ;event_mouse.value 0;event_mouse.type EV_SYN ;write(fd, usleep(50000);}for(i 0 ; i > -20 ; i--){event_mouse.type EV_REL ; event_mouse.code REL_Y ;event_mouse.value i ; write(fd, event_mouse.code 0 ; event_mouse.value 0 ;event_mouse.type EV_SYN ;write(fd, usleep(50000);}} return 0 ;}
执行效果请读者自行验证。
接下来我们再写一个案例在Tiny4412平台上获取电容屏的坐标值。
触摸屏上报坐标值的事件属于绝对事件也就是触摸的坐标点X和Y会在屏幕的分辨率范围内上报一个绝对的坐标(X,Y)。
那么上报对于的类型(type)如下EV_ABS
对于的code如下
绝对于X
ABS_MT_POSITION_X
绝对于Y
ABS_MT_POSITION_Y
我用了一个程序获取了屏幕的分辨率得知分辨率宽为480高为800。
首先写这个程序时我通过adb进到Android根目录然后用getevent -p查到触摸屏的事件节点为event0, 同时也知道触摸屏是一个绝对事件如下:
接下来我在Android5.0的源代码external目录下创建了如下目录Getft5x0x_Test
该目录下有如下两个文件文件:
Android.mk和Get_ft5x0x_tp.c
(1) 先看Android.mk
LOCAL_PATH : $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS : engLOCAL_SHARED_LIBRARIES libcutils libutils#LOCAL_STATIC_LIBRARIES libz libstdc libpng libvtpngLOCAL_STATIC_LIBRARIES libz libstdc libpngLOCAL_SRC_FILES : Get_ft5x0x_tp.cLOCAL_MODULE : ft5x0x_tpinclude $(BUILD_EXECUTABLE)
(2)Get_ft5x0x_tp.c
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ft5x0x_ts触摸屏事件初始化18int touch_fd -1 ;19int ft5x0x_ts__init(void){touch_fd open("/dev/input/event0", O_RDONLY);if (touch_fd < 0){printf("open /dev/input/event0 failed\n");return -1;}return 0;}//获取ft5x0x_ts触摸屏上的坐标点int Get_ft5x0x_ts_postion(int *x, int *y){int touch_ret -1 ; //1、定义一个结构体变量用来描述ft5x0x触摸屏事件 struct input_event ft5x0x_ts ; //2、读事件 touch_ret read(touch_fd , if(touch_ret < 0){printf("read touch fair!\n");}//3、判断事件类型switch(ft5x0x_ts.type){ case EV_SYN: break ; case EV_ABS: if(ft5x0x_ts.code ABS_MT_POSITION_X){ *x ft5x0x_ts.value ;} if(ft5x0x_ts.code ABS_MT_POSITION_Y){ *y ft5x0x_ts.value ;} defalut: break ; } return 0;}int main(int argc, char **argv){int tp_ret ;int ft5x0x_x 0;int ft5x0x_y 0; tp_ret ft5x0x_ts__init();if(-1 tp_ret){printf("tp init fair!\n");return -1 ;}printf("tp init success!\n");while(1){ //获取屏幕上的绝对坐标点Get_ft5x0x_ts_postion(printf("ft5x0x_x:%d ft5x0x_y:%d\n",ft5x0x_x,ft5x0x_y);usleep(100);} return 0;}
编写完Android.mk和C程序后切换到Android的根目录用以下命令编译Get_ft5x0x_tp.c
(使用mmm命令之前一定要先执行source和lunch这两个步骤
rootmorixinguan:/work/android-5.0.2# source build/envsetup.sh including device/samsung/manta/vendorsetup.shincluding device/moto/shamu/vendorsetup.shincluding device/friendly-arm/tiny4412/vendorsetup.shincluding device/generic/mini-emulator-x86_64/vendorsetup.shincluding device/generic/mini-emulator-armv7-a-neon/vendorsetup.shincluding device/generic/mini-emulator-mips/vendorsetup.shincluding device/generic/mini-emulator-arm64/vendorsetup.shincluding device/generic/mini-emulator-x86/vendorsetup.shincluding device/asus/deb/vendorsetup.shincluding device/asus/fugu/vendorsetup.shincluding device/asus/grouper/vendorsetup.shincluding device/asus/tilapia/vendorsetup.shincluding device/asus/flo/vendorsetup.shincluding device/lge/hammerhead/vendorsetup.shincluding device/lge/mako/vendorsetup.shincluding sdk/bash_completion/adb.bashrootmorixinguan:/work/android-5.0.2# lunch Youre building on LinuxLunch menu... pick a combo:. aosp_arm-eng. aosp_arm64-eng. aosp_mips-eng. aosp_mips64-eng. aosp_x86-eng. aosp_x86_64-eng. aosp_manta-userdebug. aosp_shamu-userdebug. full_tiny4412-userdebug. full_tiny4412-eng. mini_emulator_x86_64-userdebug. m_e_arm-userdebug. mini_emulator_mips-userdebug. mini_emulator_arm64-userdebug. mini_emulator_x86-userdebug. aosp_deb-userdebug. full_fugu-userdebug. aosp_fugu-userdebug. aosp_grouper-userdebug. aosp_tilapia-userdebug. aosp_flo-userdebug. aosp_hammerhead-userdebug. aosp_mako-userdebugWhich would you like? [aosp_arm-eng] 20PLATFORM_VERSION_CODENAMERELPLATFORM_VERSION5.0.2TARGET_PRODUCTaosp_tilapiaTARGET_BUILD_VARIANTuserdebugTARGET_BUILD_TYPEreleaseTARGET_BUILD_APPSTARGET_ARCHarmTARGET_ARCH_VARIANTarmv7-a-neonTARGET_CPU_VARIANTcortex-a9TARGET_2ND_ARCHTARGET_2ND_ARCH_VARIANTTARGET_2ND_CPU_VARIANTHOST_ARCHx86_64HOST_OSlinuxHOST_OS_EXTRALinux-4.8.0-46-generic-x86_64-with-Ubuntu-16.04-xenialHOST_BUILD_TYPEreleaseBUILD_IDLRX22GOUT_DIRoutrootmorixinguan:/work/android-5.0.2#
接下来编译程序 :
mmm external/Getft5x0x_Test/
然后我们看到以下显示:
这个二进制生成的绝对路径是out目录, 我们需要ft5x0x_tp文件这个名字就是上面Android.mk里面对应的:
LOCAL_MODULE : ft5x0x_tp
Install: out/target/product/tiny4412/system/bin/ft5x0x_tp 将这个文件拷贝到当前目录:
cp out/target/product/tiny4412/system/bin/ft5x0x_tp .
然后用USB线连接tiny4412开发板再用adb命令将ft5x0x_tp push到system/bin/目录下这个目录是Android的根文件系统下的一个命令很多命令都在这个目录下。
若没有安装adb可以apt-get install adb 进行安装)
adb push完毕以后再adb shell切换到根目录下
执行ft5x0x_tp.bin然后触摸触摸屏这样坐标值就打印出来了。
ps韦东山2期视频对输入子系统的驱动编写以及机制讲解的很清楚有需要的可以看看
--END--
关注公众号百问科技(IDbaiwenkeji第一时间阅读嵌入式干货。 技术交流加个人威信13266630429验证CSDN博客