天天看點

迅為IMX6ULL開發闆Linux下電容觸摸屏實驗-驅動架構

如今觸摸屏的使用越來越廣泛,從手機、平闆到蜂巢取貨等場合,都是用了觸摸屏,觸摸屏的使用非常便捷高效。在本章就來學習一下如何在 Linux 下編寫電容觸摸屏驅動。

54.1 Linux  下電容觸摸屏驅動架構

54.1.1  多點觸摸協定詳解

在前面的裸闆實驗中,已經詳細講解過了電容觸摸驅動的基本原理,根據前面的實驗可以總結出電容觸摸屏驅動其實就是一下幾種 linux 驅動架構的組合:

① IIC 裝置驅動,因為電容觸摸 IC 基本都是 IIC 接口的,是以大架構就是 IIC 裝置驅動。

② 通過中斷引腳(INT)向 linux 核心上報觸摸資訊,是以需要用到 linux 中斷驅動架構。坐标的上報在中斷服務函數中完成。

③ 觸摸屏的坐标資訊、螢幕按下和擡起資訊都屬于 linux 的 input 子系統,是以向 linux 核心上報觸摸屏坐标資訊就得使用 input 子系統。隻是,我們得按照 linux 核心規定的規則來上報坐标資訊。

在上面的驅動架構組合中我們發現 I2C 驅動、中斷驅動、input 子系統都已經學習了解過了,還沒有學習過 input 子系統下的多點電容觸摸協定,這個就是本章學習的重點,linux 核心中有一份文檔詳細的講解了多點電容觸摸屏協定,文檔路徑為:Documentation/input/multitouch-protocol.txt。

MT 協定被分為兩種類型,TypeA 和 TypeB,這兩種類型的差別如下:

TypeA:适用于觸摸點不能被區分或者追蹤,此類型的裝置上報原始資料(此類型在實際使用中非常少!)。

Type B:适用于有硬體追蹤并能區分觸摸點的觸摸裝置,此類型裝置通過 slot 更新某一個觸摸點的資訊,FT5426 就屬于此類型,一般的多點電容觸摸屏 IC 都有此能力。

觸摸點的資訊通過一系列的 ABS_MT 事件(有的資料也叫消息)上報給 linux 核心,隻有 ABS_MT 事件是用于多點觸摸的,ABS_MT 事件定義在檔案 linux/input.h 中,相關事件如下所示:

852 #define ABS_MT_SLOT 0x2f

853 #define ABS_MT_TOUCH_MAJOR 0x30

854 #define ABS_MT_TOUCH_MINOR 0x31

855 #define ABS_MT_WIDTH_MAJOR 0x32

856 #define ABS_MT_WIDTH_MINOR 0x33

857 #define ABS_MT_ORIENTATION 0x34

858 #define ABS_MT_POSITION_X 0x35

859 #define ABS_MT_POSITION_Y 0x36

860 #define ABS_MT_TOOL_TYPE 0x37

861 #define ABS_MT_BLOB_ID 0x38

862 #define ABS_MT_TRACKING_ID 0x39

863 #define ABS_MT_PRESSURE 0x3a

864 #define ABS_MT_DISTANCE 0x3b

865 #define ABS_MT_TOOL_X 0x3c

866 #define ABS_MT_TOOL_Y 0x3d

在上面這些衆多的 ABS_MT 事件中,我們最常用的就是 ABS_MT_SLOT 、 ABS_MT_POSITION_X 、ABS_MT_POSITION_Y 和 ABS_MT_TRACKING_ID 。其中 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 用來上報觸摸點的 (X,Y) 坐标資訊,ABS_MT_SLOT 用來上報觸摸點 ID ,對于 Type B 類型的裝置,需要用到ABS_MT_TRACKING_ID 事件來區分觸摸點。

對于 TypeA 類型的裝置,通過 input_mt_sync()函數來隔離不同的觸摸點資料資訊,此函數原型如下所示:

