TP觸控螢幕,應該是驅動開發中比較簡單並且適合新手入手的模組。不過雖然簡單,但涉及到的內容還是比較多的,其中linux相關主要的機制:
1. input 機制
2. 中斷、定時器
3. I2C
1.TP的原理:TP一般為電容或為電阻屏,不過現在基本上都是電容屏,可能一些WINCE的裝置還會用電阻屏,但Android的基本現在為電容屏,且多點觸摸和手套觸摸都整合在TP的IC中。當使用者觸摸電容屏時,由於人體電場,使用者手指和工作面形成一個耦合電容,因為工作面上接有高頻訊號,於是手指吸收走一個很小的電流,這個電流分別從屏的四個角上的電極中流出,且理論上流經四個電極的電流與手指頭到四角的距離成比例,控制器通過對四個電流比例的精密計算,得出位置。可以達到99%的精確度,具備小於3ms的響應速度。在實際產品中,當螢幕感應到手指的接觸或者靠近,會產生一個外部中斷給CPU,在中斷中,一般中斷下半部,通過I2C匯流排,從TP的IC中讀取相關的資訊,經過一定的資料處理,上報X,Y座標值。
2.linux input機制:
linux輸入子系統(linux input subsystem)從上到下由三層實現,分別為:輸入子系統事件處理層(EventHandler)、輸入子系統核心層(InputCore)和輸入子系統裝置驅動層。
對於輸入子系統裝置驅動層而言,主要實現對硬體裝置的讀寫訪問,中斷設定,並把硬體產生的事件轉換為核心層定義的規範提交給事件處理層。(工程師主要做的事情)
對於核心層而言,為裝置驅動層提供了規範和介面。裝置驅動層只要關心如何驅動硬體並獲得硬體資料(例如按下的按鍵資料),然後調用核心層提供的介面,核心層會自動把資料提交給事件處理層。
對於事件處理層而言,則是使用者編程的介面(裝置節點),並處理驅動層提交的資料處理。
3. 中斷
中 斷是指在CPU正常運行期間,由於內外來事件或由程式預先安排的事件引起的CPU暫時停止正在啟動並執行程式,轉而為該內部或外來事件或預先安排的事件服務的 程式中去,服務完畢後再返回去繼續運行被暫時中斷的程式。Linux中通常分為外部中斷(又叫硬體中斷)和內部中斷(又叫異常)。
Linux 中斷分為兩個半部:上半部(tophalf)和下半部(bottom half)。上半部的功能是"登記中斷",當一個中斷髮生時,它進行相應地硬體讀寫後就把中斷常式的下半部掛到該裝置的下半部執行隊列中去。因此,上半部 執行的速度就會很快,可以服務更多的插斷要求。但是,僅有"登記中斷"是遠遠不夠的,因為中斷的事件可能很複雜。因此,Linux引入了一個下半部,來完 成中斷事件的絕大多數使命。下半部和上半部最大的不同是下半部是可中斷的,而上半部是不可中斷的,下半部幾乎做了中斷處理常式所有的事情,而且可以被新的 中斷打斷。下半部則相對來說並不是非常緊急的,通常還是比較耗時的,因此由系統自行安排運行時機,不在中斷服務上下文中執行。
4. 定時器
兩種裝置進行計時:系統定時器和系統時鐘。
系統時鐘(RTC):用來持久存放系統時間的裝置,即便系統關閉後,靠主板上的微型電池提供電力保持系統的計時。系統啟動核心通過讀取RTC來初始化牆上時間,改時間存放在xtime變數中。
系統定時器:核心定時機制,註冊中斷處理常式,周期性觸發中斷,響應中斷處理常式,進行處理執行以下工作:
5. I2C協議
2條雙向串列線,一條資料線SDA,一條時鐘線SCL。
SDA傳輸資料是大端傳輸,每次傳輸8bit,即一位元組。
支援多主控(multimastering),任何時間點只能有一個主控。
匯流排上每個裝置都有自己的一個addr,共7個bit,廣播位址全0.
系統中可能有多個同種晶片,為此addr分為固定部分和可程式化部份,細節視晶片而定,看datasheet。
上一篇主要講的與TP相關的知識點,講這個主要是,你對這一模組有個基本的概念,知道其工作原理,這樣在實際開發過程中,你才知道怎麼去寫起的驅動,碰到問題時,出現在哪一塊,該怎麼去解決。
這一篇,主要根據代碼來講上一篇涉及到的相關機制。TP的連線很簡單,一路I2C,一根中斷線,VCC、GND,reset。
1. TP主要相關demo:
驅動demo:\kernel\drivers\input\touchscreen\Ft5x06_ts.c
\kernel\drivers\input\touchscreen\Ft5x06_ts.h
dtsi檔案:\kernel\arch\arm\boot\dts\qcom\Msm8X16-qrd.dtsi
2. dtsi的相關解釋
i2c@f9923000{ //TP所連的I2C的寄存器
focaltech@38{
compatible = "focaltech,5x06"; //I2C驅動match的內容
reg = <0x38>; //I2C地址
interrupt-parent = <&msmgpio>; //中斷引腳
interrupts = <1 0x2>;
vdd-supply = <&pm8110_l19>; //I2C供電的電影控制
vcc_i2c-supply = <&pm8110_l14>;
focaltech,name = "ft6x06"; //tp類型
focaltech,family-id = <0x06>;
focaltech,reset-gpio = <&msmgpio 0 0x00>; //複位引腳,,用於初始化時序
focaltech,irq-gpio = <&msmgpio 1 0x00>; //中斷引腳
focaltech,display-coords = <0 0 480 800>; //TP的觸點範圍
focaltech,panel-coords = <0 0 480 800>;
focaltech,button-map= <139 102 158>; //虛擬按鍵
focaltech,no-force-update;
focaltech,i2c-pull-up;
focaltech,group-id = <1>;
focaltech,hard-reset-delay-ms = <20>;
focaltech,soft-reset-delay-ms = <150>;
focaltech,num-max-touches = <2>;
focaltech,fw-name = "ft_8610_qrd_fw.bin"; //TP韌體
focaltech,fw-delay-aa-ms = <100>;
focaltech,fw-delay-55-ms = <30>;
focaltech,fw-upgrade-id1 = <0x79>;
focaltech,fw-upgrade-id2 = <0x08>;
focaltech,fw-delay-readid-ms = <10>;
focaltech,fw-delay-era-flsh-ms = <2000>;
};
};
3. 驅動檔案
3.1 I2C驅動註冊
static int __init ft5x06_ts_init(void)
{
pr_err("start \n");
return i2c_add_driver(&ft5x06_ts_driver); //I2C驅動註冊
pr_err("end \n");
}
3.2 檔案介面,of_match_table,需要DTSI中compatible定義的一致。
static struct i2c_driver ft5x06_ts_driver = {
.probe = ft5x06_ts_probe,
.remove = ft5x06_ts_remove,
.driver = {
.name = "ft5x06_ts",
.owner = THIS_MODULE,
.of_match_table = ft5x06_match_tabl
#ifdef CONFIG_PM
.pm = &ft5x06_ts_pm_ops,
#endif
},
.id_table = ft5x06_ts_id,
};
3.3 probe
這個裡面代碼量太多,就不詳細一一說明,這裡主要講解一些基本的步驟。
一般,先給相關的結構體分配儲存空間,然後從dt裝置樹中讀取相關的資訊,測試I2C是否通。
input_dev = input_allocate_device();//分配輸入子系統
data->input_dev = input_dev;
data->client = client;
data->pdata = pdata;
input_dev->name = "ft5x06_ts";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
__set_bit(EV_KEY, input_dev->evbit); //設定有什麼事件 按鍵事件 tp虛擬按鍵
__set_bit(EV_ABS, input_dev->evbit); //絕對事件
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
input_mt_init_slots(input_dev, pdata->num_max_touches, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, //X座標事件
pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, //Y座標事件
pdata->y_max, 0, 0);
ft5x06_power_init(data, true); //電源、GPIO相關的初始化
ft5x06_power_on(data, true); // 上電,時序初始化
err = request_threaded_irq(client->irq, NULL,ft5x06_ts_interrupt,IRQF_ONESHOT,
client->dev.driver->name, data); //請求中斷,並把事件處理放在下半部。
psensor_input_dev = input_allocate_device(); 分配子輸入裝置
err = input_register_device(psensor_input_dev);
ft5x06_update_fw_ver(data); //匯入韌體資訊
ft5x06_update_fw_vendor_id(data); //擷取韌體版本ID
3.4 中斷處理常式 ft5x06_ts_interrupt
static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
這個裡面主要是讀I2C,根據晶片手冊,進行資料出來,然後上報事件。
input_report_abs(ip_dev, ABS_MT_POSITION_X, x);
input_report_abs(ip_dev, ABS_MT_POSITION_Y, y);
input_sync(ip_dev);
當然有些TP,如GT9xxxx,會申請工作隊列,然後中斷產生會在上半部,啟動工作隊列,然後屏蔽當前中斷,在工作隊列事件處理完後,使能中斷。有些還會當插斷要求失敗時,申請一個高精度的定時器,會一直輪詢啟動背景工作執行緒,上報事件。
4. 調試相關經驗
4.1 一般TP驅動開發,屏產都會給驅動代碼或者PATCH,這時主要合代碼進去。
一般找代碼內現有的一個TP驅動,按它的添加。主要:
1. 把驅動檔案放入kernel\drivers\input\touchscreen\,
2. 修改kconfig和Makefile,加入需要根據宏才能編進去,那麼需要在deconfig設定檔中設定為Y.
3. 在DTSI中加入該TP的配置。
4.2 編譯boot,在out/target/product/msmXXX/obj/KERNEL_OBJ/driver/input/touchscreen/下,看是否有.o檔案沒有,有則編譯成功。
4.3 把新的boot檔案刷入板子,查看核心log,cat proc/kmsg,看是否有該TP驅動的列印資訊。
4.4 根據列印資訊,判斷出錯的問題。
一般問題,中斷註冊不上,資源分派不成功,I2C裝置通訊失敗。
一些經驗,I2C匯流排不通,可能是因為I2C供電的電源沒有供電,或者該匯流排上掛的裝置太多影響的,前期調最好I2C匯流排上,只掛一個裝置。
若probe成功,可在中斷或者背景工作執行緒裡面加一些列印log。在adb shell進入終端,輸入getevent,手按TP,查看是否有資料打出,對於該TP的輸入裝置。
5. 查看I2C裝置:
root@Android:/sys/bus/i2c # cd devices
cd devices
root@android:/sys/bus/i2c/devices # ls
ls
0-0020
0-0022
0-0036
0-0078
1-000c
1-000d
1-001d
1-0028
1-0029
1-002a
1-0038
1-0060
1-0068
2-001c
i2c-0
i2c-1
i2c-2
root@android:/sys/bus/i2c/devices # cd 0-0036
cd 0-0036
root@android:/sys/bus/i2c/devices/0-0036 # ls
ls
driver
modalias
name
power
subsystem
uevent
root@android:/sys/bus/i2c/devices/0-0036 # cat name
cat name
msm_actuator
root@android:/sys/bus/i2c/devices/0-0036 #