[Linux] システム詳細入力 + タッチ スクリーン アプリケーション戦闘 (tslib)

カタログ概要

序文:

1. 入力系統

2、Linux入力システムフレームワーク

(1) 入力系のドライバー層

 (2) 入力系コア層

(3) 入力系イベント層

3. APP がハードウェアにアクセスする方法

(1) クエリモード、スリープ-ウェイクアップ

具体例:

実際の効果: 

(2) POLL/SELECTモード

具体例:

実際の効果:

(3) 非同期通知方法

 具体例:

実際の効果:

4、tslib ライブラリ フレームワーク

(1) 静電容量式スクリーンの簡単な説明

(2) tslibライブラリの有用性

(3) tslibフレームワーク解析

5、tslib ベースのテスト プログラム:

(1) tslib ライブラリのクロスコンパイル

(2) ボード上のテストコンパイル

試験結果:

 6. tslib に基づくアプリケーション戦闘:

 具体例:

実際の効果:


序文:

古典的なリンク:私は常に、質問を考えて練習することで、理解と学習が容易になると信じてきました。

(1) 入力システムとは何ですか?

(2) 入力デバイスにはさまざまな種類がありますが、Linux の入力システムはどのように管理されていますか?

  • ①統一された標準入力イベントへの複雑な入力にどう対処するか?
  • ②複数のイベントがある場合、ドライバーがイベントをアップロードするときに、ドライバーはイベントの送信が完了したことをどのようにAPPに通知しますか?
  • ③ユーザープログラム(APP)によるデータ取得の具体的な手順は何ですか?

(3) APP はどのような方法でハードウェアにアクセスできますか? それはどのように達成されるのでしょうか?

(4) 静電容量式タッチ スクリーンを例として、入力デバイスを使用するには tslib ライブラリを使用します。

  • tslib ライブラリの役割は何ですか? その利点は何ですか?
  • tslib ライブラリ フレームワークはどのようなものですか? 内部の仕組みはどうなっているのでしょうか?
  • tslib ライブラリを使用してアプリケーションの機能を実装するにはどうすればよいですか? ---応用練習

次の記事の内容は、上記の質問に詳しく答えます。役に立ったら、三度注目してください(^_^)、もっと応援して、一緒に進歩しましょう!

1. 入力系統

入力システムとは何ですか?

        私たちの生活の中には、キーボード、マウス、リモコン スティック、タッチ スクリーンなど、多くの入力デバイスがあります。ユーザーは、これらのデバイスを通じて Linux システムとデータを交換します。

         複数の入力デバイスが統一されたインターフェイスを持つことができるかどうか、またドライバー レベルとアプリケーション レベルで統一できるかどうか、上記の要件を達成するために、Linux システムはすべての入力デバイスと互換性のある一連のフレームワーク、つまり入力システムを設計しました。 。

2、Linux入力システムフレームワーク

        以下の図に示すように、Linux 入力システムの管理方法は 3 段階に分かれています。 ① 入力イベントを統一規格に処理し、コア層に送信します。 ② 入力イベントを受信して​​イベント層(イベント層)に転送します。ハンドラー) ③ イベントを処理し、ユーザー空間アクセス インターフェイスを提供します

        

ここでは、ハードウェアから APP に関連する概念までを説明します。

(1) 入力系のドライバー層

①ここには、割り込みを生成してシステムにデータを送信するハードウェア入力デバイスが多数ありますが、システムはどのようにしてそれらを均一に処理し、均一な標準入力イベントに処理できるでしょうか?

ここでの入力イベントはstruct input_event 」構造です。Linux カーネル ファイルを参照してください。

D:\Linux-4.9.88.tar\Linux-4.9.88\include\uapi\linux input.h

 

 ここで、特定の入力イベントの構造は、時刻、タイプ、コード、値として統一されています。

timeval结构体---表示事件的发生时间

type:表示哪类事件

code:表示该类事件下的那一个事件

value:表示事件值。

 ②複数のイベントがある場合、ドライバーがイベントをアップロードするときに、ドライバーはイベントの送信が完了したことをどのようにAPPに通知しますか?

ドライバーが一連のデータの報告を完了すると、 データの報告が完了したことを示す 「 同期イベント」を報告します。 「同期イベント」を読み込むと、現在のデータが読み込まれたことがわかります。
同期イベントも input_event 構造体であり、その type code 、および value はすべて 0 です

 (2) 入力系コア層

転送ステーションの役割であるコア層は、入力イベントを処理のためにどのハンドラーに転送するかを決定できますハンドラーには、evdev_handler kbd_handler joydev_handler など、さまざまな種類があります。

最も一般的に使用されるのは evdev_handler です。これは単に input_event 構造体をカーネル バッファーなどに保存し、APP がそれを読み取るときにそれをそのまま返します。

(3) 入力系イベント層

ここでは、コア層によってアップロードされた入力イベントが処理され、ユーザー空間にユーザー インターフェイスが提供されます。

③システムの内部構造を理解した上で、ユーザープログラム(APP)がデータを取得する具体的なプロセスは何でしょうか?

  1. APP は読み取り操作を開始し、データがない場合はスリープします
  2. ユーザーがデバイスを操作すると、ハードウェアが割り込みを生成します。
  3. 入力系ドライバ層に対応するドライバが割り込みを処理します。
  4. コア層は入力イベントを、処理のために対応するハンドラー (最も一般的に使用される evdev_handler) に転送します。
  5. APP がデータを待機しているとき、evdev_handler はそれを起動して、APP がデータを取得できるようにします。

ここで APP がデータを取得するには 2 つの方法があります。1 つはデバイス ノードに直接アクセスする方法、もう 1 つは tslib や libinput などのライブラリを通じてデバイス ノードに間接的にアクセスする方法です。

便利なデバッグ コマンド:

//查看设备节点,有什么事件

ls /dev/input/* -l

//获取与event对应的相关设备信息

cat /proc/bus/input/devices
//使用命令读取数据(以触摸屏为例)

hexdump /dev/input/event1

3. APP がハードウェアにアクセスする方法

APP はどのような方法でハードウェアにアクセスできますか?

APP がハードウェアにアクセスするには、クエリ モード、スリープ/ウェイクアップ モード、POLL/SELECT モード、および非同期通知モードの 4 つの方法があります。

機構
お問い合わせ 上司が時々お邪魔に来ます
睡眠-覚醒 普段は横になって何もせず、上司の呼び出し後に仕事をする
ポーリング/選択 目覚まし時計をセットして、時間が来たとき、または上司から電話がかかってきたときに仕事をしましょう
非同期通知 一人で仕事をしていて、上司から呼ばれたら、上司から指示された仕事をする

上記の方法には、長所と短所に関係なく、それぞれの適用シナリオがあります。では、その方法を具体的に実装するにはどうすればよいでしょうか?

(1) クエリモード、スリープ-ウェイクアップ

違い
問い合わせモード

APP が open 関数を呼び出すとき、「O_NONBLOCK」を渡します --- ノンブロッキング

APP が読み取り関数を呼び出すと、ドライバーにデータがある場合、APP 関数はデータを返します。そうでない場合は、すぐにエラーが返されます。

睡眠-覚醒

APP が open 関数を呼び出すときは、「O_NONBLOCK」を渡さないでください --- ブロックします

APP が読み取り関数を呼び出すと、ドライバーにデータがある場合、APP 関数はデータを返しますが、ない場合、APP はカーネル モードでスリープします。

具体例:

#include <linux/input.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <poll.h>


#include <stdio.h>
#include <string.h>




/*01_get_input_info 
	/dev/input/event1	对应触摸屏事件
	O_NONBLOCK(非阻塞方式)
	*/
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	struct input_event event;
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	//阻塞、非阻塞方式的对比
	if(argc < 2)
	{
		printf("Usage: %s <dev> [noblock]\n",argv[0]);
		return -1;
	}
	if(argc == 3 && !strcmp(argv[2],"noblock"))
	{
		fd =  open(argv[1], O_RDWR | O_NONBLOCK);
	}
	else
	{
		fd =  open(argv[1], O_RDWR);
	}
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	while(1){
		len = read(fd, &event, sizeof(event));
		if(len == sizeof(event))
		{
			printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
		}
		else
		{
			printf(" read error %d\n", len);
		}
	}
	return 0;
	
}

実際の効果: 

クエリモード (ノンブロッキング):

デバイス情報を取得し、デバイス ノードを開いてアプリを読み取ると、ドライバーにはデータがなく、常にエラーが返されます。

 スリープウェイクアップ (ブロック):

デバイス情報を取得し、デバイスノードを開いてアプリを読み込み、データがない場合は休止状態に入り、タッチスクリーンをクリックするとデータが返されます。

