[Linux] Detalhes do sistema de entrada + combate de aplicativo de tela sensível ao toque (tslib)

Resumo do catálogo

Prefácio:

1. Sistema de entrada

Dois, estrutura do sistema de entrada do Linux

(1) A camada do driver do sistema de entrada

 (2) Camada principal do sistema de entrada

(3) Camada de eventos do sistema de entrada

3. Como o APP acessa o hardware

(1) Modo de consulta, sono-despertar

Exemplo concreto:

efeito real: 

(2) Modo POLL/SELECT

Exemplo concreto:

efeito real:

(3) Método de notificação assíncrona

 Exemplo concreto:

efeito real:

Quatro, estrutura da biblioteca tslib

(1) Breve descrição da tela capacitiva

(2) A utilidade da biblioteca tslib

(3) análise da estrutura tslib

Cinco, programa de teste baseado em tslib:

(1) Compilação cruzada da biblioteca tslib

(2) Compilação de teste no quadro

Resultado dos testes:

 6. Aplicação de combate baseada em tslib:

 Exemplo concreto:

efeito real:


Prefácio:

Link clássico: sempre acreditei que pensar e praticar com perguntas pode facilitar a compreensão e o aprendizado.

(1) O que é um sistema de entrada?

(2) Existem muitos tipos de dispositivos de entrada, como o sistema de entrada do Linux é gerenciado?

  • ① Como lidar com entrada complicada em um evento de entrada padrão unificado?
  • ②Quando há vários eventos, como o motorista notifica o APP que o evento foi totalmente enviado quando o motorista carrega o evento?
  • ③Qual é o processo específico de obtenção de dados pelo programa do usuário (APP)?

(3) De que forma o APP pode acessar o hardware? Como é alcançado?

(4) Pegue a tela de toque capacitiva como exemplo, use a biblioteca tslib para usar o dispositivo de entrada.

  • Qual é o papel da biblioteca tslib? Quais são as suas vantagens?
  • Como é a estrutura da biblioteca tslib? Como é o mecanismo interno?
  • Como usar a biblioteca tslib para implementar a funcionalidade do aplicativo? ---Prática de aplicação

O conteúdo do artigo a seguir responderá detalhadamente às perguntas acima. Se ajudar, preste atenção três vezes (^_^), nos dê mais apoio, vamos progredir juntos!

1. Sistema de entrada

O que é um sistema de entrada?

        Existem muitos dispositivos de entrada em nossa vida, como teclados, mouses, controles remotos, telas sensíveis ao toque, etc. Os usuários trocam dados com o sistema Linux por meio desses dispositivos.

         Se vários dispositivos de entrada podem ter uma interface unificada e também unificada no nível do driver e no nível do aplicativo, a fim de atender aos requisitos acima, o sistema Linux projetou um conjunto de estruturas compatíveis com todos os dispositivos de entrada - ou seja, o sistema de entrada .

Dois, estrutura do sistema de entrada do Linux

        Conforme mostrado na figura abaixo, o método de gerenciamento do sistema de entrada do Linux é dividido em três estágios, ① processar o evento de entrada em um padrão unificado e enviá-lo para a camada principal ② receber o evento de entrada e encaminhá-lo para a camada de evento (evento manipulador) ③ processar o evento, fornecer interface de acesso ao espaço do usuário

        

Segue uma explicação desde o hardware até os conceitos envolvidos no APP:

(1) A camada do driver do sistema de entrada

①Existem muitos dispositivos de entrada de hardware aqui, que irão gerar interrupções e enviar dados para o sistema, então como o sistema pode manipulá-los uniformemente e processá-los em eventos de entrada padrão uniformes?

O evento de entrada aqui é uma estrutura " struct input_event ", consulte o arquivo do kernel do Linux:

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

 

 Aqui, a estrutura específica do evento de entrada é unificada como tempo, tipo, código e valor.

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

type:表示哪类事件

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

value:表示事件值。

 ②Quando há vários eventos, como o motorista notifica o APP que o evento foi totalmente enviado quando o motorista carrega o evento?

Depois que o driver terminar de relatar uma série de dados, ele relatará um " evento síncrono ", indicando que o relatório de dados foi concluído.  Quando o "evento síncrono" é lido, sabe-se que os dados atuais foram lidos.
O evento síncrono também é uma estrutura input_event e seu tipo , código e valor são todos 0 .

 (2) Camada principal do sistema de entrada

A função da estação de transferência, a camada central pode decidir para qual manipulador encaminhar o evento de entrada para processamento. Existem muitos tipos de manipuladores , como: evdev_handler , kbd_handler , joydev_handler e assim por diante.

O mais comumente usado é evdev_handler : ele apenas salva a estrutura input_event no buffer do kernel, etc., e a retorna como está quando o APP a lê.

(3) Camada de eventos do sistema de entrada

Aqui, o evento de entrada carregado pela camada principal é processado e, em seguida, a interface do usuário é fornecida ao espaço do usuário.