void input_mt_sync(struct input_dev *dev)

此函數隻要一個參數,類型為 input_dev,用于指定具體的 input_dev 裝置。input_mt_sync()函數會觸發 SYN_MT_REPORT 事件,此事件會通知接收者擷取目前觸摸資料,并且準備接收下一個觸摸點資料。

對于 Type B 類型的裝置,上報觸摸點資訊的時候需要通過 input_mt_slot()函數區分是哪一個觸摸點,input_mt_slot()函數原型如下所示:

void input_mt_slot(struct input_dev *dev, int slot)

此函數有兩個參數,第一個參數是 input_dev 裝置,第二個參數 slot 用于指定目前上報的是哪個觸摸點資訊。input_mt_slot()函數會觸發 ABS_MT_SLOT 事件,此事件會告訴接收者目前正在更新的是哪個觸摸點(slot)的資料。

不管是哪個類型的裝置,最終都要調用 input_sync()函數來辨別多點觸摸資訊傳輸完成,告訴接收者處理之前累計的所有消息,并且準備好下一次接收。Type B 和 Type A 相比最大的差別就是 Type B 可以區分出觸摸點, 是以可以減少發送到使用者空間的資料。Type B 使用 slot 協定區分具體的觸摸點,slot 需要用

到 ABS_MT_TRACKING_ID 消息,這個 ID 需要硬體提供,或者通過原始資料計算出來。對于 TypeA 裝置,核心驅動需要一次性将觸摸屏上所有的觸摸點資訊全部上報,每個觸摸點的資訊在本次上報事件流中的順序不重要,因為事件的過濾和手指(觸摸點)跟蹤是在核心空間處理的。

Type B 裝置驅動需要給每個識别出來的觸摸點配置設定一個 slot,後面使用這個 slot 來上報觸摸點資訊。可以通過 slot 的 ABS_MT_TRACKING_ID 來新增、替換或删除觸摸點。一個非負數的 ID 表示一個有效的觸摸點,-1 這個 ID 表示未使用 slot。一個以前不存在的 ID 表示這是一個新加的觸摸點,一個 ID 如果再也不存在了就表示删除了。

有些裝置識别或追蹤的觸摸點資訊要比他上報的多,這些裝置驅動應該給硬體上報的每個觸摸點配置設定一個 Type B 的 slot。一旦檢測到某一個 slot 關聯的觸摸點 ID 發生了變化,驅動就應該改變這個 slot 的ABS_MT_TRACKING_ID,使這個 slot 失效。如果硬體裝置追蹤到了比他正在上報的還要多的觸摸點,那麼驅動程式應該發送 BTN_TOOL_*TAP 消息,并且調用 input_mt_report_pointer_emulation()函數,将此函數的第二個參數 use_count 設定為 false。

54.1.2 Type A  觸摸點資訊上報時序

對于 Type A 類型的裝置,發送觸摸點資訊的時序如下所示,這裡以 2 個觸摸點為例:

1 ABS_MT_POSITION_X x[0]

2 ABS_MT_POSITION_Y y[0]

3 SYN_MT_REPORT

4 ABS_MT_POSITION_X x[1]

5 ABS_MT_POSITION_Y y[1]

6 SYN_MT_REPORT

7 SYN_REPORT

第 1 行,通過 ABS_MT_POSITION_X 事件上報第一個觸摸點的 X 坐标資料,通過 input_report_abs 函數實作,下面同理。

第 2 行,通過 ABS_MT_POSITION_Y 事件上報第一個觸摸點的 Y 坐标資料。

第 3 行,上報 SYN_MT_REPORT 事件,通過調用 input_mt_sync 函數來實作。

第 4 行,通過 ABS_MT_POSITION_X 事件上報第二個觸摸點的 X 坐标資料。

第 5 行,通過 ABS_MT_POSITION_Y 事件上報第二個觸摸點的 Y 坐标資料。