(2) POLL/SELECTモード

POLL メカニズムと SELECT メカニズムはまったく同じですが、APP インターフェイスの機能が異なります。

ポーリングおよび選択関数を呼び出すときに「タイムアウト」を渡します。この期間中、条件が適切な場合 (たとえば、読み込むデータがある場合)、すぐに戻りますが、そうでない場合は、「タイムアウト期間」が終了したときにエラーを返します。

次の表に示すように、ポーリング/選択監視イベントにはさまざまなタイプがあります。

具体例:

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <poll.h>


#include <stdio.h>
#include <string.h>


/*01_get_input_read_poll
	/dev/input/event1	对应触摸屏事件
	*/
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int ret;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	struct input_event event;
	struct pollfd fds[1];
	nfds_t nfds = 1;
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	
	if(argc != 2)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	fd =  open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	
	
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	//POLL和SELECT方式读取输入数据,超时时间-5s
	while(1){
        //想查询哪个文件(fd)
		fds[0].fd = fd;
        //想查询什么事件
		fds[0].events = POLLIN;
        //清除“返回事件”
		fds[0].revents = 0;
		ret = poll(fds, nfds, 5000);
		if(ret > 0){
			if(fds[0].revents == POLLIN)
			{
				while(read(fd, &event, sizeof(event)) == sizeof(event))
				{
					printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
				}
			}
		}
		else if(ret == 0)
		{
			printf("time out\n");
		}
		else
		{
			printf("poll err\n");
		}
	}
	return 0;

実際の効果:

(3) 非同期通知方法

非同期通知メカニズムは、マイクロコントローラーによって開発された割り込みに似ています。つまり、APP は独自の業務で忙しい場合があり、ドライバーがデータを持っている場合、積極的に信号を APP に送信し、このとき、APP は信号処理機能を実行します。

上記の簡単なプロセスと目的を明確にすることに加えて、解決する必要のある具体的な問題がまだいくつかあります。

  1. ドライバーはどのような信号をアプリに送信しますか? ----SIGIO (ドライブコモン信号)
  2. どのように信号を送りますか?--- カーネルが機能を提供する
  3. 信号処理機能と信号の接続方法:APP登録信号処理機能

① ...\Linux-4.9.88\include\uapi\asm-generic signal.h には多くのシグナルがあります。

 ②APPは登録信号処理機能を提供しますが、SIGIOとも連携しており、詳細は以下のとおりです。

さらに考える:

  • カーネルには多くのドライバーがありますが、どのドライバーが SIGIO 信号をアプリに送信する必要がありますか?
    • APP クロックイン ドライバーのデバイス ノード
  • ドライバーは、現在の APP に信号を送信することをどのようにして知るのでしょうか?
    • APP はそのプロセス ID をドライバーに通知します

 具体例:

#include <linux/input.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h> 
#include <sys/types.h>
#include <fcntl.h>


#include <stdio.h>
#include <string.h>


int fd;

//信号处理函数
void sig_func_handler(int sig)
{
	struct input_event event;
	while(read(fd, &event, sizeof(event)) == sizeof(event))
	{
		printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
	}

	
};
/*05_get_read_faycn 
	/dev/input/event1	对应触摸屏事件
	*/
int main(int argc, char **argv)
{
	int err;
	int len;
	int ret;
	int i;
	int flags;
	int count = 0;
	
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	
	if(argc != 2)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	/*注册信号处理函数*/
	signal(SIGIO, sig_func_handler);

	/*打开驱动程序*/
	fd =  open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
			
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	
	/*把APP的进程号告诉驱动程序*/
	fcntl(fd, F_SETOWN, getpid());
	
	/*使能"异步通知"*/
	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | FASYNC);
	
	while(1){
		printf("main loop count = %d\n", count++);
		sleep(2);
	}
	return 0;
	
}

実際の効果:

4、tslib ライブラリ フレームワーク

(1) 静電容量式スクリーンの簡単な説明

容量性スクリーンには制御チップがあり、定期的に駆動信号を生成し、受信電極が信号を受信して​​電荷の大きさを測定できます。容量性スクリーンが押されると、新しい静電容量が導入されることと同じになり、受信電極が受け取る電荷の量に影響を与えます。メイン制御チップは、電荷の大きさに応じて接触位置を計算できます。

