周德榮
觸摸屏作為一種輸入設備,具有堅固耐用、反應速度快、節省空間、易于交流等優點,提供簡單、方便、自然的人機交互方式,目前被廣泛應用于工業控制、電子查詢、消費性電產品領域。Linux作為是目前最流行的操作系統之一,在桌面系統、服務器領域有大量用戶,具有源代碼開放,支持的硬件豐富、高可移植等優點,在嵌入式領域也備受青睞。Linux根據不同設備,將驅動程序分為字符設備驅動、塊設備驅動、網絡設備驅動三種, Linux輸入子系統[1]是對字符類型輸入設備驅動實現方式的抽象,是對分散的、多種不同類別的輸入設備進行統一處理的內核驅動模型。輸入子系統具高效、無Bug和可重用等優點。本文對基于Linux輸入子系統的觸摸屏驅動進行深入的討論。
S3C2440是三星公司推出的采用ARM920t內核的MCU,集成了豐富的外圍設備,其中包括4線電阻式觸摸屏控制器和8通道多路復用ADC。觸摸屏由觸摸檢測部件和觸摸屏控制器構成,對應S3C2440平臺的四線電阻觸摸屏的外接電路和S3C2440芯片自帶的A/D 轉換控制部分。四線電阻觸摸屏的外接電路控制上下兩層導電層的通斷情況以及如何取電壓,取電壓之后由S3C2440芯片中的A/D將模擬量轉換成數字量。S3C2440芯片的A/D轉換器有8個輸入通道,轉換結果為10bit數字,轉換過程在芯片內部自動實現,轉換的結果從寄存器中取值,再進行一定的轉后可直接得到觸摸點的坐標。S3C2440提供的ADC和觸摸屏接口如圖1所示,觸摸屏直接與引腳XP,XM,YP和YM連接,對觸摸屏兩個導電層的通斷通過XP,XM,YP和YM 4個引腳控制。通過讀寫指定的特殊寄存器,S3C2440的觸摸屏控制器將自動控制觸摸屏接口打開或關閉,按指定操作模式完成觸點數據的采集。

圖1 S3C2440 ADC和觸摸屏接口結構
設備驅動程序[2]在Linux內核中占很重要地位,設備驅動以內核模塊方式實現,可動態加載和卸載。Linux設備驅動的實現只需根據內核提供的一組相關數據結構和驅動接口標準,完成關鍵數據結構初始化和回調函數的編寫。對字符設備驅動內核提供cdev數據結構和file_operations結構體及操作方法,實現字符設備驅動只需完成cdev的初始化、file_operations中操作函數的實現并向內核注冊。
Linux輸入子系統是對物理形態各異的功能相似的輸入設備的抽象,是內核中字符設備驅動接口的封裝。輸入子系統由設備驅動層、核心層和事件處理層構成。設備驅動層提供對硬件各寄存器的讀寫訪問和將底層硬件對用戶輸入訪問的響應轉換為標準的輸入事件,通過核心層提交給事件處理層;核心層對設備驅動層提供編程接口,對事件處理層的也提供編程接口;事件處理層為用戶空間的應用程序提供了統一訪問設備的接口和驅動層提交來的事件處理。基于輸入子系統設計驅動時要實現設備驅動層的驅動和事件處理層的驅動,而輸入子系統在事件處理層為觸摸屏提供標準的事件接口,所以只要須完成設備驅動層的驅動,即硬件寄存器的操作和提交輸入事件信息[3]。基于輸入子系統的設備驅動層驅動的實現過程如下:
1)驅動模塊加載函數中設置輸入設備支持輸入子系統的事件;Linux內核用input_dev代表一個輸入設備,對于觸摸屏通過對input_dev實例的evbit[0]的設置來支持同步(EN_SYN)、按鍵(EN_KEY)和絕對坐標(EV_ABS)事件。
2)通過內核提供的input_register_device() 函數向輸入子系統注冊輸入設備。
3)輸入設備發生輸入操作時提交所發生的事件及對應鍵值或坐標等狀態信息。觸摸屏使用輸入子系統提供的通用輸入事件驅動程序Evdev,將事件信息打包成Input_event類型進行報告。
S3C2440觸摸屏控制器有四種工作模式[4],通 過 讀 寫 ADCTSC、ADCDAT0、ADCDAT1和ADCDLY寄存器完成觸摸屏控制器工作模式的選擇和觸摸屏觸點數據采集。由于觸摸動作時間的隨機性,驅動設計時選擇中斷工作方式。設置ADCTSC寄存器為0xD3使觸摸屏控制器進入等待中斷模式,設置ADCDLY采樣延遲時間。當觸摸屏被按下,觸摸屏控制器將產生INT_TC中斷;在INT_TC中斷處理程序中,設置ADCTSC寄存器為0x0C, 觸摸屏控制器切換為自動X/Y坐標轉換模式,將自動轉換觸點對應的x,y坐標值,并分別寫入ADCDAT0寄存器和ADCDTA1寄存器,發出INT_ADC中斷表示ADC轉換完成;進入INT_ADC中斷處理程序讀取ADCDAT0寄存器和ADCDTA1寄存器中坐標數據并進行相應轉換,數據采集后重新設置ADCTSC寄存器為0xD3使觸摸屏控制器進入等待中斷模式,等待觸摸屏被按下。
Linux驅動程序以內核模塊方式加載運行。實現驅動加載函數s3c2440ts_init()并通過module_init(s3c2440ts_init)向內核注冊。在驅動加載函數主要完成:啟用ADC所需要的時鐘、映射IO地址、初始化ADC和觸摸屏控制器相關的寄存器、申請INT_TS和INT_ADC中斷、初始化輸入設備、將輸入設備注冊到輸入子系統。關鍵代碼如下:
/*初始化ADC控制寄存器和ADC觸摸屏控制寄存器*/
adc_initialize();
input_dev = input_allocate_device();
/* 設置觸摸屏支持的事件*/
dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY)| BIT(EV_ABS);
/*設置所支持的按鍵*/
dev->keybit[BITS_TO_LONGS(BTN_TOUCH)]= BIT(BTN_TOUCH);/*設置絕對坐標x、y的最小最大值(0-0x3FF)*/input_set_abs_params(dev,ABS_X,0,0x3FF,0,0);input_set_abs_params(dev,ABS_Y,0,0x3FF,0,0);input_set_abs_params(dev,ABS_PRESSURE,0,1,0,0);
/*申請觸摸屏中斷,觸摸屏按下或提筆時觸發*/
request_irq(IRQ_TC,tc_irq,IRQF_SAMPLE_RANDOM,"s3c2440_ts",1);
/*申請ADC中斷,AD轉換完成后觸發*/
request_irq(IRQ_ADC,adc_irq,IRQF_SHARED|IRQF_SAMPLE_RANDOM,"s3c2440_ts",1);
/*注冊觸摸屏輸入設備*/
input_register_device(dev);
用戶對觸摸屏進行按下、抬起和拖動等操作時,觸發中斷INT_TS,內核進入到中斷處理函數tc_irq ()進行中斷處理。tc_irq ()中,通過ADC_LOCK鎖機制保證只有一個驅動程序使用ADC的中斷線,通過讀取ADCDAT0和ADCDAT1寄存器,判斷觸摸操作的狀態,觸摸筆按下時調用ts_timer_fire()進行數據轉換。當數據轉換完成時產進INT_ADC中斷,內核進入中斷處理函數adc_irq(),adc_irq()完成觸點信息采集并調用ts_timer_fire()進行事件報告。事件報告流程如圖2所示。