第 6 行,上報 SYN_MT_REPORT 事件,通過調用 input_mt_sync 函數來實作。

第 7 行,上報 SYN_REPORT 事件,通過調用 input_sync 函數實作。

我們在編寫 TypeA 類型的多點觸摸驅動的時候就需要按照上述代碼中的時序上報坐标資訊。Linux 核心 裡 面 也 有 Type A 類 型 的 多 點 觸 摸 驅 動 , 找 到 st2332.c 這 個 驅 動 文 件 , 路 徑 為drivers/input/touchscreen/st1232.c,找到 st1232_ts_irq_handler 函數,此函數裡面就是上報觸摸點坐标資訊的。

103 static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)

104 {

......

111 ret = st1232_ts_read_data(ts);

112 if (ret < 0)

113 goto end;

114

115

116 for (i = 0; i < MAX_FINGERS; i++) {

117 if (!finger.is_valid)

118 continue;

119

120 input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger.t);

121 input_report_abs(input_dev, ABS_MT_POSITION_X, finger.x);

122 input_report_abs(input_dev, ABS_MT_POSITION_Y, finger.y);

123 input_mt_sync(input_dev);

124 count++;

125 }

......

140

141

142 input_sync(input_dev);

143

144 end:

145 return IRQ_HANDLED;

146 }

第 111 行,擷取所有觸摸點資訊。

第 116~125 行,按照 Type A 類型輪流上報所有的觸摸點坐标資訊,第 121 和 122 行分别上報觸摸點的(X,Y)軸坐标,也就是 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 事件。每上報完一個觸摸點坐标,都要在第 123 行調用 input_mt_sync 函數上報一個 SYN_MT_REPORT 資訊。

第 142 行,每上報完一輪觸摸點資訊就調用一次 input_sync 函數,也就是發送一個 SYN_REPORT 事件

54.1.3 Type B  觸摸點資訊上報時序

對于 Type B 類型的裝置,發送觸摸點資訊的時序如下所示,這裡以 2 個觸摸點為例:

1 ABS_MT_SLOT 0

2 ABS_MT_TRACKING_ID 45

3 ABS_MT_POSITION_X x[0]

4 ABS_MT_POSITION_Y y[0]

5 ABS_MT_SLOT 1

6 ABS_MT_TRACKING_ID 46

7 ABS_MT_POSITION_X x[1]

8 ABS_MT_POSITION_Y y[1]

9 SYN_REPORT

第 1 行,上報 ABS_MT_SLOT 事件,也就是觸摸點對應的 SLOT。每次上報一個觸摸點坐标之前要先使用 input_mt_slot 函數上報目前觸摸點 SLOT,觸摸點的 SLOT 其實就是觸摸點 ID,需要由觸摸 IC 提供。

第 2 行,根據 Type B 的要求,每個 SLOT 必須關聯一個 ABS_MT_TRACKING_ID,通過修改 SLOT 關聯 的 ABS_MT_TRACKING_ID 來 完 成 對 觸 摸 點 的 添 加 、 替 換 或 删 除 。 具 體 用 到 的 函 數 就 是input_mt_report_slot_state,如果是添加一個新的觸摸點,那麼此函數的第三個參數 active 要設定為 true,linux 核心會自動配置設定一個 ABS_MT_TRACKING_ID 值,不需要使用者去指定具體的 ABS_MT_TRACKING_ID 值。

第 3 行,上報觸摸點 0 的 X 軸坐标,使用函數 input_report_abs 來完成。

第 4 行,上報觸摸點 0 的 Y 軸坐标,使用函數 input_report_abs 來完成。

第 5~8 行,和第 1~4 行類似,隻是換成了上報觸摸點 0 的(X,Y)坐标資訊

第 9 行,當所有的觸摸點坐标都上傳完畢以後就得發送 SYN_REPORT 事件,使用 input_sync 函數來完成。

