歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

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

 

Copyright © Linux教程網 All Rights Reserved