嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十五)输入系统应用编程(中)

本文涉及的产品
语种识别,语种识别 100万字符
文本翻译,文本翻译 100万字符
文档翻译,文档翻译 1千页
简介: 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十五)输入系统应用编程

1.3.4 查询方式


APP调用open函数时,传入“O_NONBLOCK”表示“非阻塞”。

APP调用read函数读取数据时,如果驱动程序中有数据,那么APP的read函数会返回数据,否则也会立刻返回错误。


1.3.5 休眠-唤醒方式


APP调用open函数时,不要传入“O_NONBLOCK”。

APP调用read函数读取数据时,如果驱动程序中有数据,那么APP的read函数会返回数据;否则APP就会在内核态休眠,当有数据时驱动程序会把APP唤醒,read函数恢复执行并返回数据给APP。

//打开设备节点ioctl
#include <linux/input>
/*./01_get_input_info     /dev/input/event0   noblock */  
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
  int fd;
  int len;
  unsigned int evbit[2];
  char ev_names={
  "EV_SYN",
  "EV_KEY",
  "EV_REL",
  "EV_ABS",
  "EV_MSC",
  "EV_SW",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "NULL",
  "EV_LED",
  "EV_SND",
  "EV_REP",
  "EV_FF",
  "EV_PWR",  
  };
  int bit;
  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);
  }
  //fd=open(argv[1],O_RDWR);
  if(fd<0)
  {
  printf("open %s err\n",argv[1]);
  return -1;
  }
  err = ioctl(fd,EVIOCGID,&id);
  if(err=0)
  {
  printf("bustype = 0x%x\n",id.bustype);
  printf("vendor = 0x%x\n",id.vendor);
  printf("product = 0x%x\n",id.product);
  printf("version = 0x%x\n",id.version);
  }
len =ioctl(fd,EVIOCGBIT(0,sizeof(evbit),&evbit));
if(len > 0&&len <= sizeof(evbit))
{
  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("support ev_type:");
}
  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 err %d\n",len);
  }
  }
return 0
}


1.3.6 POLL/SELECT方式


1. 功能介绍

POLL机制、SELECT机制是完全一样的,只是APP接口函数不一样。

简单地说,它们就是“定个闹钟”:在调用poll、select函数时可以传入“超时时间”。在这段时间内,条件合适时(比如有数据可读、有空间可写)就会立刻返回,否则等到“超时时间”结束时返回错误。


用法如下。

APP先调用open函数时。

APP不是直接调用read函数,而是先调用poll或select函数,这2个函数中可以传入“超时时间”。它们的作用是:如果驱动程序中有数据,则立刻返回;否则就休眠。在休眠期间,如果有人操作了硬件,驱动程序获得数据后就会把APP唤醒,导致poll或select立刻返回;如果在“超时时间”内无人操作硬件,则时间到后poll或select函数也会返回。APP可以根据函数的返回值判断返回原因:有数据?无数据超时返回?

APP根据poll或select的返回值判断有数据之后,就调用read函数读取数据时,这时就会立刻获得数据。

poll/select函数可以监测多个文件,可以监测多种事件:

1670901855573.jpg

在调用poll函数时,要指明:

① 你要监测哪一个文件:哪一个fd

② 你想监测这个文件的哪种事件:是POLLIN、还是POLLOUT

最后,在poll函数返回时,要判断状态。


应用程序代码如下:

struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
fds[0].fd = fd;
fds[0].events = POLLIN;
ret = poll(fds, 1, timeout_ms);
if ((ret == 1) && (fds[0].revents & POLLIN))
{
  read(fd, &val, 4);
  printf("get button : 0x%x\n", val);
}


1.3.7 异步通知方式


功能介绍

所谓同步,就是“你慢我等你”。

那么异步就是:你慢那你就自己玩,我做自己的事去了,有情况再通知我。

所谓异步通知,就是APP可以忙自己的事,当驱动程序用数据时它会主动给APP发信号,这会导致APP执行信号处理函数。

仔细想想“发信号”,这只有3个字,却可以引发很多问题:

① 谁发:驱动程序发

② 发什么:信号

