Linux下輸入子系統(tǒng)上報觸摸屏坐標(biāo)
1.輸入子系統(tǒng)簡介
??在 Linux 中,輸入子系統(tǒng)是由輸入子系統(tǒng)設(shè)備驅(qū)動層、輸入子系統(tǒng)核心層(Input Core)和輸入子系統(tǒng)事件處理層(Event Handler)組成。
設(shè)備驅(qū)動層
設(shè)備驅(qū)動層實現(xiàn)對硬件設(shè)備的各個寄存的訪問,將底層硬件對用戶層的響應(yīng)數(shù)據(jù)轉(zhuǎn)換為標(biāo)準(zhǔn)輸入事件,再通過核心層提交給事件處理層。
核心層
核心層是設(shè)備驅(qū)動層和事件處理層的連接橋梁,為設(shè)備驅(qū)動層和事件處理層提供編程接口。
事件處理層
事件處理層則為用戶空間提供統(tǒng)一訪問接口,處理驅(qū)動層提交的數(shù)據(jù),所以這使得我們輸入設(shè)備的驅(qū)動部分不在用關(guān)心對設(shè)備文件的操作,只需要關(guān)心對各硬件寄存器的操作和提交的輸入事件。
2.輸入子系統(tǒng)好處
統(tǒng)一了物理形態(tài)各異的相似的輸入設(shè)備的處理功能。例如,各種鼠標(biāo),不論 PS/2、 USB、還是藍牙,都被同樣處理。輸入子系統(tǒng)常見事件類型為:按鍵事件(如鍵盤)、相對坐標(biāo)事件(如鼠標(biāo))、絕對坐標(biāo)事件(如觸摸屏)。
提供了用于分發(fā)輸入報告給用戶應(yīng)用程序的簡單的事件( event)接口。你的驅(qū)動不必創(chuàng)建、管理/dev節(jié)點以及相關(guān)的訪問方法。因此它能夠很方便的調(diào)用輸入 API 以發(fā)送鼠標(biāo)移動、鍵盤按鍵,或觸摸事件給用戶空間。
抽取出了輸入驅(qū)動的通用部分,簡化了驅(qū)動,并提供了一致性。例如,輸入子系統(tǒng)提供了一個底層驅(qū)動(成為 serio)的集合,支持對串口和鍵盤控制器等硬件輸入的訪問。
3.輸入子系統(tǒng)相關(guān)接口函數(shù)
struct input_dev 結(jié)構(gòu)體
??結(jié)構(gòu)體 input_dev 表示底層硬件設(shè)備,是所有輸入設(shè)備的抽象。驅(qū)動層需要實現(xiàn)對input_dev 結(jié)構(gòu)體的填充。
struct input_dev { const char *name; //設(shè)備名字--比如:鍵盤的名字 const char *phys; //設(shè)備在系統(tǒng)中的路徑。比如: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)];//相對坐標(biāo) unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//絕對坐標(biāo) 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)]; //文件操作函數(shù) ,可以自行實現(xiàn) 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 結(jié)構(gòu)體
??該結(jié)構(gòu)體一般在應(yīng)用層調(diào)用,用戶接收事件層上報的數(shù)據(jù)內(nèi)容。
struct input_event { struct timeval time; //時間戳 __u16 type;//事件類型EV_KEY、EV_REL、EV_ABS __u16 code;//事件數(shù)據(jù)值,若按鍵事件,則保證按鍵鍵值;若坐標(biāo)信息,則表明為x,y __s32 value;//標(biāo)志值,若按鍵,則表示按下還是松開;若坐標(biāo),則表示位具體的坐標(biāo)值 };
動態(tài)分配和釋放inptu_dev結(jié)構(gòu)體函數(shù)
//動態(tài)分配input_dev結(jié)構(gòu)體 struct input_dev *input_allocate_device(void) //釋放input_dev結(jié)構(gòu)體 void input_free_device(struct input_dev *dev)
注冊和注銷輸入子系統(tǒng)
//注冊輸入子系統(tǒng) int input_register_device(struct input_dev *dev) //注銷輸入子系統(tǒng) void input_free_device(struct input_dev *dev) 形參: input_dev --輸入設(shè)備結(jié)構(gòu)體 返回值: 注冊成功返回0,失敗返回其它值
設(shè)置上報的數(shù)據(jù)內(nèi)容input_set_capability
??input_set_capability函數(shù)用于填充input_dev結(jié)構(gòu)體,設(shè)置要報的數(shù)據(jù)類型和數(shù)據(jù)信息。
void input_set_capability(struct input_dev *dev, unsigned int type,unsigned int code) 形參: dev --input_dev結(jié)構(gòu)體 ???type --事件類型EV_KEY、EV_REL、EV_ABS ???code --要上報的具體值 例:input_set_capability(dev,EV_KEY,KEY_A);//上報按鍵事件,上報的鍵值為’A’
設(shè)置上報的數(shù)據(jù)內(nèi)容__set_bit
??通過設(shè)置位的函數(shù)實現(xiàn)inptu_dev結(jié)構(gòu)體填充,input_set_capability函數(shù)的內(nèi)部就是通過調(diào)用__set_bit函數(shù)來實現(xiàn)的。
inline void __set_bit(int nr, volatile unsigned long *addr) 形參: nr–要上報的具體值 ???addr --設(shè)置的地址 上報按鍵事件例: ? __set_bit(EV_KEY,dev->evbit);//設(shè)置事件屬性為按鍵事件 ? __set_bit(KEY_A,dev->keybit);//設(shè)置上報的鍵值 設(shè)置重復(fù)上報例:__set_bit(EV_REP,dev->evbit);
設(shè)置上報的值的范圍input_set_abs_params
??input_set_abs_params函數(shù)用于設(shè)置上報的數(shù)值的取值范圍。
上報數(shù)據(jù)到事件處理層
//上報按鍵事件鍵值,如鍵盤 inline void input_report_key(struct input_dev *dev, unsigned int code, int value); //上報相對事件坐標(biāo)值,如鼠標(biāo) inline void input_report_rel(struct input_dev *dev, unsigned int code, int value); //上報絕對事件坐標(biāo)值,如觸摸屏 inline void input_report_abs(struct input_dev *dev, unsigned int code, int value); 形參: dev --input_dev結(jié)構(gòu)體 ??? code --事件數(shù)據(jù)值,若按鍵事件,則保證按鍵鍵值;若坐標(biāo)信息,則表明為x,y ??? value --標(biāo)志值,若按鍵,則表示按下還是松開;若坐標(biāo),則表示位具體的坐標(biāo)值
?這幾個函數(shù)完成數(shù)據(jù)上報內(nèi)部靠input_event函數(shù)實現(xiàn)。
事件同步input_mt_sync
void input_mt_sync(struct input_dev *dev) 形參: dev --input_dev結(jié)構(gòu)體
??在完成數(shù)據(jù)上報后一定要調(diào)用事件同步函數(shù)。
4.輸入子系統(tǒng)上報觸摸屏坐標(biāo)示例
硬件平臺:tiny4412
開發(fā)平臺:ubuntu18.04
交叉編譯器:arm-linux-gcc
內(nèi)核:linux3.5
觸摸屏驅(qū)動IC:ft5X06
ft5x06驅(qū)動示例參考:Linux下IIC子系統(tǒng)和觸摸屏驅(qū)動
輸入子系統(tǒng)注冊上報數(shù)據(jù)示例
#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; /*工作處理函數(shù)*/ 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;//觸控點個數(shù) 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坐標(biāo) input_report_abs(touch_dev,ABS_Y,y);//上報x坐標(biāo) 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);//同步 } /*中斷處理函數(shù)*/ static irqreturn_t touch_irq_work(int irq, void *dev) { schedule_work(&touch_work);//調(diào)度工作 return IRQ_HANDLED; } static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)//資源匹配函數(shù) { int ret; printk("資源匹配成功n"); printk("name=%staddr=%#xtirq=%dn",client->name,client->addr,client->irq); touch_client=client; /*動態(tài)分配input_dev結(jié)構(gòu)體*/ touch_dev=input_allocate_device(); if(!touch_dev)return -1;//動態(tài)分配失敗 /*設(shè)置要上報的數(shù)據(jù)內(nèi)容*/ input_set_capability(touch_dev,EV_ABS,ABS_X);//上報x坐標(biāo) input_set_capability(touch_dev,EV_ABS,ABS_Y);//上報x坐標(biāo) input_set_capability(touch_dev,EV_ABS,ABS_PRESSURE);//壓力值 input_set_capability(touch_dev,EV_KEY,BTN_TOUCH);//觸摸屏點擊事件 /*設(shè)置xy取值范圍*/ input_set_abs_params(touch_dev,ABS_X,0,800,0,0);//設(shè)置x坐標(biāo)范圍 input_set_abs_params(touch_dev,ABS_Y,0,480,0,0);//設(shè)置y坐標(biāo)范圍 input_set_abs_params(touch_dev,ABS_PRESSURE,0,1,0,0);//設(shè)置壓力值 /*注冊輸入子系統(tǒng)*/ ret=input_register_device(touch_dev); if(ret)return ret;//注冊輸入子系統(tǒng)設(shè)備失敗 /*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)//資源釋放函數(shù) { printk("IIC驅(qū)動程資源釋放成功n"); free_irq(client->irq,NULL);//注銷中斷 /*注銷輸入子系統(tǒng)設(shè)備*/ input_unregister_device(touch_dev); /*釋放input_dev結(jié)構(gòu)體*/ input_free_device(touch_dev); return 0; } //資源匹配結(jié)構(gòu)體 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,//資源匹配結(jié)構(gòu)體 }; static int __init wbyq_ft5x06_drv_init(void) { i2c_add_driver(&ft5x06_drv); return 0; } /*驅(qū)動釋放*/ static void __exit wbyq_ft5x06_drv_cleanup(void) { i2c_del_driver(&ft5x06_drv); printk("IIC驅(qū)動層注銷成功n"); } module_init(wbyq_ft5x06_drv_init);//驅(qū)動入口函數(shù) module_exit(wbyq_ft5x06_drv_cleanup);//驅(qū)動出口函數(shù) MODULE_LICENSE("GPL");//驅(qū)動注冊協(xié)議 MODULE_AUTHOR("it_ashui"); MODULE_DESCRIPTION("Exynos4 ft5x06_drv Driver");
應(yīng)用層讀取觸摸屏坐標(biāo)示例
#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;//固定參數(shù)結(jié)構(gòu)體 static struct fb_var_screeninfo fb_var;//可變參數(shù)結(jié)構(gòu)體 extern const unsigned char ascii_32_16[][32*16/8];//逐列式,高位在前 /*LCD畫點函數(shù)*/ 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的倍數(shù)) H=*font;//漢字高字節(jié) L=*(font+1);//漢字的低字節(jié) 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);//讀取點陣碼數(shù)據(jù) 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.打開設(shè)備*/ int fd=open("/dev/fb0", 2); if(fd<0) { printf("打開設(shè)備失敗n"); } /*2.獲取固定參數(shù)*/ memset(&fb_fix,0, sizeof(fb_fix)); ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix); printf("屏幕緩存大小:%dn",fb_fix.smem_len); printf("一行的字節(jié)數(shù):%dn",fb_fix.line_length); /*3.獲取屏幕可變參數(shù)*/ memset(&fb_var,0, sizeof(fb_var)); ioctl(fd,FBIOGET_VSCREENINFO,&fb_var); printf("屏幕尺寸:%d*%dn",fb_var.xres,fb_var.yres); printf("顏色位數(shù):%dn",fb_var.bits_per_pixel); /*4.將屏幕緩沖區(qū)映射到進程空間*/ 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èi)存映射失敗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,"觸摸屏驅(qū)動測試",0x45ff); /*打開觸摸屏設(shè)備*/ fd=open("/dev/input/event1",2); if(fd<0) { printf("觸摸屏驅(qū)動打開失敗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://絕對坐標(biāo) if(touchxy.code == ABS_X)//x坐標(biāo) { printf("x=%dn",touchxy.value); } else if(touchxy.code == ABS_Y)//Y坐標(biāo) { 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++)">
-
觸摸屏
+關(guān)注
關(guān)注
42文章
2301瀏覽量
116115 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209318 -
子系統(tǒng)
+關(guān)注
關(guān)注
0文章
109瀏覽量
12392
發(fā)布評論請先 登錄
相關(guān)推薦
評論