【i.MX6ULL】驅(qū)動(dòng)開(kāi)發(fā)13——電容觸摸驅(qū)動(dòng)實(shí)踐(下)

本篇,先來(lái)介紹一會(huì)測(cè)試觸摸是庫(kù)——tslib,使用它可以進(jìn)行圖形化的觸摸測(cè)試。之后,再回頭來(lái)分析分析觸摸協(xié)議上報(bào)的原理以及通過(guò)input子系統(tǒng)上報(bào)的數(shù)據(jù)的具體含義。
1 tslib的使用
Tslib是一個(gè)開(kāi)源的程序,能夠?yàn)橛|摸屏驅(qū)動(dòng)獲得的采樣提供諸如濾波、去抖、校準(zhǔn)等功能,通常作為觸摸屏驅(qū)動(dòng)的適配層,為上層的應(yīng)用提供了一個(gè)統(tǒng)一的接口。
1.1 tslib庫(kù)移植
首先下載tslib庫(kù)的源碼:https://github.com/libts/tslib/tags
目前最新的是1.22,不過(guò)本篇先使用1.21版本

1.1.1 ubuntu上編譯tslib
將下載的源碼拷貝到ubuntu虛擬機(jī)中,然后解壓:
編譯 tslib 的時(shí)候需要先在 ubuntu 中安裝一些文件
在 ubuntu 中創(chuàng)建一個(gè)名為“tslib”的目錄存放編譯結(jié)果,然后執(zhí)行以下指令進(jìn)行編譯:

編譯完成后,make install會(huì)將編譯成果復(fù)制到指定的tslib目錄中:

可以看到最終編譯生成的是5個(gè)文件夾。
1.1.2 開(kāi)發(fā)板上配置tslib
將編譯出的5個(gè)文件夾整個(gè)復(fù)制到開(kāi)發(fā)板的根文件系統(tǒng)中:
然后打開(kāi)板子的/etc/ts.conf 文件,找到下面這一行:
如果這句前面有“#”注釋?zhuān)蛣h除掉“#“,我這個(gè)默認(rèn)是沒(méi)有的,所以不用修改

打開(kāi)板子的/etc/profile文件,我的板子此時(shí)沒(méi)有這個(gè)文件,所以我新建了一個(gè)該文件,然后在里面加入如下內(nèi)容:
TSLIB_TSDEVICE :觸摸設(shè)備文件,要根據(jù)具體情況設(shè)置為/dev/input/event1還是event2(如果接口鼠標(biāo)鍵盤(pán),這個(gè)編號(hào)可能還會(huì)變,比如我接了無(wú)線鍵盤(pán)后,觸摸就又變成了event)
TSLIB_CALIBFILE :校準(zhǔn)文件,此文件可以不存在,校準(zhǔn)的時(shí)候會(huì)自動(dòng)生成
TSLIB_CONFFILE :觸摸配置文件,在移植 tslib 的時(shí)候會(huì)生成
TSLIB_PLUGINDIR :tslib 插件目錄位置
TSLIB_CONSOLEDEVICE :控制臺(tái)設(shè)置,這里不設(shè)置,設(shè)為none
TSLIB_FBDEVICE:FB 設(shè)備,也就是屏幕,也要根據(jù)實(shí)際情況配置設(shè)置為/dev/fb0或是其它
1.2 tslib庫(kù)測(cè)試
1.2.1 屏幕校準(zhǔn)
電容屏可以不用校準(zhǔn),不過(guò)也可以看看tslib的校準(zhǔn)測(cè)試用例,輸入如下指令:
校準(zhǔn)完成以后如果不滿(mǎn)意,刪除掉/etc/pointercal文件即可

1.2.2 多點(diǎn)觸摸拖拽測(cè)試
使用如下指令:
然后會(huì)出現(xiàn)一個(gè)觸摸測(cè)試界面,先測(cè)試Drag功能,手指接觸屏幕后進(jìn)行移動(dòng),屏幕上的十字標(biāo)記就會(huì)跟著移動(dòng):

1.2.3 多點(diǎn)觸摸劃線測(cè)試
還是剛才的指令,再來(lái)測(cè)試Draw功能,手指接觸屏幕后進(jìn)行移動(dòng),屏幕上就會(huì)出現(xiàn)滑過(guò)的軌跡線:

2 多點(diǎn)觸摸(MT)協(xié)議講解
多點(diǎn)觸摸協(xié)議,即Multi-touch (MT) Protocol,該協(xié)議的介紹,在linux內(nèi)核源碼中有對(duì)應(yīng)的文檔,如下圖:

多點(diǎn)電容觸摸的協(xié)議分為兩種類(lèi)型:TypeA和TypeB,目前基本都是使用TypeB協(xié)議。
TypeA協(xié)議適用于觸摸點(diǎn)不能被區(qū)分或者追蹤,此類(lèi)設(shè)備上報(bào)原始數(shù)據(jù)。
TypeB協(xié)議適用于有硬件追蹤并能區(qū)分觸摸點(diǎn)的觸摸設(shè)備,此類(lèi)型設(shè)備通過(guò)slot更新某一個(gè)觸摸點(diǎn)的信息。
觸摸點(diǎn)的信息通過(guò)一系列的 ABS_MT事件上報(bào)給linux內(nèi)核,這些事件的定義在include/uapi/linux/input.h中:

比較常用的有:
ABS_MT_SLOT :上報(bào)觸摸點(diǎn)ID
ABS_MT_POSITION_X:上報(bào)觸摸點(diǎn)的X坐標(biāo)信息
ABS_MT_POSITION_Y:上報(bào)觸摸點(diǎn)的Y坐標(biāo)信息
ABS_MT_TRACKING_ID:TypeB區(qū)分觸摸點(diǎn)
下面具體介紹兩種協(xié)議的區(qū)別。
2.1 TypeA協(xié)議
TypeA協(xié)議適用于觸摸點(diǎn)不能被區(qū)分或者追蹤,此類(lèi)設(shè)備上報(bào)原始數(shù)據(jù)。
TypeA協(xié)議發(fā)送觸摸點(diǎn)信息的時(shí)序如下(以 2 個(gè)觸摸點(diǎn)為例):
首先每上報(bào)一個(gè)點(diǎn)的x和y
然后上報(bào)一個(gè)SYN_MT_REPORT
依次循環(huán)上報(bào)其它點(diǎn)
所有的點(diǎn)上報(bào)完后,再上報(bào)一個(gè)SYN_REPORT
當(dāng)?shù)谝粋€(gè)觸點(diǎn)離開(kāi)后,上報(bào)的時(shí)序如下(就是只上報(bào)剩下的那一個(gè)):
當(dāng)?shù)诙€(gè)觸點(diǎn)也離開(kāi)后,上報(bào)的時(shí)序如下(就是上報(bào)空數(shù)據(jù)):
如果驅(qū)動(dòng)除了ABS_MT事件外還上報(bào)BTN_TOUCH或ABS_PRESSURE之一,則最后一個(gè)SYN_MT_REPORT事件可能被忽略。另外,最后的SYN_REPORT會(huì)被輸入內(nèi)核放棄,從而導(dǎo)致沒(méi)有空觸事件到達(dá)用戶(hù)層。
2.2 TypeB協(xié)議
TypeB協(xié)議適用于有硬件追蹤并能區(qū)分觸摸點(diǎn)的觸摸設(shè)備,此類(lèi)型設(shè)備通過(guò)slot更新某一個(gè)觸摸點(diǎn)的信息。
TypeA協(xié)議發(fā)送觸摸點(diǎn)信息的時(shí)序如下(以 2 個(gè)觸摸點(diǎn)為例):
每個(gè)數(shù)據(jù)點(diǎn)前,先上報(bào)ABS_MT_SLOT事件,帶上一個(gè)觸摸點(diǎn)ID,此ID由觸摸IC提供
TypeB要求每個(gè)SLOT須關(guān)聯(lián)一個(gè)ABS_MT_TRACKING_ID,這個(gè)ID由linux內(nèi)核自動(dòng)分配
然后上報(bào)一個(gè)點(diǎn)的x和y
依次循環(huán)上報(bào)其它點(diǎn)
所有的點(diǎn)上報(bào)完后,再上報(bào)一個(gè)SYN_REPORT。
當(dāng)觸點(diǎn)45在X方向上移動(dòng)后,上報(bào)的時(shí)序如下:
當(dāng)slot 0中觸點(diǎn)離開(kāi)后,上報(bào)的時(shí)序如下:
由于slot被修改為0,因此這個(gè)ABS_MT_SLOT被忽略。這條信息移除了slot 0和觸點(diǎn)45的聯(lián)系,因此銷(xiāo)毀觸點(diǎn)45同時(shí)釋放slot 0給另外的觸點(diǎn)再次使用。
當(dāng)?shù)诙€(gè)觸點(diǎn)離開(kāi)后,上報(bào)的時(shí)序如下:
總結(jié)對(duì)比一下兩個(gè)觸摸協(xié)議的區(qū)別:

2.3 多點(diǎn)觸摸API函數(shù)
了解了兩種觸摸協(xié)議,在編程時(shí),就要使用其相應(yīng)的API函數(shù)來(lái)實(shí)現(xiàn)觸摸數(shù)據(jù)的上報(bào),下面是常用的API函數(shù)。
2.3.1 input_mt_init_slots
該函數(shù)用于初始化MT的輸入slots,其函數(shù)原型如下:
其中第3個(gè)參數(shù),可設(shè)置的flags包括:
可以使用‘|’運(yùn)算來(lái)同時(shí)設(shè)置多個(gè)flags標(biāo)識(shí)
2.3.2 input_mt_slot
該函數(shù)用于Type B類(lèi)型,用于產(chǎn)生 ABS_MT_SLOT事件,其函數(shù)原型如下:
2.3.3 input_mt_report_slot_state
該函數(shù)用于Type B類(lèi)型,用于產(chǎn)生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件,其函數(shù)原型如下:
其中第2個(gè)參數(shù),tool_type包括:
MT_TOOL_FINGER:手指
MT_TOOL_PEN:筆
MT_TOOL_PALM:手掌
其中第3個(gè)參數(shù),active包括:
true: 連續(xù)觸摸, input子系統(tǒng)內(nèi)核會(huì)自動(dòng)分配一個(gè)ABS_MT_TRACKING_ID給slot
false:觸摸點(diǎn)抬起,表示某個(gè)觸摸點(diǎn)無(wú)效了,input子系統(tǒng)內(nèi)核會(huì)分配一個(gè)-1給slot
2.3.4 input_report_abs
該函數(shù)用于上報(bào)觸摸點(diǎn)坐標(biāo),TypeA和TypeB類(lèi)型都使用此函數(shù)上報(bào)觸摸點(diǎn)坐標(biāo)信息,其函數(shù)原型如下:
其中第2個(gè)參數(shù),code包括:
ABS_MT_POSITION_X
ABS_MT_POSITION_Y
2.3.5 input_mt_report_pointer_emulation
如果追蹤到的觸摸點(diǎn)數(shù)量多于當(dāng)前上報(bào)的數(shù)量,驅(qū)動(dòng)程序使用 BTN_TOOL_TAP 事件來(lái)通知用戶(hù)空間當(dāng)前追蹤到的觸摸點(diǎn)總數(shù)量,然后調(diào)用 input_mt_report_pointer_emulation 函數(shù)將use_count 參數(shù)設(shè)置為 false,否則的話(huà)將 use_count 參數(shù)設(shè)置為 true。
3 input子系統(tǒng)上報(bào)數(shù)據(jù)含義講解
3.1 input子系統(tǒng)簡(jiǎn)介
在Linux中,對(duì)于輸入設(shè)備,例如按鍵、 鼠標(biāo)、 鍵盤(pán)、 觸摸屏等,為了更加方便統(tǒng)一的管理, Linux內(nèi)核為此專(zhuān)門(mén)做了一個(gè)input子系統(tǒng)的框架來(lái)處理輸入事件。
input是輸入的意思,就是管理輸入的子系統(tǒng),和 pinctrl、gpio 子系統(tǒng)一樣,都是 Linux 內(nèi)核針對(duì)某一類(lèi)設(shè)備而創(chuàng)建的框架。input 子系統(tǒng)框架圖如下:

3.2 input輸出事件
3.2.1 事件類(lèi)型
evbit 表示輸入事件類(lèi)型,可選的事件類(lèi)型定義在 include/uapi/linux/input.h 文件中,事件類(lèi)型如下:

各個(gè)的含義為:
例如,如果要使用按鍵的inpu件功能,就需要注冊(cè)EV_KEY事件,若還要使用連按功能,需要注冊(cè)EV_REP事件。
如果要使用觸摸屏的inpu件功能,就需要注冊(cè)EV_KEY事件,
3.2.2 按鍵值類(lèi)型
evbit、keybit、relbit 等等都是存放不同事件對(duì)應(yīng)的值,Linux 內(nèi)核定義了很多按鍵值:
具體的定義在input.h文件中:

