Linux基础项目开发1:量产工具——输入系统(三)

news/2024/4/17 8:05:26

前言:

前面我们已经实现了显示系统,现在我们来实现输入系统,与显示系统类似,下面让我们一起来对输入系统进行学习搭建吧

目录

一、数据结构抽象

1. 数据本身

2. 设备本身:

3. input_manager.h

二、触摸屏编程 

touchscreen.c 

三、触摸屏单元测试 

1.touchscreen.c 

2.上机测试

四、网络编程 

netiput.c 

五、网络单元测试

1.netiput.c 

2.client.c

3.上机测试 


、数据结构抽象

        对于每一个设备,每一个模块,都用一个结构体来表示它,以后就会很方便的替换这些模块,所以对于设备本身我们需要抽象出一个结构体,所以对于输入系统我们需要抽象出两个结构体,这两个结构分别是:1.数据本身、2.设备本身。

输入来源:1.网络数据输入   2.点击事件输入 

1. 数据本身

通过这个数据抽象,就可以既表示出触摸屏本身的数据也能表示出网络本身的数据

触摸屏数据:

int iType :           判断是什么数据,是触摸屏数据还是网络数据

int iX、int iY:        判断具体坐标

int iPressure:     压力值

网络数据: 

char str[1024]:   网络数据

2. 设备本身:

 *name:                            设备名字

(*GetInputEvent)(PInputEvent ptInputEvent):        上层代码通过这个函数得到数据,返回值判断数据是否成功,如果成功数据保存至PInputEvent ptInputEvent中

(*DeviceInit):                 提供初始化函数,比如打开设备节点等

(*DeviceExit):                提供退出函数

InputDevice *ptNext :   如果想支持多个设备,就应该将这些设备列到一起,所以需要一个链表指针

3. input_manager.h

#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H#include <sys/time.h>#ifndef NULL
#define NULL (void *)8#endif#define INPUT_TYPE_TOUCH 1
#define INPUT_TYPE_NET   2typedef struct InputEvent {struct timeval	tTime;int iType;int iX;int iY;int iPressure;char str[1024];
}InputEvent, *PInputEvent;typedef struct InputDevice {char *name;int (*GetInputEvent)(PInputEvent ptInputEvent);int (*DeviceInit)(void);int (*DeviceExit)(void);struct InputDevice *ptNext;
}InputDevice, *PInputDevice;void RegisterInputDevice(PInputDevice ptInputDev);
void InputInit(void);#endif

第9行:触摸屏事件

第10行:网络事件

第13~20行:抽象出数据本身结构体InputEvent

        第14行:定义出一个时间

第23~29行:抽象出设备本身结构体InputDevice 

第32行:用于输入系统管理,提供一个注册函数,下面的各个设备进行引用它

第33行:用注册函数进行引用

二、触摸屏编程 

设备结构体的实现如下结构体:使用tslib

touchscreen.c 

#include <input_manager.h>
#include <tslib.h>
#include <stdio.h>
static struct tsdev *g_ts;static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{struct ts_sample samp;int ret;ret = ts_read(g_ts, &samp, 1);if (ret != 1)return -1;ptInputEvent->iType     = INPUT_TYPE_TOUCH;ptInputEvent->iX        = samp.x;ptInputEvent->iY        = samp.y;ptInputEvent->iPressure = samp.pressure;ptInputEvent->tTime     = samp.tv;return 0;
}static int TouchscreenDeviceInit(void)
{g_ts = ts_setup(NULL, 0);if (!g_ts){printf("ts_setup err\n");return -1;}return 0;
}static int TouchscreenDeviceExit(void)
{ts_close(g_ts);return 0;
}static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent  = TouchscreenGetInputEvent,.DeviceInit     = TouchscreenDeviceInit,.DeviceExit     = TouchscreenDeviceExit,
};void TouchscreenRegister(void)
{TouchscreenRegister(&g_tTouchscreenDev);
}

第4行:初始化结果放到全局变量g_ts

第8行:定义一个ts_sample 的结构体samp