當一個觸摸點移除以後,同樣需要通過 SLOT 關聯的 ABS_MT_TRACKING_ID 來處理,時序如下所示:

1 ABS_MT_TRACKING_ID -1

2 SYN_REPORT

第 1 行,當一個觸摸點(SLOT)移除以後,需要通過 ABS_MT_TRACKING_ID 事件發送一個-1 給核心。方法很簡單,同樣使用 input_mt_report_slot_state 函數來完成,隻需要将此函數的第三個參數 active 設定為false 即可,不需要使用者手動去設定-1。

第 2 行,當所有的觸摸點坐标都上傳完畢以後就得發送 SYN_REPORT 事件。

當要編寫 Type B 類型的多點觸摸驅動的時候就需要按照上述代碼中的時序上報坐标資訊。Linux 核心裡面有大量的 Type B 類型的多點觸摸驅動程式,我們可以參考這些現成的驅動程式來編寫自己的驅動代碼。這裡就以 ili210x 這個觸摸驅動 IC 為例,看看是 Type B 類型是如何上報觸摸點坐标資訊的。找到ili210x.c 這個驅動檔案,路徑 為 drivers/input/touchscreen/ili210x.c,找到 ili210x_report_events 函數,此函數就是用于上報 ili210x 觸摸坐标資訊的,函數内容如下所示:

78 static void ili210x_report_events(struct input_dev *input,

79 const struct touchdata *touchdata)

80 {

81 int i;

82 bool touch;

83 unsigned int x, y;

84 const struct finger *finger;

85

86 for (i = 0; i < MAX_TOUCHES; i++) {

87 input_mt_slot(input, i);

88

89 finger = &touchdata->finger;

90

91 touch = touchdata->status & (1 << i);

92 input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);

93 if (touch) {

94 x = finger->x_low | (finger->x_high << 8);

95 y = finger->y_low | (finger->y_high << 8);

96

97 input_report_abs(input, ABS_MT_POSITION_X, x);

98 input_report_abs(input, ABS_MT_POSITION_Y, y);

99 }

100 }

101

102 input_mt_report_pointer_emulation(input, false);

103 input_sync(input);

104 }

第 86~100 行,使用 for 循環實作上報所有的觸摸點坐标,第 87 行調用 input_mt_slot 函數上報ABS_MT_SLOT 事件。第 92 行調用 input_mt_report_slot_state 函數上報 ABS_MT_TRACKING_ID 事件,也就是給 SLOT 關聯一個 ABS_MT_TRACKING_ID。第 97 和 98 行使用 input_report_abs 函數上報觸摸點對應的(X,Y)坐标值。

第 103 行,使用 input_sync 函數上報 SYN_REPORT 事件。

54.1.4 MT  其他事件的使用

在 54.1.1 小節中給出了 Linux 所支援的所有 ABS_MT 事件,大家可以根據實際需求将這些事件組成各種事件組合。最簡單的組合就是 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y,可以通過在這兩個事件上報觸摸點,如果裝置支援的話,還可以使用 ABS_MT_TOUCH_MAJOR 和 ABS_MT_WIDTH_MAJOR 這兩個消息 上 報 觸 摸 面 積 信 息 , 關 于 其 他 ABS_MT 事 件 的 具 體 含 義 大 家 可 以 查 看 Linux 内 核 中 的multi-touch-protocol.txt 文檔,這裡我們重點補充一下 ABS_MT_TOOL_TYPE 事件。

ABS_MT_TOOL_TYPE 事件用于上報觸摸工具類型,很多核心驅動都不能區分出觸摸裝置類型,是手指還是觸摸筆?這種情況下,這個事件可以忽略掉。目前的協定支援 MT_TOOL_FINGER(手指)、MT_TOOL_PEN(筆)和 MT_TOOL_PALM(手掌)這三種觸摸裝置類,于 Type B 類 型 ,此事件由 input 子系統核心處理。如果驅動程式需要上報 ABS_MT_TOOL_TYPE 事件,那麼可以使用 input_mt_report_slot_state 函數來完成此工作。