3.3 觸摸數(shù)據(jù)上報(bào)實(shí)例分析
上篇文章只是將觸摸坐標(biāo)打印到了屏幕,實(shí)際是使用觸摸屏?xí)r,需要將坐標(biāo)數(shù)據(jù)通過(guò)input子系統(tǒng)上報(bào)應(yīng)用層,現(xiàn)在來(lái)具體分析一下input子系統(tǒng)上報(bào)的這些數(shù)據(jù)的含義,例如按下觸摸鍵后,串口會(huì)有如下打?。?/p>
將數(shù)據(jù)內(nèi)容摘出來(lái)看:
type 為事件類(lèi)型
0000:EV_SYN,同步事件
0001:EV_KEY,按鍵事件
0003:EV_ABS,絕對(duì)坐標(biāo)事件
code 為事件編碼,也就是按鍵號(hào)
0000:ABS_X,單點(diǎn)觸摸上報(bào)X坐標(biāo)值
0001:ABS_Y,單點(diǎn)觸摸上報(bào)Y坐標(biāo)值
0035:ABS_MT_POSITION_X,多點(diǎn)觸摸上報(bào)X坐標(biāo)值
0036:ABS_MT_POSITION_Y,多點(diǎn)觸摸上報(bào)Y坐標(biāo)值
0039:ABS_MT_TRACKING_ID,觸摸點(diǎn)的track id
014a:BTN_TOUCH,觸摸按鍵
value 就是按鍵值, 為 1 表示按下, 為 0 的話(huà)表示松開(kāi)
來(lái)分析一下每行輸出的含義:
第1行:絕對(duì)坐標(biāo)事件,觸摸點(diǎn)的track id,id=0
第2行:絕對(duì)坐標(biāo)事件,多點(diǎn)觸摸X坐標(biāo)值,X=0x9d (157)
第3行:絕對(duì)坐標(biāo)事件,多點(diǎn)觸摸Y坐標(biāo)值,Y=0xc1 (193)
第4行:按鍵事件,觸摸按鍵,1表示按鍵按下
第5行:絕對(duì)坐標(biāo)事件,單點(diǎn)觸摸X坐標(biāo)值,X=0x9d (157)
第6行:絕對(duì)坐標(biāo)事件,單點(diǎn)觸摸Y坐標(biāo)值,Y=0xc1 (193)
第7行:同步事件,由input_sync函數(shù)上報(bào)
第8行:絕對(duì)坐標(biāo)事件,觸摸點(diǎn)的track id,id=0xffffffff=-1,即觸摸點(diǎn)離開(kāi)了屏幕
第9行:按鍵事件,觸摸按鍵,0表示沒(méi)有按鍵
第10行:同步事件,由input_sync函數(shù)上報(bào)
注:上面的打印,有多點(diǎn)觸摸和單點(diǎn)觸摸的上報(bào),實(shí)際上如果使用了多點(diǎn)觸摸,可以將單點(diǎn)觸摸的上報(bào)去掉,如下:

去掉后,再次測(cè)試,可以看到只有多點(diǎn)觸摸數(shù)據(jù)的上報(bào):

4 將觸摸驅(qū)動(dòng)編譯到內(nèi)核
自己編寫(xiě)的觸摸驅(qū)動(dòng),每次系統(tǒng)啟動(dòng)后,都要手動(dòng)加載驅(qū)動(dòng)模塊后才能使用,比較麻煩,現(xiàn)在驅(qū)動(dòng)文件不需要再改了,就可以將自己的驅(qū)動(dòng)直接編譯到內(nèi)核中。方法如下:
將自己寫(xiě)的觸摸屏驅(qū)動(dòng)文件拷貝到Linux內(nèi)核的drivers/input/touchscreen/目錄下:
修改 drivers/input/touchscreen 目錄下的 Makefile,在最下面添加下面一行:

然后(使用之前編寫(xiě)的編譯腳本)重新編譯linux內(nèi)核
再將zImage拷貝到板子中,重新啟動(dòng)板子。
正常情況下,在內(nèi)核啟動(dòng)的時(shí)候就打印出觸摸驅(qū)動(dòng)的event編號(hào)信息,我這里確實(shí)也打印了,只是隨后一直刷IIC錯(cuò)誤:

暫時(shí)看不出來(lái)是什么原因,才這居打印看,觸摸開(kāi)始讀數(shù)據(jù)時(shí)才會(huì)進(jìn)到這里,感覺(jué)像是觸摸驅(qū)動(dòng)剛加載完成,就觸發(fā)了中斷,但在中斷里通過(guò)IIC讀取觸摸數(shù)據(jù)時(shí),又出現(xiàn)了問(wèn)題。。。
一個(gè)暫時(shí)的替代方式是,可以在開(kāi)機(jī)自啟動(dòng)文件中進(jìn)行觸摸驅(qū)動(dòng)的加載,在/etc/init.d/rcS文件中補(bǔ)充如下語(yǔ)句即可:
5 總結(jié)
本篇首先介紹了測(cè)試觸摸是庫(kù)——tslib,使用它可以進(jìn)行圖形化的觸摸測(cè)試。隨后,又分析觸摸協(xié)議上報(bào)的原理以及通過(guò)input子系統(tǒng)上報(bào)的數(shù)據(jù)的具體含義。
附:演示視頻
https://www.bilibili.com/video/BV1XL4y1t7kf?spm_id_from=333.999.0.0