static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)

 第6~23行:获得输入事件

static int TouchscreenDeviceInit(void)

第25~35行:对设备的初始化

static int TouchscreenDeviceExit(void)

第37~41行:关闭设备

void TouchscreenRegister(void)

第52~55行:将结构体g_tTouchscreenDev注册到上一层,上一层就是输入管理器,用注册函数进行引用

 

三、触摸屏单元测试 

1.touchscreen.c 

在touchscreen.c 代码下面加一个main函数进行触摸屏的单元测试


#if 1int main(int argc, char **argv)
{InputEvent event;int ret;g_tTouchscreenDev.DeviceInit();while (1){ret = g_tTouchscreenDev.GetInputEvent(&event);if (ret) {printf("GetInputEvent err!\n");return -1;}else{printf("Type      : %d\n", event.iType);printf("iX        : %d\n", event.iX);printf("iY        : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}}return 0;
}#endif

注意:#if 1则执行,# if 0则不执行

第5行:定义一个InputEvent 结构体 event 获取数据本身传给GetInputEvent()

第8行:调用g_tTouchscreenDev结构体中的DeviceInit()进行初始化

第12行:调用g_tTouchscreenDev结构体中的GetInputEvent(&event)进行获取输入事件

第17~23行:如果返回值ret=0的话,则将数据信息打印出来

EXTRA_CFLAGS  := 
CFLAGS_file.o := #obj-y += disp_test.o

将unittest文件夹中的Makefile中的main函数部分注释掉,因为一个程序只能允许有一个main

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += touchscreen.o

input目录下的Makefile


CROSS_COMPILE ?= 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nmSTRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdumpexport AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMPCFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/includeLDFLAGS := -ltsexport CFLAGS LDFLAGSTOPDIR := $(shell pwd)
export TOPDIRTARGET := testobj-y += display/
obj-y += input/all : start_recursive_build $(TARGET)@echo $(TARGET) has been built!start_recursive_build:make -C ./ -f $(TOPDIR)/Makefile.build$(TARGET) : built-in.o$(CC) -o $(TARGET) built-in.o $(LDFLAGS)clean:rm -f $(shell find -name "*.o")rm -f $(TARGET)distclean:rm -f $(shell find -name "*.o")rm -f $(shell find -name "*.d")rm -f $(TARGET)

需要修改顶层目录下的Makefile

第20行:LDFLAGS := -lts 设置链接

第31行:打开input目录下的文件

2.上机测试

进行make编译,如果出现以下情况,则重新译一下tslib

 上板效果如下:点击开发板会将点击的信息显示出来

四、网络编程 

对网络输入构造出同触摸屏编程中一样的结构体

netiput.c 

#include <input_manager.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>/* socket* bind* sendto/recvfrom*/#define SERVER_PORT 8888static int g_iSocketServer;static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{struct sockaddr_in tSocketClientAddr;struct int iRecvLen;char aRecvBuf[1000];int iAddrLen = sizeof(struct sockaddr);iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (iRecvLen > 0){aRecvBuf[iRecvLen] = '\0';//printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);ptInputEvent->iType 	= INPUT_TYPE_NET;gettimeofday(&ptInputEvent->tTime, NULL);strncpy(ptInputEvent->str, aRecvBuf, 1000);ptInputEvent->str[999] = '\0';return 0;}elsereturn -1;
}static int NetinputDeviceInit(void)
{struct sockaddr_in tSocketServerAddr;int iRet;g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == g_iSocketServer){printf("socket error!\n");return -1;}tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;memset(tSocketServerAddr.sin_zero, 0, 8);iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1;}return 0;
}static int NetinputDeviceExit(void)
{close(g_iSocketServer);	return 0;
}static InputDevice g_tNetinputDev ={.name = "touchscreen",.GetInputEvent  = NetinputGetInputEvent,.DeviceInit     = NetinputDeviceInit,.DeviceExit     = NetinputDeviceExit,
};void NetInputRegister(void)
{NetInputRegister(&g_tNetinputDev);
}

第29行:端口

第22行:获得的数据  

