原理概述ios
1.首先要區分lcd和觸摸屏,lcd是一個屏幕,觸摸屏是貼在lcd上的兩層膜。框架
2.四線電阻觸摸屏的原理:觸摸屏就是上下兩層膜,好比上層表明x軸(XM:負端,XP:正端),下層表明y軸(YM:負端,YP:正端)。當讀取x軸座標時,XP接3.3v,XM接地,從YM讀取按下點的電壓值做爲模擬輸入信號,再通過AD轉換後就獲得了x軸座標。同理,當讀取y軸座標時,YP接3.3v,YM接地,從XM讀取按下點的電壓值做爲模擬輸入,再通過AD轉換後就獲得了y軸座標。函數
觸摸屏驅動程序採用中斷方式對觸摸筆的按下狀態進行檢測,若是檢測到觸摸筆按下將產生中斷並觸發一個事件通知一個工做線程開始採集數據。測試
ADC觸摸屏驅動的工做流程如圖 21所示。ui
圖 21 ADC觸摸屏工做流程線程
技術實現事件
ADC觸摸屏驅動框架分析工作流
ADC觸摸屏驅動目錄如圖 31所示。it
圖 31 ADC觸摸屏驅動所在目錄io
bspnuc970/driver_module/touch_drv/src/touch_drv.c文件主要提供SylixOS的ADC觸摸屏驅動。主要關注函數如程序清單 31所示。
程序清單 31
INT __tsDrv (VOID) INT __tsDevCreate (PCHAR pcName) static void __tsThread (TS_DEV *ptsDev) static irqreturn_t __tsIsr (void *arg)
框架的流程圖
整個框架的流程如圖 33所示。
圖 33 ADC觸摸屏驅動框架
代碼實現
BSP中驅動配置
根據NUC970相關芯片手冊,配置寄存器地址並定義touch screen設備結構。如程序清單 32所示。
程序清單 32
/********************************************************************************************************* touch screen 設備結構 *********************************************************************************************************/ typedef struct { LW_DEV_HDR TS_devhdr; /* 設備頭 */ touchscreen_event_notify TS_tData; /* 採集到的數據 */ BOOL TS_bIsReadRel; /* 是否讀取的 release 操做 */ LW_HANDLE TS_hThread; /* 掃描線程 */ LW_SEL_WAKEUPLIST TS_selwulList; /* select() 等待鏈 */ LW_SPINLOCK_DEFINE (TS_slLock); /* 自旋鎖 */ } TS_DEV;
__tsDrv
該函數註冊一系列觸摸屏設備驅動程序,包括:
1) 驅動程序中的創建函數__tsOpen;
2) 驅動程序中的關閉函數__tsClose;
3) 驅動程序中的讀函數__tsRead;
具體實現如程序清單 33所示。
程序清單 33
INT __tsDrv (VOID) { …… /* * 安裝觸摸屏驅動,LW_NULL爲待實現功能 */ touch_dev_num = iosDrvInstall(__tsOpen, LW_NULL, __tsOpen, __tsClose, __tsRead, LW_NULL, LW_NULL); …… }
__tsDevCreate
該函數建立一個觸摸屏設備,並將__tsDrv中註冊的驅動程序與設備名綁定。建立成功後對該設備的open,close,read操做將調用__tsDrv中註冊的驅動程序__tsOpen,__tsClose,__tsRead。具體實現如程序清單 34所示。
程序清單 34
INT __tsDevCreate (PCHAR pcName) { …… iTemp = (INT)iosDevAdd(&ptsDev->TS_devhdr, pcName, touch_dev_num); …… }
__tsThread
該函數是在__tsOpen中建立的觸摸屏服務線程,其在執行過程當中等待__tsIsr中斷函數發送二進制信號量,收到信號後執行後續操做。具體實現如程序清單 35所示。
程序清單 35
static void __tsThread (TS_DEV *ptsDev) { …… /* * 等待INT_TC中斷,進入中斷服務函數,發送二進制信號量 */ API_SemaphoreBPend(adc_mst, LW_OPTION_WAIT_INFINITE); …… /* * Menu Start Conversion * (開始轉換) */ uiRegVal = readl(REG_ADC_IER) | ADC_IER_MIEN; writel(uiRegVal, REG_ADC_IER); uiRegVal = readl(REG_ADC_CTL) | ADC_CTL_MST; writel(uiRegVal, REG_ADC_CTL); if (__tsGetXY(&ix, &iy)) { /* * 當前有點擊操做. */ LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel); ptsDev->TS_tData.kstat |= MOUSE_LEFT; ptsDev->TS_tData.xmovement = ix; ptsDev->TS_tData.ymovement = iy; LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel); SEL_WAKE_UP_ALL(&ptsDev->TS_selwulList, SELREAD); /* 釋放全部等待讀的線程 */ } else { /* * 當前沒有點擊操做. */ if (ptsDev->TS_tData.kstat & MOUSE_LEFT) { LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel); ptsDev->TS_tData.kstat &= (~MOUSE_LEFT); LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel); } if (ptsDev->TS_bIsReadRel == LW_FALSE) { /* 沒有讀取到釋放操做 */ SEL_WAKE_UP_ALL(&ptsDev->TS_selwulList, SELREAD); /* 釋放全部等待讀的線程 */ } } } }
static INT __tsGetXY (INT *pX, INT *pY) { /* * 等待INT_ADC中斷,進入中斷服務函數,發送二進制信號量adc_sem */ API_SemaphoreBPend(adc_sem, LW_OPTION_WAIT_INFINITE); *pX = abs_xp; *pY = abs_yp; return send_press; }
__tsIsr
該函數是由__tsOpen註冊的中斷服務函數,在中斷服務函數中進行判斷,判斷不一樣中斷信號進行不一樣的中斷處理,並給__tsThread發送二進制信號量。具體實現如程序清單 36所示。
程序清單 36
static irqreturn_t __tsIsr (void *arg) { …… /* * 檢測觸摸轉換是否完成 */ if(uisr & ADC_ISR_TF) { writel(ADC_ISR_TF, REG_ADC_ISR); /* 清除標誌位 */ /* * 若轉換完成,賦值並使touchGetXY返回1 * 不然清零並使touchGetXY返回0 */ uiRegVal = readl(REG_ADC_XYDATA); abs_xp = (uiRegVal >> ADC_XYDATA_XBIT) & 0xFFF; abs_yp = (uiRegVal >> ADC_XYDATA_YBIT) & 0xFFF; send_press = TF_ON; printk("%d abs_xp = %d, abs_yp = %d \n", __LINE__, abs_xp, abs_yp); API_SemaphoreBPost(adc_sem); /* 釋放一個信號給等待任務 */ } else { abs_xp = XDATA_CLR; abs_yp = YDATA_CLR; send_press = TF_OFF; API_SemaphoreBPost(adc_sem); } /* * 檢測是否觸摸按下 / 鬆開 */ if((uisr & ADC_ISR_PEUEF) && (uier & ADC_IER_PEUEIEN)) { uiRegVal = ADC_ISR_PEUEF | ADC_ISR_PEDEF; writel(uiRegVal, REG_ADC_ISR); } else if((uisr & ADC_ISR_PEDEF) && (uisr & ADC_IER_PEDEIEN)) { uiRegVal = readl(REG_ADC_ISR) | ADC_ISR_PEUEF | ADC_ISR_PEDEF; writel(uiRegVal, REG_ADC_ISR); API_SemaphoreBPost(adc_mst); } /* * 檢測全部轉換是否完成 */ if(uisr & ADC_ISR_MF) { uiRegVal = readl(REG_ADC_ISR) | ADC_ISR_MF; writel(uiRegVal, REG_ADC_ISR); } return (LW_IRQ_HANDLED); }
__tsOpen
Open函數調用Init函數初始化並建立觸摸屏服務線程。具體實現如程序清單 37所示。
程序清單 37
static LONG __tsOpen (TS_DEV *ptsDev, PCHAR pcName, INT iFlags, INT iMode) { …… __tsInit(); …… /* * 建立觸摸屏服務線程 */ ptsDev->TS_hThread = API_ThreadCreate("t_touch", (PTHREAD_START_ROUTINE)__tsThread, &threadattr, LW_NULL); …… }
__tsInit
Init函數中建立二進制信號量,註冊中斷服務函數。具體實現如程序清單 38所示。
程序清單 38
static void __tsInit (void) { …… API_SemaphoreBCreate("ad_thread_sem", 0, LW_OPTION_WAIT_FIFO, &adc_sem); API_SemaphoreBCreate("ad_thread_mst", 0, LW_OPTION_WAIT_FIFO, &adc_mst); …… /* * 安裝ADC 中斷服務程序, */ API_InterVectorConnect(VIC_CHANNEL_ADC, (PINT_SVR_ROUTINE)__tsIsr, (PVOID)NULL, "touchscr"); API_InterVectorEnable(VIC_CHANNEL_ADC); …… }
__tsClose
Close函數用於刪除以前建立的信號量和線程。具體實現如程序清單 39所示。
程序清單 39
static INT __tsClose (TS_DEV *ptsDev) { …… /* * Disable Pen Down Event */ writel(CLEAR_ALL, REG_ADC_IER); API_ThreadForceDelete(&ptsDev->TS_hThread, LW_NULL); API_SemaphoreBDelete(&adc_sem); API_SemaphoreBDelete(&adc_mst); …… }
__tsRead
Read函數獲得ADC值並上報。具體實現如程序清單 310所示。
程序清單 310
static ssize_t __tsRead (TS_DEV *ptsDev, touchscreen_event_notify *pnotify, size_t stNbyte) { INTREG iregInterLevel; if (stNbyte == 0) { return (ERROR_NONE); } LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel); pnotify->ctype = ptsDev->TS_tData.ctype; pnotify->kstat = ptsDev->TS_tData.kstat; pnotify->xanalog = ptsDev->TS_tData.xanalog; pnotify->yanalog = ptsDev->TS_tData.yanalog; printk("%d ix = %d iy = %d\n", __LINE__, ptsDev->TS_tData.xanalog, ptsDev->TS_tData.yanalog); if (ptsDev->TS_tData.kstat & MOUSE_LEFT) { /* 讀取到點擊事件 */ ptsDev->TS_bIsReadRel = LW_FALSE; /* 須要確保應用讀到釋放操做 */ } else { ptsDev->TS_bIsReadRel = LW_TRUE; /* 已經讀取到釋放操做 */ } LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel); return (sizeof(touchscreen_event_notify)); }
內核模塊加載
當ADC觸摸屏驅動完成後,編譯生成touch_drv.ko文件,經過tftp服務下載至/lib/modules/路徑下,經過執行modulereg touch_drv.ko命令動態加載至內核,如圖 34所示:
圖 34 modulereg touch_drv.ko動態加載
加載成功後,經過執行modules命令能夠查看當前已加載.ko模塊,如圖 35所示:
圖 35查看當前已加載.ko模塊測試
在正常加載的狀況下,啓動任意一個QT程序,進行觸控操做,若是正確響應觸控事件,說明基本完成移植。