關于 Linux 系統下的多點觸摸(MT)協定就講解到這裡,簡單總結一下,MT 協定隸屬于 linux 的 input子系統,驅動通過大量的 ABS_MT 事件向 linux 核心上報多點觸摸坐标資料。根據觸摸 IC 的不同,分為TypeA 和 Type B 兩種類型,不同的類型其上報時序不同,目前使用最多的是 Type B 類型。

54.1.5  多點觸摸使用到的 API  函數

根據前面的講解,我們知道 linux 下的多點觸摸協定其實就是通過不同的事件來上報觸摸點坐标資訊,這些事件都是通過 Linux 核心提供的對應 API 函數實作的,本小節我們來看一下一些常見的 API 函數。

1 、input_mt_init_slots  函數

input_mt_init_slots 函數用于初始化 MT 的輸入 slots,編寫 MT 驅動的時候必須先調用此函數初始化slots,此函數定義在檔案 drivers/input/input-mt.c 中,函數原型如下所示:

int input_mt_init_slots( struct input_dev *dev,

unsigned int num_slots,

unsigned int flags)

函數參數和傳回值含義如下:

dev: MT 裝置對應的 input_dev,因為 MT 裝置隸屬于 input_dev。

num_slots:裝置要使用的 SLOT 數量,也就是觸摸點的數量。

flags:其他一些 flags 資訊,可設定的 flags 如下所示:

#define INPUT_MT_POINTER 0x0001

#define INPUT_MT_DIRECT 0x0002

#define INPUT_MT_DROP_UNUSED0x0004

#define INPUT_MT_TRACK 0x0008

#define INPUT_MT_SEMI_MT 0x0010

可以采用‘|’運算來同時設定多個 flags 辨別。

傳回值:0,成功;負值,失敗。

2 、input_mt_slot  函數

此函數用于 Type B 類型,此函數用于産生 ABS_MT_SLOT 事件,告訴核心目前上報的是哪個觸摸點的坐标資料,此函數定義在檔案 include/linux/input/mt.h 中,函數原型如下所示:

void input_mt_slot(struct input_dev *dev,

int slot)

函數參數和傳回值含義如下:

dev: MT 裝置對應的 input_dev。

slot:目前發送的是哪個 slot 的坐标資訊,也就是哪個觸摸點。

傳回值:無。

3 、input_mt_report_slot_state  函數

此 函 數 用 于 Type B 類 型 , 用 于 産 生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE 事 件 ,ABS_MT_TRACKING_ID 事件給 slot 關聯一個 ABS_MT_TRACKING_ID , ABS_MT_TOOL_TYPE 事件指定觸摸類 型(是筆還是手指等)。此函數定義在檔案 drivers/input/input-mt.c 中,此函數原型如下所示:

void input_mt_report_slot_state( struct input_dev *dev,

unsigned int tool_type,

bool active)

函數參數和傳回值含義如下:

dev: MT 裝置對應的 input_dev。

tool_type:觸摸類型,可以選擇 MT_TOOL_FINGER(手指)、MT_TOOL_PEN(筆)或MT_TOOL_PALM(手掌),對于多點電容觸摸屏來說一般都是手指。

active:true,連續觸摸,input 子系統核心會自動配置設定一個 ABS_MT_TRACKING_ID 給 slot。

false,觸摸點擡起,表示某個觸摸點無效了,input 子系統核心會配置設定一個-1 給 slot,表示觸摸點溢出。

傳回值:無。

4 、input_report_abs  函數

TypeA 和 Type B 類 型 都 使 用 此 函 數 上 報 觸 摸 點 坐 标 信 息 , 通 過 ABS_MT_POSITION_X 和ABS_MT_POSITION_Y 事件實作 X 和 Y 軸坐标資訊上報。此函數定義在檔案 include/linux/input.h 中,函數