第28行:获得的数据保存到ucRecvBuf里面

static int NetinputGetInputEvent(PInputEvent ptInputEvent)

   第24~45行:获得输入事件

static int NetinputDeviceInit(void)

   第47~74行:初始化socket

static int NetinputDeviceExit(void)

 第76~80行:关闭设备

void NetInputRegister(void)

第88~91行:将结构体g_tNetinputDev注册到上一层,上一层就是输入管理器,用注册函数进行引用

 

五、网络单元测试

1.netiput.c 

这里充当服务器端,用来接收数据


#if 1int main(int argc, char **argv)
{InputEvent event;int ret;g_tNetinputDev.DeviceInit();while (1){ret = g_tNetinputDev.GetInputEvent(&event);if (ret) {printf("GetInputEvent err!\n");return -1;}else{printf("Type      : %d\n", event.iType);printf("str       : %s\n", event.str);}}return 0;
}#endif

注意:需要把触摸屏里面的main函数注释掉

 Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := obj-y += touchscreen.o
obj-y += netinput.o

2.client.c

这里充当客户端,用来发送数据

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>/* socket* connect* send/recv*/#define SERVER_PORT 8888int main(int argc, char **argv)
{int iSocketClient;struct sockaddr_in tSocketServerAddr;int iRet;int iSendLen;int iAddrLen;if (argc != 3){printf("Usage:\n");printf("%s <server_ip> <str>\n", argv[0]);return -1;}iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}memset(tSocketServerAddr.sin_zero, 0, 8);#if 0iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	if (-1 == iRet){printf("connect error!\n");return -1;}
#endifiAddrLen = sizeof(struct sockaddr);iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,(const struct sockaddr *)&tSocketServerAddr, iAddrLen);close(iSocketClient);return 0;
}
arm-buildroot-linux-gnueabihf-gcc -o client unittest/client.c
make
cp test ~/nfs_rootfs/[root@100ask:~]# cd /mnt/

3.上机测试 

六、 输入系统的框架

         如果只有一个输入设备,那么我们只需要写一个main函数即可,但是我们需要好几个设备输入,那么要想统一管理,那么就需要设计一个输入管理系统进行管理选择到底哪个设备进行输入。

        在输入管理器中我们需要初始化触摸屏,并且为触摸屏提供一个线程,初始化网络设备,为网络设备提供一个线程,上层应用程序提供一些函数就可以方便的得到各类的输入事件,线程的创建线程的管理都由输入管理器进行设置。

 1.input_manager.c

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>static PInputDevice g_InputDevs  = NULL;void RegisterInputDevice(PInputDevice ptInputDev)
{ptInputDev->ptNext = g_InputDevs;g_InputDevs = ptInputDev;
}/*  */
void InputInit(void)
{/* regiseter touchscreen */extern void TouchscreenRegister(void);TouchscreenRegister();/* regiseter netinput */extern void NetInputRegister(void);NetInputRegister();
}static void *input_recv_thread_func (void *data)
{PInputDevice tInputDev = (PInputDevice)data;InputEvent tEvent;int ret;while (1){/* 读数据 */ret = tInputDev->GetInputEvent(&tEvent);if (!ret){	/* 保存数据 *//* 唤醒等待数据的线程 */pthread_mutex_lock(&g_tMutex);pthread_cond_wait(&g_tConVar, &g_tMutex);	pthread_mutex_unlock(&g_tMutex);}}return NULL;
}void IntpuDeviceInit(void)
{int ret;pthread_t tid;/* for each inputdevice, init, pthread_create */PInputDevice ptTmp = g_InputDevs;while (ptTmp){/* init device */ret = ptTmp->DeviceInit();/* pthread create */if (!ret){ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);}ptTmp= ptTmp->ptNext;}
}int GetInputEvent(PT_InputEvent ptInputEvent)
{/* 无数据则休眠 *//* 返回数据 */
}

第7行:这一层要接受下一层传输上来的注册信息 ,所以要定义一个 g_InputDevs的链表头

void RegisterInputDevice(PInputDevice ptInputDev)

