【电子量产工具】2.输入系统

简介: 【电子量产工具】2.输入系统

前言

最近看了 电子量产工具 这个项目,本专栏是对该项目的一个总结。

对于输入系统,这里只介绍 触摸屏线程 和 网络线程。


一、输入系统分析

在大纲的输入管理器下有 三种输入方式:触摸屏线程,网络线程,标准输入线程

为什么要使用多线程呢?

在单线程程序中,如果某个任务需要花费很长时间,会导致整个程序进入阻塞状态,用户体验不佳。使用多线程可以将这类耗时任务放在后台线程中执行,保持前台线程的响应性,提升用户体验,提高并发性和效率。

底层的触摸屏线程,网络线程标,标准输入线程 与 板子直接交互,负责处理数据和逻辑。

输入管理器 向下负责管理各种输入设备,向上提供APP 所需的各种函数,起承上启下作用。

我们需要写出底层代码,由中间层 调用底层代码 供 上层APP直接使用。

二、封装输入结构体

  1. InputDevice 结构体 模块化 输入设备。
    后面会根据 name 寻找目标输入设备。
typedef struct InputDevice {
  char *name;
  int (*GetInputEvent)(PInputEvent ptInputEvent);           //获取输入事件
  int (*DeviceInit)(void);                      //输入设备初始化
  int (*DeviceExit)(void);
  struct InputDevice *ptNext;                   //指针,用于连接链表
}InputDevice, *PInputDevice;
  1. InputEvent 结构体 模块化 输入事件。
typedef struct InputEvent {
  struct timeval  tTime;                      //触发的时间
  int iType;                            //触发事件的类型
  int iX;                         //对于触摸屏事件的触摸点x值
  int iY;                         //触摸屏事件的触摸点y值
  int iPressure;                      //触摸屏事件的触摸点压力值
  char str[1024];                     //网络输入事件的输入字符串
}InputEvent, *PInputEvent;

三、底层 touchscreen

  1. 实现 InputEvent 结构体。
  2. 触摸屏事件 ,需要使用 tslib 库
    tslib 是一个触摸屏的开源库,可以使用它来访问触摸屏设备。

ts_setup 函数用于在 tslib 中 初始化触摸屏设备 并 设置相关参数,包括 打开触摸屏设备、校准触摸屏、配置参数和注册

  1. 触摸事件回调函数等。

  1. 获取 触摸屏 事件的 输入数据。

    ts_read:用于从触摸屏设备中读取触摸事件的数据,并存储到 samp 结构体中。
    struct ts_sample:用于存储获取到的触摸屏输入数据。它通常包含一些字段,表示触摸点的位置、时间戳和其他相关信息。

四、底层 netinput

netinputtouchscreen 一样 都需要实现 InputEvent 结构体,初始化输入设备,获取输入事件数据。

  1. 涉及网络通信,要了解一些基本知识:
  • 网络传输中的2个对象:serverclient
  • UDP 网络通信大概交互图:
  1. 网络输入初始化。
    AF_INET 是针对 Internet 的通讯协族,可以允许远程通信使用。
    SOCK_DGRAM 表明用的是 UDP 协议
static int NetinputDeviceInit(void)
{
  struct sockaddr_in tSocketServerAddr;
  int iRet;
  /* socket 函数创建一个套接字,成功时返回文件描述符 */
  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;
}

接收输入事件。

recvfrom 通常用于无连接套接字, 可以获得发送者的地址。

gettimeofday 函数,它

  1. 是一个 C 标准库中的函数,主要用于获取当前的系统时间
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;
}

五、显示管理层

  1. 将 触摸屏输入 和 网络输入 注册进入链表。头添加的方式,添如链表。
  2. 对于环形缓冲区这里就不多说了,不懂的可以参考我之前的文章:环形缓冲区

如果我们频繁快速的持续向计算机输入数据,计算机可能执行某个进程不能及时的执行输入的数据,导致数据丢失。这时,我们可以将要输入的数据放入环形缓冲区内,计算机就不会造成数据丢失。

#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;
  }
}
  1. 初始化设备并创建线程。环形缓冲区有数据则唤醒线程。

这里使用了 互斥锁操作,在调用 pthread_cond_wait 前,必须先获取互斥锁,即使用 pthread_mutex_lock 函数。这确保了在等待条件期间的正确 同步 。

pthread_cond_wait 函数会在等待时自动解锁并将线程置于等待状态。当满足特定条件时,该线程会被唤醒并重新获得锁

使用 pthread_cond_signal 函数来 唤醒 等待线程。

六、测试程序

  1. 触摸屏输入事件测试。
    只需要在 main 函数里调用 输入管理层的封装好的函数即可。
  2. 网络输入事件测试。
    还需要编写一个 client.c 文件,进行客户端 和 服务端的通信。
int main(int argc, char **argv)
{
  int ret;
  InputEvent event;
  InputInit();
  IntpuDeviceInit();
  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; 
}

测试效果

触摸屏事件:

网络输入事件:


总结

输入系统 的 触摸屏事件 设计 tslib 库,所以在编译时,一定要链接库。

网络输入事件的 通信 使用 UDP 会比较容易,那些函数 以及 通信流程 要了解。

网络输入事件 包括 客户端服务端 间的通信,要编写 client 文件。

相关文章
|
7月前
【电子量产工具】6. 业务系统
【电子量产工具】6. 业务系统
35 0
|
7月前
|
中间件
【电子量产工具】大纲分析
【电子量产工具】大纲分析
28 0
|
7月前
|
编解码 中间件 Linux
【电子量产工具】1.显示系统
【电子量产工具】1.显示系统
43 0
|
7月前
【电子量产工具】5.页面系统
【电子量产工具】5.页面系统
22 0
|
7月前
|
编解码
【电子量产工具】3.文字系统
【电子量产工具】3.文字系统
23 0
|
7月前
|
vr&ar
【电子量产工具】4. UI系统
【电子量产工具】4. UI系统
45 0
|
Android开发
当中兴安卓手机遇上农行音频通用K宝 -- 卡在“正在通讯”,一直加载中
当中兴安卓手机遇上农行音频通用K宝 今天去新版一张农行卡,开通手机银行用到音频通用K宝,弄了一上午银行工作人员也不懂要怎么弄才能用,因为在下载证书时老是加载的界面,显示正在通讯。
1394 0