原型如下所示:

void input_report_abs( struct input_dev *dev,

unsigned int code,

int value)

函數參數和傳回值含義如下:

dev: MT 裝置對應的 input_dev。

code:要上報的是什麼資料,可以設定為 ABS_MT_POSITION_X 或 ABS_MT_POSITION_Y,也就是 X 軸或者 Y 軸坐标資料。

value:具體的 X 軸或 Y 軸坐标資料值。

傳回值:無。

5 、input_mt_report_pointer_emulation  函數

如果追蹤到的觸摸點數量多于目前上報的數量,驅動程式使用 BTN_TOOL_TAP 事件來通知使用者空間目前追蹤到的觸摸點總數量,然後調用 input_mt_report_pointer_emulation 函數将 use_count 參數設定為false。否則的話将 use_count 參數設定為 true,表示目前的觸摸點數量(此函數會擷取到具體的觸摸點數量,不需要使用者給出),此函數定義在檔案 drivers/input/input-mt.c 中,函數原型如下:

void input_mt_report_pointer_emulation(struct input_dev *dev,

bool use_count)

函數參數和傳回值含義如下:

dev: MT 裝置對應的 input_dev。

use_count:true,有效的觸摸點數量;false,追蹤到的觸摸點數量多于目前上報的數量。

傳回值:無。

54.1.6  多點電容觸摸驅動架構

前面幾小節已經詳細的講解了 linux 下多點觸摸屏驅動原理,本小節我們來梳理一下 linux 下多點電容觸摸驅動的編寫架構和步驟。首先确定驅動需要用到哪些知識點,哪些架構?根據前面的分析,我們在編寫驅動的時候需要注意一下幾點:

① 多點電容觸摸晶片的接口,一般都為 I2C 接口,是以驅動主架構肯定是 I2C。

② linux 裡面一般都是通過中斷來上報觸摸點坐标資訊,是以需要用到中斷架構。

③ 多點電容觸摸屬于 input 子系統,是以還要用到 input 子系統架構。

④ 在中斷處理程式中按照 linux 的 MT 協定上報坐标資訊。

根據上面的分析,多點電容觸摸驅動編寫架構以及步驟如下:

1 、I2C  驅動架構

驅動總體采用 I2C 架構,參考架構代碼如下所示:

1

2 static const struct i2c_device_id xxx_ts_id[] = {

3 { "xxx", 0, },

4 { }

5 };

6

7

8 static const struct of_device_id xxx_of_match[] = {

9 { .compatible = "xxx", },

10 { }

11 };

12

13

14 static struct i2c_driver ft5x06_ts_driver = {

15 .driver = {

16 .owner = THIS_MODULE,

17 .name = "edt_ft5x06",

18 .of_match_table = of_match_ptr(xxx_of_match),

19 },

20 .id_table = xxx_ts_id,

21 .probe = xxx_ts_probe,

22 .remove = xxx_ts_remove,

23 };

24

25

30 static int __init xxx_init(void)

31 {

32 int ret = 0;

33

34 ret = i2c_add_driver(&xxx_ts_driver);

35

36 return ret;

37 }

38

39

44 static void __exit xxx_exit(void)

45 {

46 i2c_del_driver(&ft5x06_ts_driver);

47 }

48

49 module_init(xxx_init);

50 module_exit(xxx_exit);

51 MODULE_LICENSE("GPL");

52 MODULE_AUTHOR("topeet");

當裝置樹中觸摸 IC 的裝置節點和驅動比對以後,第 21 行的 xxx_ts_probe 函數就會執行,我們可以在此函數中初始化觸摸 IC,中斷和 input 子系統等。

2摸 、初始化觸摸 IC和 、中斷和 input  子系統

初始化操作都是在 xxx_ts_probe 函數中完成,參考架構如下所示:

1 static int xxx_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)

