天天看點

Linux系統中标準輸入裝置的控制實作

        大家好,今天主要和大家聊一聊,如何使用标準輸入裝置,進行控制資訊的識别。

Linux系統中标準輸入裝置的控制實作

目錄

​​第一:按鍵應用程式設計方法​​

​​第二:單點觸摸應用程式實作​​

​​第三:多點觸摸應用程式實作​​

第一:按鍵應用程式設計方法

     編寫一個應用程式,擷取按鍵狀态,判斷按鍵目前是按下,松開或長按狀态。

#以字母A鍵為例
KEY_A    //上報KEY_A事件
SYN_REPORT   //同步      

      如果是按下,則上報KEY_A事件時,value=1;如果是松開,則value=0;如果長按,則value=2。接下來編寫按鈕應用程式,讀取按鍵狀态并将結果列印出來,代碼如下所示。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(int argc, char *argv[])
{
 struct input_event in_ev = {0};
 int fd = -1;
 int value = -1;
 /* 校驗傳參 */
 if (2 != argc) {
 fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
 exit(-1);
 }
 /* 打開檔案 */
 if (0 > (fd = open(argv[1], O_RDONLY))) {
 perror("open error");
 exit(-1);
 }
 for ( ; ; ) {
 /* 循環讀取資料 */
 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>: 松開\n", in_ev.code);
 break;
 case 1:
 printf("code<%d>: 按下\n", in_ev.code);
 break;
 case 2:
 printf("code<%d>: 長按\n", in_ev.code);
 break;
       }
     }
   }
}      

    在for循環中,調用read()讀取輸入裝置上報的資料,當按鍵按下或松開(以及長按)動作發生時,read()會讀取到輸入裝置上報的資料,首先判斷此次上報的事件是否是按鍵類事件(EV_KEY),如果是按鍵類事件,接着根據value值來判斷按鍵目前的狀态是松開、按下還是長按。

    将編譯得到的可執行檔案複制到開發闆Linux系統的家目錄下:

Linux系統中标準輸入裝置的控制實作

注意:除了能夠測試KEY0按鍵之外,還可以測試鍵盤上的按鍵,可以找到一個USB鍵盤連接配接到開發闆的USB HOST接口上,當鍵盤插入之後,終端将會列印出相應的驅動加載資訊。

     驅動加載成功之後,可以檢視下該鍵盤裝置對應的裝置節點,使用指令"cat /proc/bus/input/devices",在列印資訊中找到鍵盤裝置的資訊:

Linux系統中标準輸入裝置的控制實作

      操作的時候,可以對應相應的裝置節點/dev/input/event3,運作測試程式并按下、松開鍵盤上的按鍵;

Linux系統中标準輸入裝置的控制實作

     大家可以根據code值查詢對應的按鍵,譬如code=30對應的鍵盤上的字母A鍵,code=48對應的字母B鍵。

第二:單點觸摸應用程式實作

     通過上面的詳細介紹,大家應該知道如何編寫一個觸摸屏的應用程式了,接下來我們編寫一個單點觸摸屏應用程式,擷取一個觸摸點的坐标資訊,并将其列印出來。具體代碼實作如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char *argv[])
{
 struct input_event in_ev;
 int x, y; //觸摸點 x 和 y 坐标
 int down; //用于記錄 BTN_TOUCH 事件的 value,1 表示按下,0 表示松開,-1 表示移動
 int valid; //用于記錄資料是否有效(我們關注的資訊發生更新表示有效,1 表示有效,0 表示無效)
 int fd = -1;
 /* 校驗傳參 */
 if (2 != argc) {
 fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
 exit(EXIT_FAILURE);
 }
 /* 打開檔案 */
 if (0 > (fd = open(argv[1], O_RDONLY))) {
 perror("open error");
 exit(EXIT_FAILURE);
 }
 x = y = 0; //初始化 x 和 y 坐标值
 down = -1; //初始化<移動>
 valid = 0;//初始化<無效>
 for ( ; ; ) {
 /* 循環讀取資料 */
 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: //絕對位移事件
 switch (in_ev.code) {
 case ABS_X: //X 坐标
 x = in_ev.value;
 valid = 1;
 break;
 case ABS_Y: //Y 坐标
 y = in_ev.value;
 valid = 1;
 break;
 }
 break;
 case EV_SYN: //同步事件
 if (SYN_REPORT == in_ev.code) {
 if (valid) {//判斷是否有效
 switch (down) {//判斷狀态
 case 1:
 printf("按下(%d, %d)\n", x, y);
 break;
 case 0:
 printf("松開\n");
 break;
 case -1:
 printf("移動(%d, %d)\n", x, y);
 break;
 }
 valid = 0; //重置 valid
 down = -1; //重置 down
 }
 }
 break;
       }
    }
}      

     分析:程式中先傳入參數,main()函數中定義了4個變量;

⑴、變量 x 表示觸摸點的 X 坐标;

