Linux下输入子系统上报触摸屏坐标

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)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_size8;j++)>  

窄带物联网未来的技术演进及发展趋势探讨
OTP与EFuse有什么不同呢?
RC电路的时间常数的求法以及实现低通与高通的RC电路
笔电市场低迷冲击加重 处理器大厂纷另起炉灶
南京市发布2021年经济社会发展重大项目计划
Linux下输入子系统上报触摸屏坐标
光通信和光纤通信的区别
光缆反复弯曲试验机的产品特点/技术参数/安装要求
盘点CES热点新品,2010哪些领域最值得投入?
自动驾驶合成数据科普一:不做真实数据的“颠覆者”,做“杠杆”
电控箱是不是电工做的?
为什么iPhone 12屏幕会集体变绿?
WiFi保护设计的诞生背景与工作原理
高速电机的起动问题及采用ML4425的解决方案介绍
小爱音箱mini评测 入门智能音箱首选
功率器件封装结构热设计综述
盘点一下PostgreSQL的几种常用脱敏方式
虚拟现实技术在高端制造行业的应用介绍
MOSFET好坏的判断方法
携程又现“大数据杀熟”,网友:你咋不往便宜Bug呢?