2 {

3 struct input_dev *input;

4

5

6 ......

7

8

9 devm_request_threaded_irq(&client->dev, client->irq, NULL,

10 xxx_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,

11 client->name, &xxx);

12 ......

13

14

15 input = devm_input_allocate_device(&client->dev);

16

17 input->name = client->name;

18 input->id.bustype = BUS_I2C;

19 input->dev.parent = &client->dev;

20 ......

21

22

23 __set_bit(EV_ABS, input->evbit);

24 __set_bit(BTN_TOUCH, input->keybit);

25

26 input_set_abs_params(input, ABS_X, 0, width, 0, 0);

27 input_set_abs_params(input, ABS_Y, 0, height, 0, 0);

28 input_set_abs_params(input, ABS_MT_POSITION_X,0, width, 0, 0);

29 input_set_abs_params(input, ABS_MT_POSITION_Y,0, height, 0, 0);

30 input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);

31 ......

32

33

34 input_register_device(input);

35 ......

36 }

第 5~7 行,首先肯定是初始化觸摸晶片,包括晶片的相關 IO,比如複位、中斷等 IO 引腳,然後就是晶片本身的初始化,也就是配置觸摸晶片的相關寄存器。

第 9 行,因為一般觸摸晶片都是通過中斷來向系統上報觸摸點坐标資訊的,是以我們需要初始化中斷。大家可能會發現第 9 行并沒有使用 request_irq 函數申請中斷,而是采用了 devm_request_threaded_irq 這個函數,為什麼使用這個函數呢?是不是 request_irq 函數不能使用?答案肯定不是的,這裡用 request_irq函數是絕對沒問題的。那為何要用 devm_request_threaded_irq 呢?這裡我們就簡單的介紹一下這個 API

函數,devm_request_threaded_irq 函數特點如下:

① 用于申請中斷,作用和 request_irq 函數類似。

② 此函數的作用是中斷線程化,大家如果直接在網上搜尋“devm_request_threaded_irq”會發現相關解釋很少。但是大家去搜尋 request_threaded_irq 函數就會有很多講解的部落格和文章,這兩個函數在名字上的差别就是前者比後者多了個“devm_”字首,“devm_”字首稍後講解。大家應該注意到了

“request_threaded_irq”相比“request_irq”多了個 threaded 函數,也就是線程的意思。那麼為什麼要中斷線程化呢?我們都是知道硬體中斷具有最高優先級,不論什麼時候隻要硬體中斷發生,那麼核心都會終止目前正在執行的操作,轉而去執行中斷處理程式(不考慮關閉中斷和中斷優先級的情況),如果中斷非常頻

繁的話那麼核心将會頻繁的執行中斷處理程式,導緻任務得不到及時的處理。中斷線程化以後中斷将作為核心線程運作,而且也可以被賦予不同的優先級,任務的優先級可能比中斷線程的優先級高,這樣做的目的就是保證高優先級的任務能被優先處理。大家可能會疑問,前面不是說可以将比較耗時的中斷放到下半

部(bottom half)處理嗎?雖然下半部可以被延遲處理,但是依舊先于線程執行,中斷線程化可以讓這些比較耗時的下半部與程序進行公平競争。

要注意,并不是所有的中斷都可以被線程化,重要的中斷就不能這麼操作。對于觸摸屏而言隻要手指放到螢幕上,它可能就會一直産生中斷(視具體晶片而定,FT5426 是這樣的),中斷處理程式裡面需要通過 I2C讀取觸摸資訊并上報給核心,I2C 的速度最大隻有 400KHz,算是低速外設。不斷的産生中斷、讀取觸摸信

息、上報資訊會導緻處理器在觸摸中斷上花費大量的時間,但是觸摸相對來說不是那麼重要的事件,是以可以将觸摸中斷線程化。如果你覺得觸摸中斷很重要,那麼就可以不将其進行線程化處理。總之,要不要将一個中斷進行線程化處理是需要自己根據實際情況去衡量的。

