Resumo do catálogo
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
(3) Método de notificação assíncrona
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
6. Aplicação de combate baseada em tslib:
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?
(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.
(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?
- APP inicia uma operação de leitura e dorme se não houver dados
- O usuário opera o dispositivo e o hardware gera uma interrupção
- O driver correspondente à camada do driver do sistema de entrada lida com as interrupções.
- A camada central encaminha o evento de entrada para o manipulador correspondente para processamento, o evdev_handler mais comumente usado.
- 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.
- Qual sinal o motorista envia para o APP? ----SIGIO (drive sinal comum)
- Como sinalizar? --- Kernel fornece funções
- 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: