量产工具一一输入系统(二)

目录

前言

一、输入系统数据结构抽象

1.input_manager.h

二、触摸屏编程和测试

1.touchscreen.c

2.上机测试

三、网络编程和测试

1.netinput.c

2.client.c

3.上机测试

四、输入系统的框架

1.框架思路

2.环形缓冲区

3.input_manager.c

五、输入管理单元测试

1.input_test.c

2.上机测试


前言

前面我们实现了显示系统框架,链接:

接下来我们来实现输入系统框架。

一**、输入系统数据结构抽象**

对于每一个设备,每一个模块,都用一个结构体来表示它,以后就会很方便的替换这些模块,所以对于设备本身我们需要抽象出结构体 InPutDevice,初始化各个模块。

对于输入系统我们需要抽象出结构体 InputEvent,以便于判断是触摸屏输入还是网络输入。

1.input_manager.h

cpp 复制代码
#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H

#include <sys/time.h>
#include <common.h>

#define INPUT_TYPE_TOUCH 1
#define INPUT_TYPE_NET   2

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

#endif

二、触摸屏编程和测试

触摸屏编程原理和实操具体可看:

1.touchscreen.c

cpp 复制代码
#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,
};

#if 1

int 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

2.上机测试

开发板上电测试:触摸屏幕后会显示X,Y方向坐标,压力值和类型(类型一为点击事件输入 ,类型二为网络数据输入)

三、网络编程和测试

网络编程代码编写主要分为服务端和客户端,具体介绍可看:

1.netinput.c

cpp 复制代码
#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>

#include <input_manager.h>

/* socket
 * bind
 * sendto/recvfrom
 */

#define SERVER_PORT 8888

static int g_iSocketServer;

static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{
	struct sockaddr_in tSocketClientAddr;
	int iRecvLen;
	char aRecvBuf[1000];
	
	unsigned 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;
	}
	else
		return -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,
};

#if 1

int 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

2.client.c

cpp 复制代码
#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 8888

int 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 0
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
	if (-1 == iRet)
	{
		printf("connect error!\n");
		return -1;
	}
#endif

	iAddrLen = sizeof(struct sockaddr);
	iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,
	              (const struct sockaddr *)&tSocketServerAddr, iAddrLen);

	close(iSocketClient);
	
	return 0;
}

3.上机测试

开发板上电测试:

./test & 将可执行文件 test 放在后台运行,结束后可通过 kill -9 408 杀死该进程

127.0.0.1 为主机环回地址,用于让计算机进行自我回路测试和通信

四、输入系统的框架

1.框架思路

1.输入管理器支持多个设备,这多个设备怎么管理起来呢?

使用InputInit 创建一个链表,链表指向触摸屏设备本身和网络设备本身,通过遍历链表就可以找到所有的输入设备。

2.对于每一个设备都可以调用里面的 GetInputEvent 来获得数据,如果想同时获得多个设备的输入数据的话,那么该怎么获得?

调用IntpuDeviceInit从链表里面把每一个设备取出来,调用里面的DeviceInit 初始化,并且为每一个输入设备创造一个线程thread,线程不断调用设备里面的 GetInputEvent 等待数据,一旦得到数据,就可以将获得的数据放到某个buffer。

3.上层的应用程序调用 GetInputEvent 时候就会去某个buffer里面查看是否有数据,有数据则返回,没数据则休眠 。

2.环形缓冲区

环形缓冲区实质也是一个一维数组,并不是一个环形的数组。

3.input_manager.c

在这里我们用到线程,编译时需要链接到 pthread库,具体可看:

  1. 注册新的输入设备到全局链表 g_InputDevs 中。
  2. 初始化输入系统,包括注册触摸屏和网络输入设备。
  3. 为每个输入设备创建一个接收线程,用于从设备读取数据并保存到环形缓冲区。
  4. 使用互斥锁和条件变量实现线程同步,确保数据的安全访问和线程的正确唤醒。
  5. 提供一个 GetInputEvent 函数,用于从环形缓冲区获取输入事件,如果没有可用事件则线程会休眠等待。
cpp 复制代码
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

#include <input_manager.h>

static PInputDevice g_InputDevs  = NULL;