③ Depois de entender a estrutura interna do sistema, qual é o processo específico para o programa do usuário (APP) obter dados?

  1. APP inicia uma operação de leitura e dorme se não houver dados
  2. O usuário opera o dispositivo e o hardware gera uma interrupção
  3. O driver correspondente à camada do driver do sistema de entrada lida com as interrupções.
  4. A camada central encaminha o evento de entrada para o manipulador correspondente para processamento, o evdev_handler mais comumente usado.
  5. Quando o APP estiver aguardando dados, o evdev_handler irá ativá-lo para que o APP possa obter os dados.

Existem dois métodos para o APP obter dados aqui, um é acessar diretamente o nó do dispositivo ou acessar indiretamente o nó do dispositivo por meio de bibliotecas como tslib e libinput.

Comandos de depuração úteis:

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

ls /dev/input/* -l

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

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

hexdump /dev/input/event1

3. Como o APP acessa o hardware

De que forma o APP pode acessar o hardware?

Existem quatro maneiras de o APP acessar o hardware: modo de consulta, modo de despertar, modo POLL/SELECT e modo de notificação assíncrona.

Caminho mecanismo
Investigar O patrão vem te incomodar de vez em quando
sono-vigília Geralmente deita e não faz nada, trabalha depois que o chefe liga
ENQUETE/SELECIONE Defina um despertador e trabalhe quando o tempo acabar ou quando o chefe ligar para você
notificação assíncrona Estou fazendo algum trabalho sozinho, e quando o chefe ligar para você, faça o trabalho que o chefe indicar

Os métodos acima, independentemente de suas vantagens e desvantagens, possuem seus cenários de aplicação, então qual é a implementação específica do método?

(1) Modo de consulta, sono-despertar

a diferença
modo de consulta

Quando o APP chamar a função open, passe "O_NONBLOCK" --- sem bloqueio

Quando o APP chama a função de leitura, se houver dados no driver, a função do APP retornará os dados, caso contrário, um erro será retornado imediatamente.

sono-vigília

Quando o APP chamar a função open, não passe em "O_NONBLOCK" --- bloqueando

Quando o APP chama a função de leitura, se houver dados no driver, a função APP retornará os dados; caso contrário, o APP irá dormir no modo kernel.

Exemplo concreto:

#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;
	
}

efeito real: 

Modo de consulta (sem bloqueio):

Obtenha as informações do dispositivo, abra o nó do dispositivo e leia o aplicativo, o driver não possui dados e sempre retornará o erro

 Despertar do sono (bloqueio):

Obtenha informações do dispositivo, abra o nó do dispositivo e leia o aplicativo. Se não houver dados, ele entrará no estado inativo. Quando a tela sensível ao toque for clicada, os dados serão retornados.

(2) Modo POLL/SELECT

O mecanismo POLL e o mecanismo SELECT são exatamente os mesmos, mas as funções da interface APP são diferentes.

Passar "timeout" ao chamar funções de poll e select. Durante este período, quando as condições forem adequadas (por exemplo, há dados para ler), ele retornará imediatamente, caso contrário, retornará um erro quando o "período de tempo limite" terminar.

Existem muitos tipos de eventos de monitoramento de sondagem/seleção, conforme mostrado na tabela a seguir:

Exemplo concreto:

#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;

efeito real:

(3) Método de notificação assíncrona

O mecanismo de notificação assíncrona é semelhante à interrupção desenvolvida pelo microcontrolador. Ou seja, o APP pode estar ocupado com seus próprios negócios. Quando o motorista tiver dados, ele enviará ativamente um sinal para o APP. Neste momento, o APP executa a função de processamento de sinal.

Além de esclarecer o breve processo e objetos acima, ainda existem algumas questões específicas que precisam ser resolvidas.

  1. Qual sinal o motorista envia para o APP? ----SIGIO (drive sinal comum)
  2. Como sinalizar? --- Kernel fornece funções
  3. Como conectar a função de processamento de sinal e o sinal: função de processamento de sinal de registro APP

① Existem muitos sinais em ...\Linux-4.9.88\include\uapi\asm-generic signal.h:

 ②APP fornece função de processamento de sinal de registro, mas também vinculado ao SIGIO, os detalhes são os seguintes:

Pensamentos adicionais:

  • O kernel tem muitos drivers, qual driver deve enviar o sinal SIGIO para o aplicativo?
    • O nó do dispositivo do driver de relógio do APP
  • Como o motorista sabe enviar um sinal para o APP atual?
    • APP informa seu ID de processo ao driver

 Exemplo concreto:

#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;
	
}

efeito real:

Quatro, estrutura da biblioteca tslib

(1) Breve descrição da tela capacitiva

Existe um chip de controle na tela capacitiva, que gera periodicamente um sinal de condução, o eletrodo receptor recebe o sinal e pode medir a magnitude da carga. Quando a tela capacitiva é pressionada, equivale a introduzir uma nova capacitância, que afeta a quantidade de carga recebida pelo eletrodo receptor. O chip de controle principal pode calcular a posição do contato de acordo com a magnitude da carga.

Para analisar os dados da tela de toque capacitiva, execute o seguinte comando no nó do dispositivo /dev/input/event1 correspondente à tela capacitiva na placa de desenvolvimento:

hexdump /dev/input/event1

①Toque na tela sensível ao toque com um dedo para obter:

② Clique na tela sensível ao toque com dois dedos para obter:

(2) A utilidade da biblioteca tslib

Qual é o papel da biblioteca tslib? Quais são as suas vantagens?

Podemos ver que haverá muitos eventos quando a tela sensível ao toque for clicada e é inconveniente filtrá-los e processá-los.

tslib é uma biblioteca de código aberto para telas sensíveis ao toque, você pode usá-la para acessar dispositivos com tela sensível ao toque e pode adicionar vários "filtros" (bibliotecas de filtragem, ou seja, vários processamentos) aos dispositivos de entrada.

(3) análise da estrutura tslib

Como é a estrutura da biblioteca tslib? Como é o mecanismo interno?

A estrutura do tslib é mostrada na figura:

 Os principais códigos do tslib são:

  • funções src/interface
    • ts_setup.c
    • ts_open.c
    • ts_config.c
  • plugins/módulo módulo, o seguinte é um módulo
    • linear.c
    • dejitter.c
    • pthres.c
    • input_raw.c     
  • testes / programa de teste
    • ts_test.c
    • ts_test_mt.c
    • ts_print.c
    • ts_print_mt.c

Analise toda a estrutura tslib, consulte o programa de amostra ( ts_test.c e ts_test_mt.c ), para tela de toque único e tela multitoque. O processo de operação do tslib é o seguinte:

  • 1. Chame ts_open para abrir o nó do dispositivo e construir a estrutura tsdev. O conteúdo desta estrutura é o seguinte:
  • 2. Chame ts_config para ler o processamento do arquivo de configuração.Para todos os módulos, será inserido o cabeçalho da lista encadeada. module e module_raw correspondem a diferentes listas vinculadas list e list_raw na estrutura tsdev.
  • 3. Chame recursivamente cada módulo, input_raw→pthres→dejitter→linear module, execute o processamento de dados nos dados brutos obtidos do nó do dispositivo e retorne os dados finais. A figura abaixo é o processo recursivo.

Portanto, o corpo principal chama três funções: ts_setup, ts_read ou ts_readmt, ts_close.

Cinco, programa de teste baseado em tslib:

Como usar a biblioteca tslib para implementar a funcionalidade do aplicativo?

(1) Compilação cruzada da biblioteca tslib

// Configure a cadeia de ferramentas de compilação cruzada ( preste atenção ao verificar ao copiar para ver se é consistente)

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin

// descompactação da biblioteca tslib

cp /home/book/01_all_series_quickstart/04_Conhecimento básico de desenvolvimento de aplicativos Linux integrado/source/11_input/02_tslib/tslib-1.21.tar.xz ./

tar xJf tslib-1.21.tar.xz

// Comando universal de compilação cruzada

cd tslib-1.21

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

fazer

make install DESTDIR=$PWD/tmp

// Coloque os arquivos de cabeçalho e os arquivos de biblioteca no diretório da cadeia de ferramentas

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/ lib/

(2) Compilação de teste no quadro

// Copia o arquivo para a pasta nfs_rootfs montada no nfs

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

// Copia os arquivos para lib, bin e etc no quadro

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

// Feche o programa qt GUI padrão (sujeito às condições atuais), abra /etc/init.d/ para ver o nome do programa qtGUI

// Se for reaberto, o arquivo correspondente será movido de volta

mv /etc/init.d/S99myirhmi2 /root

reinício

//teste

ts_test_mt

Resultado dos testes:

 6. Aplicação de combate baseada em tslib:

 Implemente um programa que imprima continuamente a distância entre 2 contatos

A tela sensível ao toque pode suportar vários pontos de toque, por exemplo, 5. Para simplificar o processamento, o tslib retornará os dados de cinco pontos de toque, mesmo que haja apenas dois pontos de toque.

O driver utiliza slot e tracing_id para identificar um contato, quando tracing_id for igual a -1, indica que o contato foi liberado.

Assim você pode julgar se os dados são válidos de acordo com esta marca, então quando dois contatos forem válidos, imprima a distância entre eles.

Função principal: ts_read_mt

Quatro parâmetros: estrutura tsdev, max_slots (número máximo de pontos), estrutura ts_sample_mt (armazenamento de dados), nr

 

 Exemplo concreto:

De acordo com o conteúdo da quarta parte, as ideias acima e o programa de amostra ts_test_mt.c completam a redação do programa.

#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;
}

efeito real:

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

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

Execute o programa no quadro, coloque dois dedos nele e imprima o resultado: 

Acho que você gosta

Origin blog.csdn.net/weixin_42373086/article/details/130138000
Recomendado
Clasificación