③ 最後來看一下“devm_”字首,在 linux 核心中有很多的申請資源類的 API 函數都有對應的“devm_”字首版本。比如 devm_request_irq 和 request_irq 這兩個函數,這兩個函數都是申請中斷的,我們使用request_irq 函數申請中斷的時候,如果驅動初始化失敗的話就要調用 free_irq 函數對申請成功的 irq 進行

釋放,解除安裝驅動的時候也需要我們手動調用 free_irq 來釋放 irq。假如我們的驅動裡面申請了很多資源,比如:gpio、irq、input_dev,那麼就需要添加很多goto 語句對其做處理,當這樣的标簽多了以後代碼看起來就不整潔了。“devm_”函數就是為了處理這種情況而誕生的,“devm_”函數最大的作用就是:

使用“devm_”字首的函數申請到的資源可以由系統自動釋放,不需要我們手動處理。 如果我們使用devm_request_threaded_irq 函數來申請中斷,那麼就不需要我們再調用 free_irq 函數對其進行釋放。大家可以注意一下,帶有“devm_”字首的都是一些和裝置資源管理有關的函數。關于“devm_”函數的實作原理這裡就不做詳細的講解了,我們的重點在于學會如何使用這些 API 函數,感興趣的可以查閱一些其他文檔或者文章來看一下“devm_”函數的實作原理。

第 15 行,接下來就是申請 input_dev,因為多點電容觸摸屬于 input 子系統。這裡同樣使用devm_input_allocate_device 函數來申請 input_dev,也就是我們前面講解的 input_allocate_device 函數加“devm_”字首版本。申請到 input_dev 以後還需要對其進行初始化操作。

第 23~24 行,設定 input_dev 需要上報的事件為 EV_ABS 和 BTN_TOUCH,因為多點電容屏的觸摸坐标為絕對值,是以需要上報 EV_ABS 事件。觸摸屏有按下和擡起之分,是以需要上報 BTN_TOUCH 按鍵。

第 26~29 行,調用 input_set_abs_params 函數設定 EV_ABS 事件需要上報 ABS_X、ABS_Y、ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。單點觸摸需要上報 ABS_X 和 ABS_Y,對于多點觸摸需要上報 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。

第 30 行,調用 input_mt_init_slots 函數初始化多點電容觸摸的 slots。

第 34 行,調用 input_register_device 函數系統注冊前面申請到的 input_dev。

3 、上報坐标資訊

最後就是在中斷服務程式中上報讀取到的坐标資訊,根據所使用的多點電容觸摸裝置類型選擇使用TypeA 還是 Type B 時序。由于大多數的裝置都是 Type B 類型,是以這裡就以 Type B 類型為例講解一下上報過程,參考驅動架構如下所示:

1 static irqreturn_t xxx_handler(int irq, void *dev_id)

2 {

3

4 int num;

5 int x[n], y[n];

6

7

8 ......

9

10

11 for (i = 0; i < num; i++) {

12 input_mt_slot(input, id);

13 input_mt_report_slot_state(input, MT_TOOL_FINGER, true);

14 input_report_abs(input, ABS_MT_POSITION_X, x);

15 input_report_abs(input, ABS_MT_POSITION_Y, y);

16 }

17 ......

18

19 input_sync(input);

20 ......

21

22 return IRQ_HANDLED;

23 }

進入中斷處理程式以後首先肯定是從觸摸 IC 裡面讀取觸摸坐标以及觸摸點數量,假設觸摸點數量儲存到 num 變量,觸摸點坐标存放到 x,y 數組裡面。

第 11~16 行,循環上報每一個觸摸點坐标,一定要按照 Type B 類型的時序進行。

第 19 行,每一輪觸摸點坐标上報完畢以後就調用一次 input_sync 函數發送一個 SYN_REPORT 事件。

關于多點電容觸摸驅動架構就講解到這裡,接下來我們就實際編寫一個多點電容觸摸驅動程式。

繼續閱讀