静電容量式タッチ スクリーン データを分析するには、開発ボード上の静電容量式スクリーンに対応するデバイス ノード /dev/input/event1 で次のコマンドを実行します。

hexdump /dev/input/event1

①タッチスクリーンを指1本でタッチすると、以下が表示されます。

② タッチ スクリーンを 2 本の指でクリックして、次の情報を取得します。

(2) tslibライブラリの有用性

tslib ライブラリの役割は何ですか? その利点は何ですか?

タッチ スクリーンをクリックすると多くのイベントが発生することがわかりますが、それらをフィルタリングして処理するのは不便です。

tslib はタッチ スクリーン用のオープンソース ライブラリで、これを使用してタッチ スクリーン デバイスにアクセスしたり、入力デバイスにさまざまな「フィルター」(フィルタリング ライブラリ、つまりさまざまな処理)を追加したりできます。

(3) tslibフレームワーク解析

tslib ライブラリ フレームワークはどのようなものですか? 内部の仕組みはどうなっているのでしょうか?

tslib のフレームワークを図に示します。

 tslib の主なコードは次のとおりです。

  • src/インターフェース関数
    • ts_setup.c
    • ts_open.c
    • ts_config.c
  • plugins/ module module、以下はモジュールです
    • リニア.c
    • デジッター.c
    • pthres.c
    • input_raw.c     
  • テスト/テストプログラム
    • ts_test.c
    • ts_test_mt.c
    • ts_print.c
    • ts_print_mt.c

tslib フレームワーク全体を分析します。シングル タッチ スクリーンとマルチタッチ スクリーンのサンプル プログラム ( ts_test.cおよびts_test_mt.c ) を参照してください。tslib の操作プロセスは次のとおりです。

  • 1. ts_open を呼び出してデバイス ノードを開き、tsdev 構造を構築します。この構造体の内容は次のとおりです。
  • 2. ts_config を呼び出して設定ファイルの処理を読み込み、すべてのモジュールに対してリンク リストのヘッダーが挿入されます。module と module_raw は、tsdev 構造体の異なるリンク リスト list と list_raw に対応します。
  • 3. input_raw→pthres→dejitter→linearモジュールの各モジュールを再帰的に呼び出し、デバイスノードから取得した生データに対してデータ処理を行い、最終データを返します。以下の図は再帰的な処理です。

したがって、本体は 3 つの関数 (ts_setup、ts_read、または ts_readmt、ts_close) を呼び出します。

5、tslib ベースのテスト プログラム:

tslib ライブラリを使用してアプリケーションの機能を実装するにはどうすればよいですか?

(1) tslib ライブラリのクロスコンパイル

//クロスコンパイル ツールチェーンを設定します(コピー時に一貫性があるかどうかのチェックに注意してください)

エクスポート ARCH=arm
エクスポート CROSS_COMPILE=arm-linux-gnueabihf-

エクスポート PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin

// tslib ライブラリの解凍

cp /home/book/01_all_series_quickstart/04_組み込み Linux アプリケーション開発の基礎知識/source/11_input/02_tslib/tslib-1.21.tar.xz ./

tar xJf tslib-1.21.tar.xz

//クロスコンパイルの汎用コマンド

cd tslib-1.21

./configure --host=arm-linux-gnueabihf --prefix=/

作る

make install DESTDIR=$PWD/tmp

//ヘッダー ファイルとライブラリ ファイルをツールチェーン ディレクトリに配置します

cd tslib-1.21/tmp/

