首页 > 技术知识 > 正文

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

1.输入子系统简介

  在 Linux 中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。

Linux下输入子系统上报触摸屏坐标-linux系统触摸屏怎么校准

设备驱动层

设备驱动层实现对硬件设备的各个寄存的访问,将底层硬件对用户层的响应数据转换为标准输入事件,再通过核心层提交给事件处理层。

核心层

核心层是设备驱动层和事件处理层的连接桥梁,为设备驱动层和事件处理层提供编程接口。

事件处理层

事件处理层则为用户空间提供统一访问接口,处理驱动层提交的数据,所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,只需要关心对各硬件寄存器的操作和提交的输入事件。

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”);=”” struct=”” stat=”” statbuf;=”” fstat(fontfd,&statbuf);=”” if(statbuf.st_size<=”0)goto”>*size>8;j++)”>

猜你喜欢