第9~13行:提供一个注册函数

void InputInit(void)

第16~25行:向上提供一个InputInit(void) 函数

        第19、20行:注册触摸屏设备

        第23、24行:注册网络设备

注意:其中extern是外部函数的意思

void IntpuDeviceInit(void)

第54~74行:对于每个设备初始化它,并且创建线程

int GetInputEvent(PT_InputEvent ptInputEvent)

第76行:最重要的一个函数 ,最上层的代码只要调用这个函数就可以得到这些设备的数据

         

怎么避免数据丢失 ?

比如触摸屏,它会一下子上报很多数据

对于网络输入,也有可能同时又多个client发来数据

所以,不能使用单一的变量来保存数据,而是使用一个数组来保存数据

使用“环形缓冲区


https://www.xjx100.cn/news/3118824.html

相关文章

python类的多重继承继承和查找顺序

1 python类的多重继承继承和查找顺序 python中&#xff0c;类的多重继承允许子类继承多个基类&#xff0c;子类可以访问多个基类的属性和方法。 1.1 多重继承基础 用法 class MulClass(BaseC1,BaseC2,...BaseCn):pass描述 Mulclass&#xff1a;子类&#xff08;或者称混合…

面试篇之微服务(二)

目录 服务容灾 21.什么是服务雪崩&#xff1f; 22.什么是服务熔断&#xff1f;什么是服务降级&#xff1f; 什么是服务熔断&#xff1f; 什么是服务降级&#xff1f; 有哪些熔断降级方案实现&#xff1f; 23.Hystrix怎么实现服务容错&#xff1f; 24.Sentinel怎么实现限…

如何在Rocky Linux中安装nmon

一、环境基础 [rootlocalhost nmon16d]# cat /etc/redhat-release Rocky Linux release 9.2 (Blue Onyx) [rootlocalhost nmon16d]# uname -r 5.14.0-284.11.1.el9_2.x86_64 [rootlocalhost nmon16d]# 二、安装步骤 在Rocky Linux和AlmaLinux等基于RHEL 的发行版上&#xff…

设计规则:模块化的力量

这是一本比较冷门的书**《设计规则&#xff1a;模块化的力量》**&#xff0c;虽然豆瓣上只有58个评价&#xff0c;但是确实能学到很多东西。 这本书对我非常深远。不是是投资&#xff0c;创业&#xff0c;还是其他领域&#xff0c;模块化思想都能帮上你。这本书告诉我们生万物…

Arkts@Watch装饰器与内置组件双向同步深度讲解与实战应用【鸿蒙专栏-14】

文章目录 ArkTS 状态管理深度解析:@Watch 和 $$ 运算符的妙用@Watch 装饰器:状态变量的敏感监听装饰器说明语法说明观察变化和行为表现限制条件使用场景$$ 运算符:内置组件状态的双向同步使用规则使用示例ArkTS 深度探索:@Watch 装饰器与 $$ 运算符的进阶应用进阶应用:@Wa…

Java数据结构之《合并线性表》问题

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道难度中等偏下的一道编程题(此方法为博主自己研究&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论提出意见&#xff0c;我会第一时间改进代码&#xff0c;谢谢&#xff01;) 后面其他编程题只要我…

使用Feign简化Spring Boot微服务间的调用

摘要&#xff1a; 在微服务架构中&#xff0c;服务间的通信是至关重要的。Spring Cloud提供了多种工具&#xff0c;其中Feign是一款声明式的Web服务客户端&#xff0c;能够显著简化服务调用的过程。本文将详细介绍在Spring Boot应用中如何使用Feign进行微服务之间的调用。 正文…

VR虚拟教育展厅,为教学领域开启创新之路

线上虚拟展厅是一项全新的展示技术&#xff0c;可以为参展者带来不一样的观展体验。传统的实体展览存在着空间限制、时间限制以及高昂的成本&#xff0c;因此对于教育领域来说&#xff0c;线上虚拟教育展厅的出现&#xff0c;可以对传统教育方式带来改革&#xff0c;凭借强大的…