cp include/* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include

cp -d lib/*so* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/ライブラリ/

(2) ボード上のテストコンパイル

//ファイルを nfs にマウントされたフォルダー nfs_rootfs にコピーします

cp -r ~/tslib-1.21/* ~/nfs_rootfs

//ファイルをボード上の lib、bin などにコピーします

cp /mnt/tslib-1.21/tmp/lib/*so* -d /lib

cp /mnt/tslib-1.21/tmp/bin/* /bin

cp /mnt/tslib-1.21/tmp/etc/ts.conf -d /etc

cp /mnt/tslib-1.21/tmp/lib/ts -rf /lib

//デフォルトの qt GUI プログラムを閉じて (実際の状況に応じて)、/etc/init.d/ を開いて qtGUI プログラム名を表示します。

//再度開くと、対応するファイルは元に戻されます

mv /etc/init.d/S99myirhmi2 /root

リブート

//テスト

ts_test_mt

試験結果:

 6. tslib に基づくアプリケーション戦闘:

 2 つの接触間の距離を連続的に出力するプログラムを実装する

タッチ スクリーンは複数のタッチ ポイント (たとえば、5 つ) をサポートする場合があります。処理を簡略化するために、タッチ ポイントが 2 つしかない場合でも、tslib は 5 つのタッチ ポイントのデータを返します

ドライバーはスロットと tracing_id を使用してコンタクトを識別します。tracing_id が -1 に等しい場合、コンタクトが解放されたことを示します。

このマークによってデータが有効であるかどうかが判断できるので、2つの接点が有効な場合には、それらの間の距離を出力します。

コア関数: ts_read_mt

4 つのパラメータ: tsdev 構造体、max_slots (最大ポイント数)、ts_sample_mt 構造体 (データ ストレージ)、nr

 

 具体例:

第 4 部の内容に従って、上記の考え方と ts_test_mt.c サンプル プログラムを使用して、プログラムの作成が完了します。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>

#include <linux/input.h>

#include <sys/ioctl.h>

#include <tslib.h>

int distance(struct ts_sample_mt *point1, struct ts_sample_mt *point2)
{
	int x = point1->x - point2->x;
	int y = point1->y - point2->y;

	return x*x + y*y;
}

int main(int argc, char **argv)
{
	struct tsdev *ts;
	int i;
	int ret;
	//定义新旧触点sample结构体
	struct ts_sample_mt **samp_mt;
	struct ts_sample_mt **pre_samp_mt;
	int max_slots;
	int point_pressed[20];
	struct input_absinfo slot;
	int touch_cnt = 0;

	//阻塞方式
	ts = ts_setup(NULL, 0);
	if (!ts)
	{
		printf("ts_setup err\n");
		return -1;
	}
	
	//读取设备节点,获取属性---同时支持多少个触点,得到max_slots
	if (ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0) {
		perror("ioctl EVIOGABS");
		ts_close(ts);
		return errno;
	}

	max_slots = slot.maximum + 1 - slot.minimum;

	
	//参照测试程序初始samp_mt和pre_samp_mt结构体数组
	samp_mt = malloc(sizeof(struct ts_sample_mt *));
	if (!samp_mt) {
		ts_close(ts);
		return -ENOMEM;
	}
	samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));
	if (!samp_mt[0]) {
		free(samp_mt);
		ts_close(ts);
		return -ENOMEM;
	}

	pre_samp_mt = malloc(sizeof(struct ts_sample_mt *));
	if (!pre_samp_mt) {
		ts_close(ts);
		return -ENOMEM;
	}
	pre_samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));
	if (!pre_samp_mt[0]) {
		free(pre_samp_mt);
		ts_close(ts);
		return -ENOMEM;
	}

	for ( i = 0; i < max_slots; i++)
		pre_samp_mt[0][i].valid = 0;

	

	while (1)
	{
		//第一步:读取触点数据
		ret = ts_read_mt(ts, samp_mt, max_slots, 1);
		if (ret < 0) {
			printf("ts_read_mt err\n");
			ts_close(ts);
			return -1;
		}

		//第二步:判断是否更新,将新数据拷贝到旧数据里
		for (i = 0; i < max_slots; i++)
		{
			if (samp_mt[0][i].valid)
			{
				memcpy(&pre_samp_mt[0][i], &samp_mt[0][i], sizeof(struct ts_sample_mt));
			}
		}

		//第三步:判断是否有两个触点,如果是两个,则执行打印
		touch_cnt = 0;
		for (i = 0; i < max_slots; i++)
		{
			if (pre_samp_mt[0][i].valid && pre_samp_mt[0][i].tracking_id != -1)
				point_pressed[touch_cnt++] = i;
		}

		if (touch_cnt == 2)
		{
			printf("distance: %08d\n", distance(&pre_samp_mt[0][point_pressed[0]], &pre_samp_mt[0][point_pressed[1]]));
		}
	}
	
	return 0;
}

実際の効果:

//交叉编译
arm-buildroot-linux-gnueabihf-gcc -o mt_cal_distance mt_cal_distance.c -lts

//复制到nfs挂载文件 nfs_rootfs
cp mt_cal_distance ~/nfs_rootfs

ボード上でプログラムを実行し、2 本の指をボード上に置き、結果を出力します。 

おすすめ

転載: blog.csdn.net/weixin_42373086/article/details/130138000