msg2133触摸屏(TP源代码学习)

强调:下面的设备指触摸屏

ABS:绝对值

1.     input子系统简介

Linux输入设备总类繁杂,常见的包括有按键、键盘、触摸屏、鼠标、摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简化驱动开发建立了一个input子系统。子系统共分为三层,如图1所示。

图1

驱动层和硬件相关,直接捕捉和获取硬件设备的数据信息等(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),然后将数据信息报告到核心层。核心层负责连接驱动层和事件处理层,设备驱动(device driver)和处理程序(handler)的注册需要通过核心层来完成,核心层接收来自驱动层的数据信息,并将数据信息选择对应的handler去处理,最终handler将数据复制到用户空间。

所有的input device在注册后会加入一个input_dev_list(输入设备链表),所有的eventhandler在注册后会加入一个input_handler_list(输入处理程序链表),这里的list_head主要的作用是作为input_dev_list和input_handler_list的一个节点来保存地址。Input_dev_list和input_handler_list之间的对应关系由input_handle结构体桥接

input_handle是用来关联input_dev和input_handler。为什么用input_handle来关联input_dev和input_handler而不将input_dev和input_handler直接对应呢?因为一个device可以对应多个handler,而一个handler也可处理多个device。就如一个触摸屏设备可以对应event handler也可以对应tseve handler。

input_dev、input_handler、input_handle的关系如下图2所示。

图2

2.     触摸屏驱动简介

流程

在Linux中,Input设备用input_dev结构体描述,定义在input.h中。设备的驱动只需按照如下步骤就可实现了。

1).在驱动模块加载函数中设置Input设备支持input子系统的哪些事件;

2).将Input设备注册到input子系统中;

3).在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。

参考:

ARM Linux内核Input输入子系统浅解

http://www.51hei.com/bbs/dpj-27652-1.html

2.1  input device的注册

Input device的注册实际上仅仅只有几行代码,因为在input.c中已经将大量的代码封装好了,主需要调用几个关键的函数就能完成对input device的注册。

在xxx_ts.c中预先定义全局变量structinput_dev  tsdev;然后进入到初始化函数

一个完整input设备系统不仅要有设备,还需要有处理程序input_handler

2.2  input handler的注册

Input_handler是要和用户层打交道的,在evdev.c中直接定义了一个input_handler结构体并初始化了一些内部成员变量。

2.3  数据传递过程

从硬件设备(触摸屏)中获得的数据需要经过input.c选择相应的handler进行处理,最后上报到用户空间。如何从硬件设备(触摸屏)中获得数据,那就得看xxx_ts.c中的代码了,在xxx_ts.c中的源代码是直接和硬件设备相关的。在xxx_ts.c中我们一方面要完成触摸屏设备相关的寄存器配置,另一方面要完成将获得的数据上报。

至于设备初始化配置方面,根据每个人使用的ARM芯片的Datasheet去初始化配置寄存器,这里也不需要多说了。不管是通过查询法还是中断法(我没见过用查询的),当触摸屏按下时候我们会得到触摸屏被按下的相关数据(主要是被按下的X和Y坐标值),然后需要将数据信息上报,在触摸屏被按下的时候需要上报:

input_report_key(tsdev, BTN_TOUCH, 1);      //报告按键被按下事件

input_report_abs(tsdev, ABS_X, x);            //报告触摸屏被按下的x坐标值

input_report_abs(tsdev, ABS_Y, y);      //报告触摸屏被按下的y坐标值

input_report_abs(tsdev, ABS_PRESSURE, 1);//报告触摸屏被按下的压力值(0或者1)

input_sync(tsdev);            //报告同步事件,表示一次事件结束

当触笔从触摸屏上抬起时需要上报:

input_report_key(tsdev, BTN_TOUCH, 0);      //报告按键被松开事件

input_report_abs(tsdev, ABS_PRESSURE, 0);//报告触摸屏被按下的压力值(0或者1)

input_sync(tsdev);            //报告同步事件,表示一次事件结束

2.4  数据读取过程

读取就变得很简单了,做过linux编程的都能知道,只要在应用中定义了个input_event结构体,通过open打开设备,然后进行read即可。

2.5

3.     Msg2133A驱动代码学习

3.1  touch_driver_probe()

所涉及的文件及一些主要函数关系如下:

图3

(1)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrInputDeviceInitialize()

//为一个新的输入设备分配内容,返回一个预先准备好的结构体input_dev,并让//g_InputDevice指向它。

