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


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

ABS:绝对值

 

 

1.input子系统简介

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

\

图1


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

所有的inputdevice在注册后会加入一个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设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。

 

2.1 input device的注册

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

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

一个完整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;
};

 

相关内容