⑵、變量 y 表示觸摸點的 Y 坐标;

⑶、變量 down 表示手指狀态時候按下、松開還是滑動,down=1 表示手指按下、down=0 表示手指松開、down=-1 表示手指滑動;

⑷、變量 valid 表示資料是否有效,valid=1 表示有效、valid=0 表示無效;有效指的是我們檢測的資訊發生了更改,譬如程式中隻檢測了手指的按下、松開動作以及坐标值的變化。接着調用 open()打開觸摸屏裝置檔案得到檔案描述符 fd;在 for 循環之前,首先對 x、y、down、valid這 4 個變量進行初始化操作。在 for 循環讀取觸摸屏上報的資料,将讀取到的資料存放在 struct input_event資料結構中。在 switch…case 語句中對讀取到的資料進行解析,擷取 BTN_TOUCH 事件的 value 資料,判斷觸摸屏是按下還是松開狀态,擷取 ABS_X 和 ABS_Y 事件的 value 變量,得到觸摸點的 X 軸坐标和 Y 軸坐标。

    當上報同步事件時,表示資料已近完整,接着對得到的資料進行分析,列印坐标資訊。

Linux系統中标準輸入裝置的控制實作

​第三:多點觸摸應用程式實作

    實作了單點觸摸應用程式之後,可以再來實作多點觸摸屏應用程式該如何實作。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
/* 用于描述 MT 多點觸摸每一個觸摸點的資訊 */
struct ts_mt {
 int x; //X 坐标
 int y; //Y 坐标
 int id; //對應 ABS_MT_TRACKING_ID
 int valid; //資料有效标志位(=1 表示觸摸點資訊發生更新)
};
/* 一個觸摸點的 x 坐标和 y 坐标 */
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;//用于儲存上一個 slot
 static struct tp_xy xy[12] = {0};//用于儲存上一次的 x 和 y 坐标值,假設觸摸屏支援的最大觸摸點數不會超
過 12
 int i;
 /* 對緩沖區初始化操作 */
 memset(mt, 0x0, max_slots * sizeof(struct ts_mt)); //清零
 for (i = 0; i < max_slots; i++)
 mt[i].id = -2;//将 id 初始化為-2, id=-1 表示觸摸點删除, id>=0 表示建立
 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://按鍵事件對單點觸摸應用比較有用
// 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;
 /* 參數校驗 */
 if (2 != argc) {
 fprintf(stderr,"usage: %s <input_dev>\n", argv[0]);
 exit(EXIT_FAILURE);
 }
 /* 打開檔案 */
 fd = open(argv[1], O_RDONLY);
 if (0 > fd) {
 perror("open error");
 exit(EXIT_FAILURE);
 }
 /* 擷取觸摸屏支援的最大觸摸點數 */
 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\n", max_slots); 
 /* 申請記憶體空間并清零 */
 mt = calloc(max_slots, sizeof(struct ts_mt));
 /* 讀資料 */
 for ( ; ; ) {
 if (0 > ts_read(fd, max_slots, mt))
 break;
 for (i = 0; i < max_slots; i++) {
 if (mt[i].valid) {//判斷每一個觸摸點資訊是否發生更新(關注的資訊發生更新)
 if (0 <= mt[i].id)
 printf("slot<%d>, 按下(%d, %d)\n", i, mt[i].x, mt[i].y);
 else if (-1 == mt[i].id)
 printf("slot<%d>, 松開\n", i);
 else
 printf("slot<%d>, 移動(%d, %d)\n", i, mt[i].x, mt[i].y);
 }
 }
 }
 /* 關閉裝置、退出 */
 close(fd);
 free(mt);
 exit(EXIT_FAILURE);
}      

      示例代碼中申明了 struct ts_mt 資料結構,用于描述多點觸摸情況下每一個觸摸點的資訊。

      首先來看下 main()函數,定義了 max_slots 變量,用于指定觸摸屏裝置的支援的最大觸摸點數,通過:

ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)

擷取到觸摸屏該資訊。

接着根據 max_slots 變量的值,為 mt 指針申請記憶體:

            mt = calloc(max_slots, sizeof(struct ts_mt));

     for( ; ; )循環中調用 ts_read()函數,該函數是自定義函數,用于擷取觸摸屏上報的資料,第一個參數表示檔案描述符 fd、第二個參數表示觸摸屏支援的最大觸摸點數、第三個參數則是 struct ts_mt 數組,ts_read()函數會将擷取到的資料存放在數組中,mt[0]表示 slot<0>資料、mt[1]表示 slot<1>的資料依次類推!

    在内部的 for 循環中,則對擷取到的資料進行分析,判斷資料是否有效,并根據 id 判斷手指的動作,在單點觸摸應用程式中,我們是通過 BTN_TOUCH 事件來判斷手指的動作;而在多點觸摸應用中,我們需要通過 id 來判斷多個手指的動作。

     關于自定義函數 ts_read()就不再介紹了,代碼的注釋已經描述很清楚了!