/* allocate an input device */
   g_InputDevice = input_allocate_device();
   if (g_InputDevice == NULL)
    {
       DBG("*** input device allocation failed ***\n");
       return -ENOMEM;
    }

//phys:系统层次结构中触摸屏设备的物理路径,这里的触摸屏设备是挂载在//I2C总线上的;id:设备的id,对应结构体input_id,设备采用的总线是I2C。
g_InputDevice->name= pClient->name;
   g_InputDevice->phys = "I2C";
g_InputDevice->dev.parent= &pClient->dev;
g_InputDevice->id.bustype= BUS_I2C;

//设置设备支持的事件类型,evbit表示设备支持的事件类型,这里支持
EV_ABS:绝对坐标事件,用于摇杆
EV_SYN:同步事件
EV_KEY:键盘事件
Keybit表示设备支持的按键类型,BTN_TOUCH表示触摸按键;propbit表示设备属性和兼容(quirks),INPUT_PROP_DIRECT表示直接的输入设备
   /* set the supported event type for input device */
   set_bit(EV_ABS, g_InputDevice->evbit);
   set_bit(EV_SYN, g_InputDevice->evbit);
   set_bit(EV_KEY, g_InputDevice->evbit);
   set_bit(BTN_TOUCH, g_InputDevice->keybit);
   set_bit(INPUT_PROP_DIRECT, g_InputDevice->propbit);

// when touch panel support virtual key(EX.Menu, Home, Back, Search)定义此宏,input_set_capability()作用是标记设备有能力处理按键事件(EV_KEY),可处理的事件code由g_TpVirtualKey[i]指定,这里支持menu、home、back和search。
#ifdef CONFIG_TP_HAVE_KEY
   // Method 1.
    {
       u32 i;
       for (i = 0; i < MAX_KEY_NUM; i ++)
       {
           input_set_capability(g_InputDevice, EV_KEY, g_TpVirtualKey[i]);
       }
    }
#endif

// ABS_MT_TOUCH_MAJOR表示描述了主接触面的长轴

ABS_MT_WIDTH_MAJOR表示了接触工具的长轴,比如

ABS_MT_TOUCH_MAJOR/ABS_MT_WIDTH_MAJOR,永远小于1,并且和压力有关,压力越大,越接近1。

ABS_MT_POSITION_X接触位置的中心点X坐标

ABS_MT_POSITION_Y接触位置的中心点Y坐标

