Linux下输入子系统上报触摸屏坐标
1.输入子系统简介
在 Linux 中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。
设备驱动层
设备驱动层实现对硬件设备的各个寄存的访问,将底层硬件对用户层的响应数据转换为标准输入事件,再通过核心层提交给事件处理层。
核心层
核心层是设备驱动层和事件处理层的连接桥梁,为设备驱动层和事件处理层提供编程接口。
事件处理层
事件处理层则为用户空间提供统一访问接口,处理驱动层提交的数据,所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,只需要关心对各硬件寄存器的操作和提交的输入事件。
2.输入子系统好处
统一了物理形态各异的相似的输入设备的处理功能。例如,各种鼠标,不论 PS/2、 USB、还是蓝牙,都被同样处理。输入子系统常见事件类型为:按键事件(如键盘)、相对坐标事件(如鼠标)、绝对坐标事件(如触摸屏)。
提供了用于分发输入报告给用户应用程序的简单的事件( event)接口。你的驱动不必创建、管理/dev节点以及相关的访问方法。因此它能够很方便的调用输入 API 以发送鼠标移动、键盘按键,或触摸事件给用户空间。
抽取出了输入驱动的通用部分,简化了驱动,并提供了一致性。例如,输入子系统提供了一个底层驱动(成为 serio)的集合,支持对串口和键盘控制器等硬件输入的访问。
3.输入子系统相关接口函数
struct input_dev 结构体
结构体 input_dev 表示底层硬件设备,是所有输入设备的抽象。驱动层需要实现对input_dev 结构体的填充。
struct input_dev { const char *name; //设备名字--比如:键盘的名字 const char *phys; //设备在系统中的路径。比如:input/key0 const char *uniq; //唯一ID号 struct input_id id; //用于匹配事件处理层 handler unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //记录支持的事件 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按键事件 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//相对坐标 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//绝对坐标 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; unsigned int hint_events_per_packet; unsigned int keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode); int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke); struct ff_device *ff; unsigned int repeat_key; struct timer_list timer; int rep[REP_CNT]; struct input_mt_slot *mt; int mtsize; int slot; int trkid; struct input_absinfo *absinfo; unsigned long key[BITS_TO_LONGS(KEY_CNT)]; unsigned long led[BITS_TO_LONGS(LED_CNT)]; unsigned long snd[BITS_TO_LONGS(SND_CNT)]; unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //文件操作函数 ,可以自行实现 int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); struct input_handle __rcu *grab; spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; bool sync;//最后一次同步后没有新的事件置 1 struct device dev; struct list_head h_list; struct list_head node; };
struct input_event 结构体
该结构体一般在应用层调用,用户接收事件层上报的数据内容。
struct input_event { struct timeval time; //时间戳 __u16 type;//事件类型EV_KEY、EV_REL、EV_ABS __u16 code;//事件数据值,若按键事件,则保证按键键值;若坐标信息,则表明为x,y __s32 value;//标志值,若按键,则表示按下还是松开;若坐标,则表示位具体的坐标值 };
动态分配和释放inptu_dev结构体函数
//动态分配input_dev结构体 struct input_dev *input_allocate_device(void) //释放input_dev结构体 void input_free_device(struct input_dev *dev)
注册和注销输入子系统
//注册输入子系统 int input_register_device(struct input_dev *dev) //注销输入子系统 void input_free_device(struct input_dev *dev) 形参: input_dev --输入设备结构体 返回值: 注册成功返回0,失败返回其它值
设置上报的数据内容input_set_capability
input_set_capability函数用于填充input_dev结构体,设置要报的数据类型和数据信息。
void input_set_capability(struct input_dev *dev, unsigned int type,unsigned int code) 形参: dev --input_dev结构体 type --事件类型EV_KEY、EV_REL、EV_ABS code --要上报的具体值 例:input_set_capability(dev,EV_KEY,KEY_A);//上报按键事件,上报的键值为’A’
设置上报的数据内容__set_bit
通过设置位的函数实现inptu_dev结构体填充,input_set_capability函数的内部就是通过调用__set_bit函数来实现的。
inline void __set_bit(int nr, volatile unsigned long *addr) 形参: nr–要上报的具体值 addr --设置的地址 上报按键事件例: __set_bit(EV_KEY,dev->evbit);//设置事件属性为按键事件 __set_bit(KEY_A,dev->keybit);//设置上报的键值 设置重复上报例:__set_bit(EV_REP,dev->evbit);
设置上报的值的范围input_set_abs_params
input_set_abs_params函数用于设置上报的数值的取值范围。
上报数据到事件处理层
//上报按键事件键值,如键盘 inline void input_report_key(struct input_dev *dev, unsigned int code, int value); //上报相对事件坐标值,如鼠标 inline void input_report_rel(struct input_dev *dev, unsigned int code, int value); //上报绝对事件坐标值,如触摸屏 inline void input_report_abs(struct input_dev *dev, unsigned int code, int value); 形参: dev --input_dev结构体 code --事件数据值,若按键事件,则保证按键键值;若坐标信息,则表明为x,y value --标志值,若按键,则表示按下还是松开;若坐标,则表示位具体的坐标值
这几个函数完成数据上报内部靠input_event函数实现。
事件同步input_mt_sync
void input_mt_sync(struct input_dev *dev) 形参: dev --input_dev结构体
在完成数据上报后一定要调用事件同步函数。
4.输入子系统上报触摸屏坐标示例
硬件平台:tiny4412
开发平台:ubuntu18.04
交叉编译器:arm-linux-gcc
内核:linux3.5
触摸屏驱动IC:ft5X06
ft5x06驱动示例参考:Linux下IIC子系统和触摸屏驱动
输入子系统注册上报数据示例
#include #include #include #include #include #include #include #include #include #include #include static struct work_struct touch_work; static struct i2c_client *touch_client; static struct input_dev *touch_dev=NULL; /*工作处理函数*/ static void touch_work_func(struct work_struct *work) { u8 touch_buff[7]; int x,y; int num; i2c_smbus_read_i2c_block_data(touch_client,0, 7,touch_buff); num=touch_buff[2]&0xf;//触控点个数 x=((touch_buff[3]&0xf)<<8)|touch_buff[4]; y=((touch_buff[5]&0xf)<<8)|touch_buff[6]; //printk("(x,y)=%d,%dtnum=%dn",x,y,num); if(num) { input_report_abs(touch_dev,ABS_X,x);//上报x坐标 input_report_abs(touch_dev,ABS_Y,y);//上报x坐标 input_report_abs(touch_dev,ABS_PRESSURE,1);//压力值,1表示按下 input_report_key(touch_dev,BTN_TOUCH,1);//按下 } else { input_report_abs(touch_dev,ABS_PRESSURE,0);//压力值,0表示松开 input_report_key(touch_dev,BTN_TOUCH,0);//释放 } input_sync(touch_dev);//同步 } /*中断处理函数*/ static irqreturn_t touch_irq_work(int irq, void *dev) { schedule_work(&touch_work);//调度工作 return IRQ_HANDLED; } static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)//资源匹配函数 { int ret; printk("资源匹配成功n"); printk("name=%staddr=%#xtirq=%dn",client->name,client->addr,client->irq); touch_client=client; /*动态分配input_dev结构体*/ touch_dev=input_allocate_device(); if(!touch_dev)return -1;//动态分配失败 /*设置要上报的数据内容*/ input_set_capability(touch_dev,EV_ABS,ABS_X);//上报x坐标 input_set_capability(touch_dev,EV_ABS,ABS_Y);//上报x坐标 input_set_capability(touch_dev,EV_ABS,ABS_PRESSURE);//压力值 input_set_capability(touch_dev,EV_KEY,BTN_TOUCH);//触摸屏点击事件 /*设置xy取值范围*/ input_set_abs_params(touch_dev,ABS_X,0,800,0,0);//设置x坐标范围 input_set_abs_params(touch_dev,ABS_Y,0,480,0,0);//设置y坐标范围 input_set_abs_params(touch_dev,ABS_PRESSURE,0,1,0,0);//设置压力值 /*注册输入子系统*/ ret=input_register_device(touch_dev); if(ret)return ret;//注册输入子系统设备失败 /*1.初始化工作*/ INIT_WORK(&touch_work, touch_work_func); /*注册中断*/ ret=request_irq(client->irq,touch_irq_work,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"ft5x06",NULL); if(ret) { printk("中断注册失败n"); return -1; } return 0; } static int ft5x06_remove(struct i2c_client *client)//资源释放函数 { printk("IIC驱动程资源释放成功n"); free_irq(client->irq,NULL);//注销中断 /*注销输入子系统设备*/ input_unregister_device(touch_dev); /*释放input_dev结构体*/ input_free_device(touch_dev); return 0; } //资源匹配结构体 static struct i2c_device_id id_table[]= { {"touch_ft5x06",0}, {}, }; static struct i2c_driver ft5x06_drv= { .probe=ft5x06_probe, .remove=ft5x06_remove, .driver= { .name="touch_drv", }, .id_table=id_table,//资源匹配结构体 }; static int __init wbyq_ft5x06_drv_init(void) { i2c_add_driver(&ft5x06_drv); return 0; } /*驱动释放*/ static void __exit wbyq_ft5x06_drv_cleanup(void) { i2c_del_driver(&ft5x06_drv); printk("IIC驱动层注销成功n"); } module_init(wbyq_ft5x06_drv_init);//驱动入口函数 module_exit(wbyq_ft5x06_drv_cleanup);//驱动出口函数 MODULE_LICENSE("GPL");//驱动注册协议 MODULE_AUTHOR("it_ashui"); MODULE_DESCRIPTION("Exynos4 ft5x06_drv Driver");
应用层读取触摸屏坐标示例
#include #include #include #include #include #include #include #include #include #include #include #include typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; static unsigned char *lcd_p=NULL;//屏幕缓存地址 static unsigned char *gbk_addr=NULL;//屏幕缓存地址 static struct fb_fix_screeninfo fb_fix;//固定参数结构体 static struct fb_var_screeninfo fb_var;//可变参数结构体 extern const unsigned char ascii_32_16[][32*16/8];//逐列式,高位在前 /*LCD画点函数*/ static inline void LCD_DrawPoint(int x,int y,int c) { //获取要绘制的点的地址 unsigned int *p= (unsigned int *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8); *p=c;//写入颜色值 } /* 显示汉字 x,y --要显示的位置 size --字体大小 font --要显示的汉字 c -- 颜色值 */ static void LCD_DisplayFont(int x,int y,int size,char *font,int c) { u8 *p=NULL; u8 H,L; u32 addr=0;//汉字偏移地址 u16 font_size=size*size/8;//汉字点阵大小(宽度保证为8的倍数) H=*font;//汉字高字节 L=*(font+1);//汉字的低字节 if(L<0x7F)L-=0x40; else L-=0x41; H-=0x81; addr=(190*H+L)*font_size;//汉字所在点阵中的偏移地址 p=malloc(font_size); if(p==NULL) { printf("申请空间失败rn"); return ; } memcpy(p,gbk_addr+addr,font_size);//读取点阵码数据 int i,j; int x0=x; unsigned char tmep; for(i=0;i=size) { x0=x; y++; } } } /* 显示字符 x,y --要显示的位置 h,w -- 字符高和宽 cha --要显示的字符 c -- 颜色值 取模走向:逐列式,高位在前 */ static void LCD_DisplayCha(int x,int y,int h,int w,char cha,int c) { int i,j; int y0=y; u8 temp; for(i=0;i=' ' && *str<='~')//字符显示 { LCD_DisplayCha(x0,y,size,size/2,*str,c); str++; x0+=size/2; } else str++; } } int main() { /*1.打开设备*/ int fd=open("/dev/fb0", 2); if(fd<0) { printf("打开设备失败n"); } /*2.获取固定参数*/ memset(&fb_fix,0, sizeof(fb_fix)); ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix); printf("屏幕缓存大小:%dn",fb_fix.smem_len); printf("一行的字节数:%dn",fb_fix.line_length); /*3.获取屏幕可变参数*/ memset(&fb_var,0, sizeof(fb_var)); ioctl(fd,FBIOGET_VSCREENINFO,&fb_var); printf("屏幕尺寸:%d*%dn",fb_var.xres,fb_var.yres); printf("颜色位数:%dn",fb_var.bits_per_pixel); /*4.将屏幕缓冲区映射到进程空间*/ lcd_p=mmap(NULL,fb_fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); close(fd); if(lcd_p==(void *)-1) { printf("内存映射失败n"); return 0; } /*打开字库文件*/ int fontfd=open("GBK_32.DZK",2); if(fontfd<0) { printf("字库文件打开失败n"); return 0; } struct stat statbuf; fstat(fontfd,&statbuf); if(statbuf.st_size<=0)goto AA; gbk_addr=mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fontfd,0); close(fontfd); if(gbk_addr==(void *)-1)goto AA; memset(lcd_p,0xff,fb_fix.smem_len);//将屏幕清空为白色 LCD_DisplayStr(300,50,32,"触摸屏驱动测试",0x45ff); /*打开触摸屏设备*/ fd=open("/dev/input/event1",2); if(fd<0) { printf("触摸屏驱动打开失败n"); return 0; } struct pollfd fds= { .fd=fd, .events=POLLIN, }; /* struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; */ struct input_event touchxy; while(1) { poll(&fds,1,-1); read(fd,&touchxy,sizeof(touchxy)); switch(touchxy.type) { case EV_KEY://按键值 printf("key=%dtstat=%dn",touchxy.code,touchxy.value); break; case EV_ABS://绝对坐标 if(touchxy.code == ABS_X)//x坐标 { printf("x=%dn",touchxy.value); } else if(touchxy.code == ABS_Y)//Y坐标 { printf("y=%dn",touchxy.value); } else if(touchxy.code == ABS_PRESSURE)//压力值 { printf("press=%dn",touchxy.value); } break; } } munmap(gbk_addr,statbuf.st_size); AA: //取消映射 munmap(lcd_p,fb_fix.smem_len); return 0; } 8;j++)">*size>8;j++)">
-
触摸屏
+关注
关注
42文章
2304浏览量
116173 -
Linux
+关注
关注
87文章
11302浏览量
209434 -
子系统
+关注
关注
0文章
109浏览量
12398
发布评论请先 登录
相关推荐
评论