? ? 大家好,我是ST。
? ? 今天主要和大家聊一聊,如何使用標(biāo)準(zhǔn)輸入設(shè)備,進(jìn)行控制信息的識(shí)別。
第一:按鍵應(yīng)用編程方法
? ? ?編寫一個(gè)應(yīng)用程序,獲取按鍵狀態(tài),判斷按鍵當(dāng)前是按下,松開或長(zhǎng)按狀態(tài)。
?
#以字母A鍵為例 KEY_A????//上報(bào)KEY_A事件 SYN_REPORT //同步
?
? ? ??如果是按下,則上報(bào)KEY_A事件時(shí),value=1;如果是松開,則value=0;如果長(zhǎng)按,則value=2。接下來(lái)編寫按鈕應(yīng)用程序,讀取按鍵狀態(tài)并將結(jié)果打印出來(lái),代碼如下所示。
?
#include#include #include #include #include #include #include int main(int argc, char *argv[]) { struct input_event in_ev = {0}; int fd = -1; int value = -1; /* 校驗(yàn)傳參 */ if (2 != argc) { fprintf(stderr, "usage: %s ", argv[0]); exit(-1); } /* 打開文件 */ if (0 > (fd = open(argv[1], O_RDONLY))) { perror("open error"); exit(-1); } for ( ; ; ) { /* 循環(huán)讀取數(shù)據(jù) */ if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))) { perror("read error"); exit(-1); } if (EV_KEY == in_ev.type) { //按鍵事件 switch (in_ev.value) { case 0: printf("code<%d>: 松開 ", in_ev.code); break; case 1: printf("code<%d>: 按下 ", in_ev.code); break; case 2: printf("code<%d>: 長(zhǎng)按 ", in_ev.code); break; } } } }
?
?? ?在for循環(huán)中,調(diào)用read()讀取輸入設(shè)備上報(bào)的數(shù)據(jù),當(dāng)按鍵按下或松開(以及長(zhǎng)按)動(dòng)作發(fā)生時(shí),read()會(huì)讀取到輸入設(shè)備上報(bào)的數(shù)據(jù),首先判斷此次上報(bào)的事件是否是按鍵類事件(EV_KEY),如果是按鍵類事件,接著根據(jù)value值來(lái)判斷按鍵當(dāng)前的狀態(tài)是松開、按下還是長(zhǎng)按。
? ??將編譯得到的可執(zhí)行文件復(fù)制到開發(fā)板Linux系統(tǒng)的家目錄下:
注意:除了能夠測(cè)試KEY0按鍵之外,還可以測(cè)試鍵盤上的按鍵,可以找到一個(gè)USB鍵盤連接到開發(fā)板的USB HOST接口上,當(dāng)鍵盤插入之后,終端將會(huì)打印出相應(yīng)的驅(qū)動(dòng)加載信息。
? ? ?驅(qū)動(dòng)加載成功之后,可以查看下該鍵盤設(shè)備對(duì)應(yīng)的設(shè)備節(jié)點(diǎn),使用命令"cat /proc/bus/input/devices",在打印信息中找到鍵盤設(shè)備的信息:
? ? ? 操作的時(shí)候,可以對(duì)應(yīng)相應(yīng)的設(shè)備節(jié)點(diǎn)/dev/input/event3,運(yùn)行測(cè)試程序并按下、松開鍵盤上的按鍵;
? ? ?大家可以根據(jù)code值查詢對(duì)應(yīng)的按鍵,譬如code=30對(duì)應(yīng)的鍵盤上的字母A鍵,code=48對(duì)應(yīng)的字母B鍵。
第二:?jiǎn)吸c(diǎn)觸摸應(yīng)用程序?qū)崿F(xiàn)
? ? ?通過(guò)上面的詳細(xì)介紹,大家應(yīng)該知道如何編寫一個(gè)觸摸屏的應(yīng)用程序了,接下來(lái)我們編寫一個(gè)單點(diǎn)觸摸屏應(yīng)用程序,獲取一個(gè)觸摸點(diǎn)的坐標(biāo)信息,并將其打印出來(lái)。具體代碼實(shí)現(xiàn)如下:
?
#include#include #include #include #include #include #include int main(int argc, char *argv[]) { struct input_event in_ev; int x, y; //觸摸點(diǎn) x 和 y 坐標(biāo) int down; //用于記錄 BTN_TOUCH 事件的 value,1 表示按下,0 表示松開,-1 表示移動(dòng) int valid; //用于記錄數(shù)據(jù)是否有效(我們關(guān)注的信息發(fā)生更新表示有效,1 表示有效,0 表示無(wú)效) int fd = -1; /* 校驗(yàn)傳參 */ if (2 != argc) { fprintf(stderr, "usage: %s ", argv[0]); exit(EXIT_FAILURE); } /* 打開文件 */ if (0 > (fd = open(argv[1], O_RDONLY))) { perror("open error"); exit(EXIT_FAILURE); } x = y = 0; //初始化 x 和 y 坐標(biāo)值 down = -1; //初始化<移動(dòng)> valid = 0;//初始化<無(wú)效> for ( ; ; ) { /* 循環(huán)讀取數(shù)據(jù) */ if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))) { perror("read error"); exit(EXIT_FAILURE); } ?switch(in_ev.type) { case EV_KEY: //按鍵事件 if (BTN_TOUCH == in_ev.code) { down = in_ev.value; valid = 1; } break; case EV_ABS: //絕對(duì)位移事件 switch (in_ev.code) { case ABS_X: //X 坐標(biāo) x = in_ev.value; valid = 1; break; case ABS_Y: //Y 坐標(biāo) y = in_ev.value; valid = 1; break; } break; case EV_SYN: //同步事件 if (SYN_REPORT == in_ev.code) { if (valid) {//判斷是否有效 switch (down) {//判斷狀態(tài) case 1: printf("按下(%d, %d) ", x, y); break; case 0: printf("松開 "); break; case -1: printf("移動(dòng)(%d, %d) ", x, y); break; } valid = 0; //重置 valid down = -1; //重置 down } } break; } } }
?
? ? ?分析:程序中先傳入參數(shù),main()函數(shù)中定義了4個(gè)變量;
⑴、變量 x 表示觸摸點(diǎn)的 X 坐標(biāo);
⑵、變量 y 表示觸摸點(diǎn)的 Y 坐標(biāo);
⑶、變量 down 表示手指狀態(tài)時(shí)候按下、松開還是滑動(dòng),down=1 表示手指按下、down=0 表示手指松開、down=-1 表示手指滑動(dòng);
⑷、變量 valid 表示數(shù)據(jù)是否有效,valid=1 表示有效、valid=0 表示無(wú)效;有效指的是我們檢測(cè)的信息發(fā)生了更改,譬如程序中只檢測(cè)了手指的按下、松開動(dòng)作以及坐標(biāo)值的變化。接著調(diào)用 open()打開觸摸屏設(shè)備文件得到文件描述符 fd;在 for 循環(huán)之前,首先對(duì) x、y、down、valid這 4 個(gè)變量進(jìn)行初始化操作。在 for 循環(huán)讀取觸摸屏上報(bào)的數(shù)據(jù),將讀取到的數(shù)據(jù)存放在 struct input_event數(shù)據(jù)結(jié)構(gòu)中。在 switch…case 語(yǔ)句中對(duì)讀取到的數(shù)據(jù)進(jìn)行解析,獲取 BTN_TOUCH 事件的 value 數(shù)據(jù),判斷觸摸屏是按下還是松開狀態(tài),獲取 ABS_X 和 ABS_Y 事件的 value 變量,得到觸摸點(diǎn)的 X 軸坐標(biāo)和 Y 軸坐標(biāo)。
? ? 當(dāng)上報(bào)同步事件時(shí),表示數(shù)據(jù)已上傳完整,接著對(duì)得到的數(shù)據(jù)進(jìn)行分析,打印坐標(biāo)信息。
第三:多點(diǎn)觸摸應(yīng)用程序?qū)崿F(xiàn)
? ? 實(shí)現(xiàn)了單點(diǎn)觸摸應(yīng)用程序之后,可以再來(lái)實(shí)現(xiàn)多點(diǎn)觸摸屏應(yīng)用程序該如何實(shí)現(xiàn)。
?
#include#include #include #include #include #include #include #include #include /* 用于描述 MT 多點(diǎn)觸摸每一個(gè)觸摸點(diǎn)的信息 */ struct ts_mt { int x; //X 坐標(biāo) int y; //Y 坐標(biāo) int id; //對(duì)應(yīng) ABS_MT_TRACKING_ID int valid; //數(shù)據(jù)有效標(biāo)志位(=1 表示觸摸點(diǎn)信息發(fā)生更新) }; /* 一個(gè)觸摸點(diǎn)的 x 坐標(biāo)和 y 坐標(biāo) */ struct tp_xy { int x; int y; }; static int ts_read(const int fd, const int max_slots, struct ts_mt *mt) { struct input_event in_ev; static int slot = 0;//用于保存上一個(gè) slot static struct tp_xy xy[12] = {0};//用于保存上一次的 x 和 y 坐標(biāo)值,假設(shè)觸摸屏支持的最大觸摸點(diǎn)數(shù)不會(huì)超 過(guò) 12 int i; /* 對(duì)緩沖區(qū)初始化操作 */ memset(mt, 0x0, max_slots * sizeof(struct ts_mt)); //清零 for (i = 0; i < max_slots; i++) mt[i].id = -2;//將 id 初始化為-2, id=-1 表示觸摸點(diǎn)刪除, id>=0 表示創(chuàng)建 for ( ; ; ) { if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))) { perror("read error"); return -1; } switch (in_ev.type) { case EV_ABS: switch (in_ev.code) { case ABS_MT_SLOT: slot = in_ev.value; break; case ABS_MT_POSITION_X: xy[slot].x = in_ev.value; mt[slot].valid = 1; break; case ABS_MT_POSITION_Y: xy[slot].y = in_ev.value; mt[slot].valid = 1; break; case ABS_MT_TRACKING_ID: mt[slot].id = in_ev.value; mt[slot].valid = 1; break; } break; //case EV_KEY://按鍵事件對(duì)單點(diǎn)觸摸應(yīng)用比較有用 // break; case EV_SYN: if (SYN_REPORT == in_ev.code) { for (i = 0; i < max_slots; i++) { mt[i].x = xy[i].x; mt[i].y = xy[i].y; } } return 0; } } } int main(int argc, char *argv[]) { struct input_absinfo slot; struct ts_mt *mt = NULL; int max_slots; int fd; int i; /* 參數(shù)校驗(yàn) */ if (2 != argc) { fprintf(stderr,"usage: %s ", argv[0]); exit(EXIT_FAILURE); } /* 打開文件 */ fd = open(argv[1], O_RDONLY); if (0 > fd) { perror("open error"); exit(EXIT_FAILURE); } /* 獲取觸摸屏支持的最大觸摸點(diǎn)數(shù) */ if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)) { perror("ioctl error"); close(fd); exit(EXIT_FAILURE); } max_slots = slot.maximum + 1 - slot.minimum; printf("max_slots: %d ", max_slots); /* 申請(qǐng)內(nèi)存空間并清零 */ mt = calloc(max_slots, sizeof(struct ts_mt)); /* 讀數(shù)據(jù) */ for ( ; ; ) { if (0 > ts_read(fd, max_slots, mt)) break; for (i = 0; i < max_slots; i++) { if (mt[i].valid) {//判斷每一個(gè)觸摸點(diǎn)信息是否發(fā)生更新(關(guān)注的信息發(fā)生更新) if (0 <= mt[i].id) printf("slot<%d>, 按下(%d, %d) ", i, mt[i].x, mt[i].y); else if (-1 == mt[i].id) printf("slot<%d>, 松開 ", i); else printf("slot<%d>, 移動(dòng)(%d, %d) ", i, mt[i].x, mt[i].y); } } } /* 關(guān)閉設(shè)備、退出 */ close(fd); free(mt); exit(EXIT_FAILURE); }
?
? ? ? 示例代碼中申明了 struct ts_mt 數(shù)據(jù)結(jié)構(gòu),用于描述多點(diǎn)觸摸情況下每一個(gè)觸摸點(diǎn)的信息。
? ? ? 首先來(lái)看下 main()函數(shù),定義了 max_slots 變量,用于指定觸摸屏設(shè)備的支持的最大觸摸點(diǎn)數(shù),通過(guò):
ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)
獲取到觸摸屏該信息。
接著根據(jù) max_slots 變量的值,為 mt 指針申請(qǐng)內(nèi)存:
? ? ? ? ? ? mt = calloc(max_slots, sizeof(struct ts_mt));
? ? ?for( ; ; )循環(huán)中調(diào)用 ts_read()函數(shù),該函數(shù)是自定義函數(shù),用于獲取觸摸屏上報(bào)的數(shù)據(jù),第一個(gè)參數(shù)表示文件描述符 fd、第二個(gè)參數(shù)表示觸摸屏支持的最大觸摸點(diǎn)數(shù)、第三個(gè)參數(shù)則是 struct ts_mt 數(shù)組,ts_read()函數(shù)會(huì)將獲取到的數(shù)據(jù)存放在數(shù)組中,mt[0]表示 slot<0>數(shù)據(jù)、mt[1]表示 slot<1>的數(shù)據(jù)依次類推!
? ? 在內(nèi)部的 for 循環(huán)中,則對(duì)獲取到的數(shù)據(jù)進(jìn)行分析,判斷數(shù)據(jù)是否有效,并根據(jù) id 判斷手指的動(dòng)作,在單點(diǎn)觸摸應(yīng)用程序中,我們是通過(guò) BTN_TOUCH 事件來(lái)判斷手指的動(dòng)作;而在多點(diǎn)觸摸應(yīng)用中,我們需要通過(guò) id 來(lái)判斷多個(gè)手指的動(dòng)作。
? ? ?關(guān)于自定義函數(shù) ts_read()就不再介紹了,代碼的注釋已經(jīng)描述很清楚了!
? ? ?接著編譯應(yīng)用程序,將編譯得到的可執(zhí)行文件拷貝到開發(fā)板 Linux 系統(tǒng)的用戶家目錄下,執(zhí)行應(yīng)用程序,接著可以用多個(gè)手指觸摸觸摸屏、松開、滑動(dòng)等操作。
總結(jié):每一個(gè)不同的slot表示不同的觸摸點(diǎn),譬如 slot<0>表示觸摸點(diǎn) 0、slot<1>表示觸摸點(diǎn) 1 以此類推!
評(píng)論
查看更多