③ 发什么信号:SIGIO

④ 怎么发:内核里提供有函数

⑤ 发给谁:APP,APP要把自己告诉驱动

⑥ APP收到后做什么:执行信号处理函数

⑦ 信号处理函数和信号,之间怎么挂钩:APP注册信号处理函数

小孩通知妈妈的事情有很多:饿了、渴了、想找人玩。

Linux系统中也有很多信号,在Linux内核源文件include\uapi\asm-generic\signal.h中,有很多信号的宏定义:

1670901901243.jpg

驱动程序通知APP时,它会发出“SIGIO”这个信号,表示有“IO事件”要处理。

就APP而言,你想处理SIGIO信息,那么需要提供信号处理函数,并且要跟SIGIO挂钩。这可以通过一个signal函数来“给某个信号注册处理函数”,用法如下:

1670901914892.jpg

除了注册SIGIO的处理函数,APP还要做什么事?想想这几个问题:

① 内核里有那么多驱动,你想让哪一个驱动给你发SIGIO信号?

APP要打开驱动程序的设备节点。

② 驱动程序怎么知道要发信号给你而不是别人?

APP要把自己的进程ID告诉驱动程序。

③ APP有时候想收到信号,有时候又不想收到信号:

应该可以把APP的意愿告诉驱动:设置Flag里面的FASYNC位为1,使能“异步通知”。


应用编程

应用程序要做的事情有这几件:

① 编写信号处理函数:

static void sig_func(int sig)
{
  int val;
  read(fd, &val, 4);
  printf("get button : 0x%x\n", val);
}


② 注册信号处理函数:

signal(SIGIO, sig_func);

③ 打开驱动:

fd = open(argv[1], O_RDWR);

④ 把进程ID告诉驱动:

fcntl(fd, F_SETOWN, getpid());

⑤ 使能驱动的FASYNC功能:

flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);


1.4 电阻屏和电容屏


触摸屏分为电阻屏、电容屏。电阻屏结构简单,在以前很流行;电容屏支持多点触摸,现在的手机基本都是使用电容屏。

注意:LCD、触摸屏不是一回事,LCD是输出设备,触摸屏是输入设备。制作触摸屏时特意把它的尺寸做得跟LCD一模一样,并且把触摸屏覆盖在LCD上。


1.4.1 电阻屏


1. 复习一下欧姆定律

1670901976084.jpg

上图中的电阻假设是均匀的,就是长度和阻值成正比关系。电阻长度为L,阻值为R,在两端施加3.3V电压。在某点测得电压为V,求上图中长度X。

根据欧姆定律:3.3/R = V/Rx,

因为长度和阻值成正比关系,上述公式转换为:3.3∕L = V/X,所以X=LV/3.3。


2. 电阻屏原理

电阻屏就是基于欧姆定律制作的,它有上下两层薄膜,这两层薄膜就是两个电阻,如下图所示:

1670901985194.jpg

平时上下两层薄膜无触触,当点击触摸屏时,上下两层薄膜接触:这时就可以测量触点电压。过程如下:

① 测量X坐标:

在xp、xm两端施加3.3V电压,yp和ym不施加电压(yp就相当于探针),测量yp电压值。该电压值就跟X坐标成正比关系,假设:

② 测量Y坐标:

在yp、ym两端施加3.3V电压,xp和xm不施加电压(xp就相当于探针),测量xp电压值。该电压值就跟Y坐标成正比关系,假设:

1670901995971.jpg

在实际使用时,电阻屏的Xmax、Ymax无从得知,所以使用之前要先较准:依次点击触摸屏的四个角和中心点,推算出X、Y坐标的公式:

1670902007843.jpg


3. 电阻屏数据

Linux驱动程序中,会上报触点的X、Y数据,注意:这不是LCD的坐标值,需要APP再次处理才能转换为LCD坐标值。

对应的input_event结构体中,“type、code、value”如下:

按下时:
EV_KEY   BTN_TOUCH     1        /* 按下 */
EV_ABS   ABS_PRESSURE  1        /* 压力值,可以上报,也可以不报,可以是其他压力值 */
EV_ABS   ABS_X         x_value  /* X坐标 */
EV_ABS   ABS_Y         y_value  /* Y坐标 */
EV_SYNC  0             0        /* 同步事件 */
松开时:
EV_KEY   BTN_TOUCH     0        /* 松开 */
EV_ABS   ABS_PRESSURE  0        /* 压力值,可以上报,也可以不报 */
EV_SYNC  0             0        /* 同步事件 */


1.4.2 电容屏


1. 原理

原理如下图所示:

1670902038969.jpg

电容屏中有一个控制芯片,它会周期性产生驱动信号,接收电极接收到信号,并可测量电荷大小。当电容屏被按下时,相当于引入了新的电容,从而影响了接收电极接收到的电荷大小。主控芯片根据电荷大小即可计算出触点位置。

怎么通过电荷计算出触点位置?这由控制芯片实现,这类芯片一般是I2C接口。

我们只需要编写程序,通过I2C读取芯片寄存器即可得到这些数据。


2. 电容屏数据

参考文档:Linux内核Documentation\input\multi-touch-protocol.rst。

电容屏可以支持多点触摸(Multi touch),驱动程序上报的数据中怎么分辨触点?

这有两种方法:Type A、Type B,这也对应两种类型的触摸屏:

① Type A

该类型的触摸屏不能分辨是哪一个触点,它只是把所有触点的坐标一股脑地上报,由软件来分辨这些数据分别属于哪一个触点。

Type A已经过时,Linux内核中都没有Type A的源码了。


② Type B

该类型的触摸屏能分辨是哪一个触点,上报数据时会先上报触点ID,再上报它的数据。

具体例子如下,这是最简单的示例,使用场景分析来看看它上报的数据。

当有2个触点时(type, code, value):

EV_ABS   ABS_MT_SLOT 0                  // 这表示“我要上报一个触点信息了”,用来分隔触点信息
EV_ABS   ABS_MT_TRACKING_ID 45          // 这个触点的ID是45
EV_ABS   ABS_MT_POSITION_X x[0]         // 触点X坐标
EV_ABS   ABS_MT_POSITION_Y y[0]         // 触点Y坐标
EV_ABS   ABS_MT_SLOT 1                  // 这表示“我要上报一个触点信息了”,用来分隔触点信息
EV_ABS   ABS_MT_TRACKING_ID 46          // 这个触点的ID是46
EV_ABS   ABS_MT_POSITION_X x[1]         // 触点X坐标
EV_ABS   ABS_MT_POSITION_Y y[1]         // 触点Y坐标
EV_SYNC  SYN_REPORT        0            // 全部数据上报完毕


当ID为45的触点正在移动时:

EV_ABS   ABS_MT_SLOT 0   // 这表示“我要上报一个触点信息了”,之前上报过ID,就不用再上报ID了
EV_ABS   ABS_MT_POSITION_X x[0]   // 触点X坐标
EV_SYNC  SYN_REPORT         0     // 全部数据上报完毕


松开ID为45的触点时(在前面slot已经被设置为0,这里这需要再重新设置slot,slot就像一个全局变量一样:如果它没变化的话,就无需再次设置):

// 刚刚设置了ABS_MT_SLOT为0,它对应ID为45,这里设置ID为-1就表示ID为45的触点被松开了
EV_ABS   ABS_MT_TRACKING_ID -1   
EV_SYNC  SYN_REPORT         0    // 全部数据上报完毕


最后,松开ID为46的触点:

EV_ABS   ABS_MT_SLOT 1       // 这表示“我要上报一个触点信息了”,在前面设置过slot 1的ID为46
EV_ABS   ABS_MT_TRACKING_ID -1  // ID为-1,表示slot 1被松开,即ID为46的触点被松开
EV_SYNC  SYN_REPORT             // 全部数据上报完毕
相关文章
|
15天前
「Mac畅玩鸿蒙与硬件51」UI互动应用篇28 - 模拟记账应用
本篇教程将介绍如何创建一个模拟记账应用,通过账单输入、动态列表展示和实时统计功能,学习接口定义和组件间的数据交互。
134 68
|
17天前
|
存储 人工智能 JavaScript
Harmony OS开发-ArkTS语言速成二
本文介绍了ArkTS基础语法,包括三种基本数据类型(string、number、boolean)和变量的使用。重点讲解了let、const和var的区别,涵盖作用域、变量提升、重新赋值及初始化等方面。期待与你共同进步!
80 47
Harmony OS开发-ArkTS语言速成二
|
14天前
|
UED
「Mac畅玩鸿蒙与硬件52」UI互动应用篇29 - 模拟火车票查询系统
本篇教程将实现一个模拟火车票查询系统,通过输入条件筛选车次信息,并展示动态筛选结果,学习事件处理、状态管理和界面展示的综合开发技巧。
51 13
|
13天前
「Mac畅玩鸿蒙与硬件53」UI互动应用篇30 - 打卡提醒小应用
本篇教程将实现一个打卡提醒小应用,通过用户输入时间进行提醒设置,并展示实时提醒状态,实现提醒设置和取消等功能。
46 10
|
9天前
|
存储 JSON 区块链
【HarmonyOS NEXT开发——ArkTS语言】购物商城的实现【合集】
HarmonyOS应用开发使用@Component装饰器将Home结构体标记为一个组件,意味着它可以在界面构建中被当作一个独立的UI单元来使用,并且按照其内部定义的build方法来渲染具体的界面内容。txt:string定义了一个名为Data的接口,用于规范表示产品数据的结构。src:类型为,推测是用于引用资源(可能是图片资源等)的一种特定类型,用于指定产品对应的图片资源。txt:字符串类型,用于存放产品的文字描述,比如产品名称等相关信息。price:数值类型,用于表示产品的价格信息。
34 5
|
9天前
|
开发工具 开发者 容器
【HarmonyOS NEXT开发——ArkTS语言】欢迎界面(启动加载页)的实现【合集】
从ArkTS代码架构层面而言,@Entry指明入口、@Component助力复用、@Preview便于预览,只是初窥门径,为开发流程带来些许便利。尤其动画回调与Blank组件,细节粗糙,后续定当潜心钻研,力求精进。”,字体颜色为白色,字体大小等设置与之前类似,不过动画配置有所不同,时长为。,不过这里没有看到额外的动画效果添加到这个特定的图片元素上(与前面带动画的元素对比而言)。这是一个显示文本的视图,文本内容为“奇怪的知识”,设置了字体颜色为灰色(的结构体,它代表了整个界面组件的逻辑和视图结构。
29 1
|
自然语言处理 JavaScript 前端开发
一文了解HarmonyOS系统架构
HarmonyOS是一款面向 万物互联时代的、全新分布式操作系统。在传统的单设备系统能力基础上,HarmonyOS提出了基于`同一套系统能力`、`适配多种终端形态`的分布式理念。能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备,提供全场景(移动办公、运动健康、社交通信、媒体娱乐等)业务能力。
2434 0
一文了解HarmonyOS系统架构
|
19天前
|
API 索引
鸿蒙开发:实现一个超简单的网格拖拽
实现拖拽,最重要的三个方法就是,打开编辑状态editMode,实现onItemDragStart和onItemDrop,设置拖拽移动动画和交换数据,如果想到开启补位动画,还需要实现supportAnimation方法。
75 13
鸿蒙开发:实现一个超简单的网格拖拽
|
18天前
|
索引 API
鸿蒙开发:自定义一个股票代码选择键盘
金融类的软件,特别是股票基金类的应用,在查找股票的时候,都会有一个区别于正常键盘的键盘,也就是股票代码键盘,和普通键盘的区别就是,除了常见的数字之外,也有一些常见的股票代码前缀按钮,方便在查找股票的时候,更加方便的进行检索。
鸿蒙开发:自定义一个股票代码选择键盘
|
18天前
|
API
鸿蒙开发:自定义一个英文键盘
实现方式呢,有很多种,目前采用了比较简单的一种,如果大家采用网格Grid组件实现方式,也是可以的,但是需要考虑每行的边距以及数据,还有最后两行的格子占位问题。
鸿蒙开发:自定义一个英文键盘