input_set_abs_params(g_InputDevice,ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(g_InputDevice,ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
//对于X轴范围是TOUCH_SCREEN_X_MIN到TOUCH_SCREEN_X_MIN,数据误差是-0到+0,中心平滑位置是0。
input_set_abs_params(g_InputDevice,ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, TOUCH_SCREEN_X_MAX, 0, 0);
input_set_abs_params(g_InputDevice,ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, TOUCH_SCREEN_Y_MAX, 0, 0);

//向input子系统注册输入设备
   /* register the input device to input sub-system */
   nRetVal = input_register_device(g_InputDevice);
   if (nRetVal < 0)
    {
       DBG("*** Unable to register touch input device ***\n");
    }

(2)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrTouchDeviceRequestGPIO()

此函数就是申请复位和中断引脚的GPIO。

#define MS_TS_MSG_IC_GPIO_RST   12
#define MS_TS_MSG_IC_GPIO_INT   13
#define MSM_TS_MSG_IC_GPIO_RST  (MS_TS_MSG_IC_GPIO_RST+911)
#define MSM_TS_MSG_IC_GPIO_INT  (MS_TS_MSG_IC_GPIO_INT+911)
//向申请MSM_TS_MSG_IC_GPIO_RST这个GPIO,其名字为为C_TP_RST
nRetVal =gpio_request(MSM_TS_MSG_IC_GPIO_RST, "C_TP_RST");
   if (nRetVal < 0)
    {
       DBG("*** Failed to request GPIO %d, error %d ***\n",MSM_TS_MSG_IC_GPIO_RST, nRetVal);
    }

   nRetVal = gpio_request(MSM_TS_MSG_IC_GPIO_INT,"C_TP_INT");
   if (nRetVal < 0)
    {
       DBG("*** Failed to request GPIO %d, error %d ***\n",MSM_TS_MSG_IC_GPIO_INT, nRetVal);
    }

(3)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrTouchDevicePowerOn()

// 复位触摸屏IC,gpio_direction_output()在某个GPIO口写上某个值之后,还会把这个端口设置为输出模式。

gpio_direction_output(MSM_TS_MSG_IC_GPIO_RST,1);
udelay(100);
gpio_set_value(MSM_TS_MSG_IC_GPIO_RST, 0);
udelay(100);
gpio_set_value(MSM_TS_MSG_IC_GPIO_RST, 1);
mdelay(25);

(4)  mstar_drv_main.c :DrvMainTouchDeviceInitialize()

主要是创建procfs文件系统目录入口和创建/kernel/kset_example/kobject_example目录

图4

(5)  mstar_drv_ic_fw_porting_layer.c:DrvIcFwLyrGetChipType()

通过调用mstar_drv_self_fw_control.c:DrvFwCtrlGetChipType()获取触摸屏IC类型,msg2133A对应的为2

(6)  mstar_drv_self_fw_control.c:DrvFwCtrlGetChipType()

获取触摸屏IC芯片类型,比如#defineCHIP_TYPE_MSG21XXA  (0x02)

(7)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrTouchDeviceResetHw()

通过控制复位引脚复位hw,和DrvPlatformLyrTouchDevicePowerOn()实现一样。

(8)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrTouchDeviceRegisterFingerTouchInterruptHandler()

//初始化手指触摸工作队列,并将此工作队列和处理函数_DrvPlatformLyrFingerTouchDoWork()绑定。
/* initialize the finger touch work queue*/
INIT_WORK(&_gFingerTouchWork,_DrvPlatformLyrFingerTouchDoWork);

//返回中断标号给_gIrq。
_gIrq =gpio_to_irq(MSM_TS_MSG_IC_GPIO_INT);

//申请一个IRQ和注册对应的ISR,当IRQ发生的时候,会调用ISR(这里为_DrvPlatformLyrFingerTouchInterruptHandler())
 /*request an irq and register the isr */
nRetVal =request_threaded_irq(_gIrq/*MS_TS_MSG_IC_GPIO_INT*/, NULL,_DrvPlatformLyrFingerTouchInterruptHandler,IRQF_TRIGGER_RISING | IRQF_ONESHOT/*| IRQF_NO_SUSPEND *//* IRQF_TRIGGER_FALLING */,
                      "msg2xxx",NULL);
_gInterruptFlag = 1;

(9)  mstar_drv_platform_porting_layer.c:DrvPlatformLyrTouchDeviceRegisterEarlySuspend()

//注册通知器,什么时候调用呢?

_gFbNotifier.notifier_call = MsDrvInterfaceTouchDeviceFbNotifierCallback;
fb_register_client(&_gFbNotifier);

(10)

3.2  手指触摸触摸屏的处理过程

从上面可知当触摸TP时,对应的中断会产生,然后调用_DrvPlatformLyrFingerTouchInterruptHandler()。

图5

(1)  _DrvPlatformLyrFingerTouchInterruptHandler()

spin_lock_irqsave(&_gIrqLock,nIrqFlag);

    if(_gInterruptFlag == 1)
{
      //disable_irq_nosync()关闭中断,不等待中断处理完成
       disable_irq_nosync(_gIrq);
       _gInterruptFlag = 0;
       schedule_work(&_gFingerTouchWork);
    }

spin_unlock_irqrestore(&_gIrqLock,nIrqFlag);

初始化后_gInterruptFlag就是1,schedule_work(&_gFingerTouchWork);这里会调用_DrvPlatformLyrFingerTouchDoWork()

(2)  _DrvPlatformLyrFingerTouchDoWork()

主要通过调用DrvIcFwLyrHandleFingerTouch来处理触摸动作。

DrvIcFwLyrHandleFingerTouch(NULL, 0);
spin_lock_irqsave(&_gIrqLock,nIrqFlag);
if (_gInterruptFlag == 0)
{
      //使能一个IRQ的处理,便于响应下个触摸动作。
       enable_irq(_gIrq);
       _gInterruptFlag = 1;
}
spin_unlock_irqrestore(&_gIrqLock,nIrqFlag);

nReportPacketLength =DEMO_MODE_PACKET_LENGTH;
pPacket = g_DemoModePacket;

rc = IicReadData(SLAVE_I2C_ID_DWI2C,&pPacket[0], nReportPacketLength);
   if (rc < 0)
    {
       DBG("I2C read packet data failed, rc = %d\n", rc);
       goto TouchHandleEnd;
    }

(3)  DrvIcFwLyrHandleFingerTouch()

调用DrvFwCtrlHandleFingerTouch()来处理

(4)  DrvFwCtrlHandleFingerTouch()

DrvFwCtrlHandleFingerTouch

通过调用_DrvFwCtrlParsePacket()来解析数据,然后上报数据。

(5)  _DrvFwCtrlParsePacket()

通过I2C读取到msg2133a发送回来的8个字节数据包,这8个字节数据的意义

/*

pPacket[0]:id, pPacket[1]~pPacket[3]:thefirst point abs, pPacket[4]~pPacket[6]:the relative distance between the firstpoint abs and the second point abs

when pPacket[1]~pPacket[4], pPacket[6] is0xFF, keyevent, pPacket[5] to judge which key press.

pPacket[1]~pPacket[6] all are 0xFF, releasetouch

*/

对应msg2133A来说,pPacket[0]=0x52,pPacket[1]~pPacket[3]是ADC采样值,需要转换为x和y坐标值,转换的公式为x=(x对应的AD采样值*480)/2048,y的类似。

(6)  DrvPlatformLyrFingerTouchPressed()

上报事件

input_report_key(g_InputDevice, BTN_TOUCH,1);
input_report_abs(g_InputDevice, ABS_MT_TOUCH_MAJOR,1);
input_report_abs(g_InputDevice,ABS_MT_WIDTH_MAJOR, 1);
input_report_abs(g_InputDevice,ABS_MT_POSITION_X, nX);
input_report_abs(g_InputDevice,ABS_MT_POSITION_Y, nY);
input_mt_sync(g_InputDevice);

(7)

3.3  虚拟按键实现

when pPacket[1]~pPacket[4], pPacket[6] is0xFF, keyevent, pPacket[5] to judge which key press.根据我们触摸屏丝印按键有menu、home、back和search,按下这几个位置pPacket[5]的值分别为4、8、1、2,然后在DrvFwCtrlHandleFingerTouch函数中做对应的处理即可:

const int g_TpVirtualKey[] = {TOUCH_KEY_MENU,TOUCH_KEY_HOME, TOUCH_KEY_BACK, TOUCH_KEY_SEARCH};
if (tInfo.nTouchKeyCode != 0)
           {
#ifdef CONFIG_TP_HAVE_KEY
                if (tInfo.nTouchKeyCode == 4)// TOUCH_KEY_MENU
                {
                    nTouchKeyCode=g_TpVirtualKey[0];
                }
                else if (tInfo.nTouchKeyCode ==1) // TOUCH_KEY_BACK
                {
                    nTouchKeyCode =g_TpVirtualKey[2];
                }
                else if (tInfo.nTouchKeyCode ==2) //TOUCH_KEY_SEARCH
                {
                    nTouchKeyCode =g_TpVirtualKey[3];
                }
                else if (tInfo.nTouchKeyCode ==8) //TOUCH_KEY_HOME
                {
                    nTouchKeyCode =g_TpVirtualKey[1];
                }

                if (nLastKeyCode !=nTouchKeyCode)
               {
                    DBG("key touchpressed\n");
                    DBG("nTouchKeyCode =%d, nLastKeyCode = %d\n", nTouchKeyCode, nLastKeyCode);

                    nLastKeyCode =nTouchKeyCode;

                    input_report_key(g_InputDevice,BTN_TOUCH, 1);
                   input_report_key(g_InputDevice, nTouchKeyCode, 1);

                    input_sync(g_InputDevice);
                }
#endif //CONFIG_TP_HAVE_KEY   

3.4

4.     调试方法

4.1  调试串口

4.2  Adb

(1)  getevent,按键触摸屏

图6

(2)  busybox hexdump /dev/input/event2

图7

对应下面的代码

struct timeval {
       __kernel_time_t        tv_sec;          /*seconds */
       __kernel_suseconds_t       tv_usec; /*microseconds */
};
/*
 *The event structure itself
 */

struct input_event {
       structtimeval time;
       __u16type;
       __u16code;
       __s32value;
};
时间: 2024-10-14 17:55:21

msg2133触摸屏(TP源代码学习)的相关文章

nginx源代码学习资源(不断更新)

nginx源代码学习是一个痛苦又快乐的过程,以下列出了一些nginx的学习资源. 首先要做的当然是下载一份nginx源代码,能够从nginx官方站点下载一份最新的. 看了nginx源代码,发现这是一份全然没有凝视,全然没有配置文档的代码. 如今你最希望要的是一份凝视版的nginx源代码,能够从以下的链接中下载一份: https://github.com/jianfengye/nginx-1.0.14_comment 这份凝视版源代码会不断进行更新的 好了,第一个问题, nginx的main函数在

tablib源代码学习

tablib简介 ----------- Tablib is a format-agnostic tabular dataset library, written in Python. Tablib 是一个格式未知的表格操作库,使用python编写,目前(2014-06-11)支持如下格式:Excel .JSON .YAML .HTML.TSV .CSV的导入/导出,及修改操作.实现方法是使用各种数据格式的python支持库(大多是各种格式的有明支持库)导入数据成list(列表,python 内

struts2源代码学习之初始化(一)

看struts2源代码已有一段时日,从今天開始,就做一个总结吧. 首先,先看看怎么调试struts2源代码吧,主要是下面步骤: 使用Myeclipse创建一个webproject 导入struts2须要的jar包 如图: 让jar包关联源文件 在上图中的jar包右键,选择properties->java source attach,假设关联成功,双击jar包下的某个class文件就会显示java源码了. 双击.class文件,在源码关键地方设置断点 部署project到Tomcat Tomcat

[Java] LinkedList / Queue - 源代码学习笔记

简单地画了下 LinkedList 的继承关系,如下图.只是画了关注的部分,并不是完整的关系图.本博文涉及的是 Queue, Deque, LinkedList 的源代码阅读笔记.关于 List 接口的笔记,可以参考上一篇博文 List / ArrayList - 源代码学习笔记 Queue 1. 继承 Collection 接口,并提供了额外的插入.提取和查看元素的方法.新增的方法都有两种形式:当操作失败时,抛出异常或者返回一个特殊值.特殊值可以是 null 或者 false ,这取决于方法本

JDK源代码学习系列04----ArrayList

                                                                         JDK源代码学习系列04----ArrayList 1.ArrayList简单介绍 ArrayList是基于Object[] 数组的,也就是我们常说的动态数组.它能非常方便的实现数组的添加删除等操作. public class ArrayList<E> extends AbstractList<E> implements List<

igmpproxy源代码学习——igmpProxyInit()

igmpproxy源代码学习--igmpProxyInit()函数详解,igmpproxy初始化 在运行igmpproxy的主程序igmpproxyRun()之前需要对igmpproxy进行一些配置,这些配置都是在igmpProxyInit()中完成的. 要进行的配置主要有: 信号处理配置 物理网络接口配置加载 配置文件的加载 虚拟网络设备初始化 路由向量表初始化 定时器初始化 信号处理配置 首先进行信号处理配置: sigemptyset(&sa.sa_mask); sigaction(SIGT

lucene源代码学习之LZ4压缩算法在lucene中应用

LZ4算法又称为Realtime Compression Algorithm,在操作系统(linux/freeBSD).文件系统(OpenZFS).大数据(Hadoop).搜索引擎(Lucene/solr).数据库(Hbase)--都可以看到它的身影,可以说是一个非常通用的算法.LZ4最突出的地方在于它的压缩/解压速度. 基础知识 理解Lucene中LZ4算法的实现,需要有以下两点基础知识: 1. 理解Lucene里面的packedInts. 关于PacedInts,可以参考http://sbp

PetaPoco源代码学习--0.目录贴

2017年3季度后,以人力外包的形式派驻到甲方单位进行项目救急时,接触到了甲方单位的ASP.NET MVC项目的ORM框架,它以PetaPoco(2012年的老版本)进行改造升级的,当初就想学习一下这个小型的ORM框架,但是一直没有机会切入,目前,项目不是太紧张,想起此框架,因此,决定以此为目标,对该框架进行深入学习,希望能够坚持下来,学习框架里的编程思想来提高自己. 将该贴作为系列博文的索引页,同时也算是对自己的一个监督. 注:该源代码学习计划可能比较杂乱,没有系统性,关键是坚持,并理解其编程

jQuery源代码学习之六——jQuery数据缓存Data

一.jQuery数据缓存基本原理 jQuery数据缓存就两个全局Data对象,data_user以及data_priv; 这两个对象分别用于缓存用户自定义数据和内部数据: 以data_user为例,所有用户自定义数据都被保存在这个对象的cache属性下,cache在此姑且称之为自定义数据缓存: 自定义数据缓存和DOM元素/javascript对象通过id建立关联,id的查找通过DOM元素/javascript元素下挂载的expando属性获得 话不多说,直接上代码.相关思路在代码注释中都有讲解