圖2 事件報告流程
ts_timer_fire()是主要完成觸點坐標信息向應用層報告。updown、count為靜態全局變量,updown觸點狀態,count代表1個 jiffies 時間內ADC轉換的次數,count為0,設置自動X/Y軸坐標轉換模式,轉換完成后產生相應的INT_ADC中斷通知轉換完畢。count不為0, input_report_abs()函數向輸入子系統報告x,y絕對坐標事件,input_report_key()觸摸屏對應按鍵被按下事件,輸入子系統使用input_sync()將報告的事件組成一個evdev包,通過/dev/input/eventX發送出去,應用程序通過讀取/dev/input/eventX即可獲得事件信息。關鍵代碼如下:
static void ts_timer_fire(unsigned long data)
{
if (updown) {/*updown為1,觸點被按下,為0否則抬起*/
if (count != 0) {
/*報告x,y絕對坐標值,觸摸屏對應按鍵被按下,觸摸屏的狀態*/
input_report_abs(dev, ABS_X, xp);
input_report_abs(dev, ABS_Y, yp);
input_report_key(dev, BTN_TOUCH, 1);
input_report_abs(dev, ABS_PRESSURE, 1);
/*事件同步,組成evdev包提交*/
input_sync(dev);
}
/*設置觸摸屏控制器為自動X/Y軸坐標轉換模式,自動地進行X軸和Y軸的轉換操作,轉換完成后產生INT_ADC中斷通知轉換完畢*/
代碼省略…
} else {
count = 0;
/*如果觸摸筆是彈起狀態,則提出報告,并讓觸摸屏處于等待觸摸的階段*/
input_report_key(dev, BTN_TOUCH, 0);
input_report_abs(dev, ABS_PRESSURE, 0);
input_sync(dev);
/*設置觸摸屏為等待中斷模式,等待觸摸筆按下*/
iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
}
}
隨著信息技術的快速發展,嵌入式技術與人們的生活越來越緊密,觸摸屏作為一種新型輸入設備因具有輕便、占用空間少、方便靈活等優點,應用逐漸普及。要充分發揮觸摸屏的優點,嵌入式中驅動設計至關重要。嵌入式Linux中基于輸入子系統實現觸摸屏驅動時,利用了Linux輸入子系統提供標準事件接口,簡化了驅動設計,驅動設計的重點變成了觸摸屏控制器相關的硬件操作及功能實現,充分體現Linux內核代碼的高可重性,對其他類型輸入設備驅動程序的設計有一定參考作用。
[1] Sreekrishnan Venkateswaran.Essential Linux Device Drivers[M].Prentice Hall PTR,2009.4.
[2] Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman,魏永明,譯.Linux設備驅動程序(第三版)[M].中國電力出版社,2006.
[3] 宋寶華.Linux設備驅動開發詳解[M].人民郵電出版社,2008.
[4] 韋東山.嵌入式Linux應用開發完全手冊[M].人民郵電出版社,2008.