static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;

/* start of 实现环形buffer */
#define BUFFER_LEN 20
static int g_iRead  = 0;
static int g_iWrite = 0;
static InputEvent g_atInputEvents[BUFFER_LEN];


static int isInputBufferFull(void)
{
	return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN));
}

static int isInputBufferEmpty(void)
{
	return (g_iRead == g_iWrite);
}

static void PutInputEventToBuffer(PInputEvent ptInputEvent)
{
	if (!isInputBufferFull())
	{
		g_atInputEvents[g_iWrite] = *ptInputEvent;
		g_iWrite = (g_iWrite + 1) % BUFFER_LEN;
	}
}


static int GetInputEventFromBuffer(PInputEvent ptInputEvent)
{
	if (!isInputBufferEmpty())
	{
		*ptInputEvent = g_atInputEvents[g_iRead];
		g_iRead = (g_iRead + 1) % BUFFER_LEN;
		return 1;
	}
	else
	{
		return 0;
	}
}


/* end of 实现环形buffer */

void RegisterInputDevice(PInputDevice ptInputDev)
{
	ptInputDev->ptNext = g_InputDevs;
	g_InputDevs = ptInputDev;
}

/*  */
void InputSystemRegister(void)
{
	/* regiseter touchscreen */
	extern void TouchscreenRegister(void);
	TouchscreenRegister();

	/* regiseter netinput */
	extern void NetInputRegister(void);
	NetInputRegister();
}

static void *input_recv_thread_func (void *data)
{
	PInputDevice ptInputDev = (PInputDevice)data;
	InputEvent tEvent;
	int ret;
	
	while (1)
	{
		/* 读数据 */
		ret = ptInputDev->GetInputEvent(&tEvent);

		if (!ret)
		{	
			/* 保存数据 */
			pthread_mutex_lock(&g_tMutex);
			PutInputEventToBuffer(&tEvent);

			/* 唤醒等待数据的线程 */
			pthread_cond_signal(&g_tConVar); /* 通知接收线程 */
			pthread_mutex_unlock(&g_tMutex);
		}
	}

	return NULL;
}

void InputDeviceInit(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(PInputEvent ptInputEvent)
{
	InputEvent tEvent;
	int ret;
	/* 无数据则休眠 */
	pthread_mutex_lock(&g_tMutex);
	if (GetInputEventFromBuffer(&tEvent))
	{
		*ptInputEvent = tEvent;
		pthread_mutex_unlock(&g_tMutex);
		return 0;
	}
	else
	{
		/* 休眠等待 */
		pthread_cond_wait(&g_tConVar, &g_tMutex);	
		if (GetInputEventFromBuffer(&tEvent))
		{
			*ptInputEvent = tEvent;
			ret = 0;
		}
		else
		{
			ret = -1;
		}
		pthread_mutex_unlock(&g_tMutex);		
	}
	return ret;

}

五、输入管理单元测试

1.input_test.c

cpp 复制代码
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>

#include <input_manager.h>

int main(int argc, char **argv)
{
	int ret;
	InputEvent event;
	
	InputSystemRegister();
	InputDeviceInit();

	while (1)
	{
		printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
		ret = GetInputEvent(&event);

		printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType);
			if (event.iType == INPUT_TYPE_TOUCH)
			{
				printf("Type      : %d\n", event.iType);
				printf("iX        : %d\n", event.iX);
				printf("iY        : %d\n", event.iY);
				printf("iPressure : %d\n", event.iPressure);
			}
			else if (event.iType == INPUT_TYPE_NET)
			{
				printf("Type      : %d\n", event.iType);
				printf("str       : %s\n", event.str);
			}
		}
	}
	return 0;	
}

2.上机测试

上电开发板,编译测试:

相关推荐
云飞云共享云桌面37 分钟前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq1 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮2 小时前
Linux 使用中的问题
linux·运维
dsywws3 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零4 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程5 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬6 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
sun0077006 小时前
拷贝 cp -rdp 和 cp -a
linux·运维·服务器
wowocpp6 小时前
ubuntu 22.04 server 安装 anaconda3
linux·运维·ubuntu
乡村农夫6 小时前
cuda 环境搭建
linux