【i.MX6ULL】驅(qū)動開發(fā)12——電容觸摸驅(qū)動實踐(上)
觸摸驅(qū)動的編寫以及將觸摸點坐標(biāo)實時打印出來進行測試,先有一個整體的使用感受,下篇文章再介紹具體的觸摸上報協(xié)議以及圖形化的測試方法。
1 觸摸介紹
LCD的觸摸功能,本質(zhì)就是顯示屏上再疊加一層透明的觸摸屏,實現(xiàn)觸摸的方式與LCD進行交互。
觸摸屏分為電阻觸摸屏和電容觸摸屏。
電阻觸摸屏是一種傳感器,其結(jié)構(gòu)是薄膜加上玻璃的結(jié)構(gòu),兩結(jié)構(gòu)相鄰的一面上均涂有ITO(一種導(dǎo)電性和透明性很好的)涂層。當(dāng)觸摸操作時,兩層結(jié)構(gòu)擠壓接觸,經(jīng)由感應(yīng)器傳出相應(yīng)的電信號,通過運算轉(zhuǎn)化為屏幕上的X、Y值。
電容技術(shù)觸摸屏CTP(Capacity Touch Panel)是利用人體的電流感應(yīng)進行工作的。電容屏是一塊四層復(fù)合玻璃屏,電容式觸摸屏就是支持多點觸摸的人機交互方式,普通電阻式觸摸屏只能進行單一點的觸控。
1.1 硬件原理圖
本篇使用的是野火的7寸電容觸摸屏,分辨率和屏幕一樣,800x480。觸摸驅(qū)動芯片我GT911,是IIC接口的芯片。

觸摸芯片有四個引腳:
SDA:觸摸芯片的IIC 通信引腳
SCL:觸摸芯片的IIC 通信引腳
RSTN:觸摸芯片的復(fù)位引腳
INT:觸摸芯片的中斷引腳
對應(yīng)板子原理圖的觸摸接口如下:

對應(yīng)屏幕原理圖的觸摸接口如下:

2 編寫觸摸驅(qū)動代碼
觸摸芯片用到IIC通信,還要用到復(fù)位引腳和中斷引腳,因此需要先在設(shè)備樹中對引腳信息進行配置。
2.1 修改設(shè)備樹
修改imx6ull_myboard.dts文件。
在設(shè)備樹中把觸摸要用到的引腳追加到 iomuxc即可。

需要注意的是,SNVS_TAMPER9 引腳被復(fù)用為 GPIO5_IO09,需要追加到 iomuxc_snvs 節(jié)點。
2.1.1 IIC引腳
觸摸芯片用到的是IIC1,這兩個引腳在設(shè)備樹中以及默認(rèn)添加了,無需修改:

2.1.2 復(fù)位引腳
&iomuxc節(jié)點中添加:
/*my gt911*/
pinctrl_tsc_reset: tscresetgrp {
? ?fsl,pins = <
? ? ? ?/* used for tsc reset */
? ? ? ?MX6UL_PAD_LCD_RESET__GPIO3_IO04 0x10b0
? ? ? ?>;
};

2.1.3 中斷引腳
&iomuxc_snvs節(jié)點中添加:
/*my gt911*/
pinctrl_tsc_irq: tsc_irq {
? ?fsl,pins = <
? ? ? ?/* used for tsc irq */
? ? ? ?MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 ? 0x4001b8b0
? ? ? ?>;
}

2.1.4 IIC設(shè)備添加GT911
GT911觸摸驅(qū)動作為一個IIC設(shè)備掛載在IIC1總線上,找到IIC1節(jié)點:

需要在 IIC1 設(shè)備節(jié)點下追加相應(yīng)的子節(jié)點:
gt911_tsc@5d {
? ?compatible = "goodix,gt911";
reg = <0x5d>;
? ?pinctrl-0 = <&pinctrl_tsc_reset>;
? ?pinctrl-1 = <&pinctrl_tsc_irq>;
? ?reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
? ?irq-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>;
? ?interrupt-parent = <&gpio5>;
? ?interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
};
修改后:

reg = <0x5d>是GT911觸摸芯片在 IIC1總線上的地址。
2.2 觸摸芯片數(shù)據(jù)寄存器
查看GT911的數(shù)據(jù)手冊,找到寄存器相關(guān)的表格:

主要關(guān)注以下這些寄存器,它們是用來讀取觸摸坐標(biāo)點的:
0x814E:這個寄存器很重要,它是可讀可寫的寄存器,通過讀取該寄存器,可以知道當(dāng)前是否有觸摸點(由最高位表示),以及有幾個觸摸點(由低3位表示)
0x814F~0x8156:是第一組觸摸的坐標(biāo)數(shù)據(jù)
0x814F:是觸摸點的追蹤id,GT911支持5點觸摸,這里id的取值為0~4
0x8150:觸摸點1的x坐標(biāo)(低字節(jié)) ?
0x8151:觸摸點1的x坐標(biāo)(高字節(jié))y
0x8152:觸摸點1的y坐標(biāo)(低字節(jié)) ?
0x8153:觸摸點1的y坐標(biāo)(高字節(jié))
0x8154~0x8156:暫不使用
0x8157以后的寄存器:與第一組觸摸的坐標(biāo)數(shù)據(jù)的含義類似,一個有五組
注:GT911支持硬件追蹤觸摸點,因此為每個觸摸點提供了一個track id,舉個簡單的例子,當(dāng)5個手指依次觸摸到屏幕時,5組坐標(biāo)寄存器中的track id會依次是0、1、2、3、4,當(dāng)松開第1個手指時,即track id為0的點沒有了,此時5組坐標(biāo)寄存器,是只有前45組坐標(biāo)寄存器有數(shù)據(jù),track id會依次是1、2、3、4(理解這個很重要,因為我一開始想當(dāng)然的誤認(rèn)為,移開第1個手指時,是第1組坐標(biāo)寄存器沒數(shù)據(jù)了)
2.3 編寫驅(qū)動程序
新建gt911.c文件作為驅(qū)動文件
觸摸芯片GT911的使用,本質(zhì)是使用IIC通信,進行數(shù)據(jù)的讀寫,因為觸摸屏的驅(qū)動,實際就是IIC驅(qū)動。另外,觸摸的數(shù)據(jù)是通過中斷的方式觸發(fā)的,因此觸摸驅(qū)動的編寫,涉及到中斷的處理。在中斷時,讀取到觸摸數(shù)據(jù)后,要傳遞到應(yīng)用層,這里是使用Linux的input子系統(tǒng)(這也是Linux的一種軟件分層設(shè)計的方式)。
所以,編寫觸摸驅(qū)動,主要涉及3點:
IIC協(xié)議的驅(qū)動
中斷的處理(獲取觸摸數(shù)據(jù))
input子系統(tǒng)(將觸摸數(shù)據(jù)傳遞到應(yīng)用層)
2.3.1 IIC驅(qū)動架構(gòu)
GT911的驅(qū)動按照IIC驅(qū)動來寫,當(dāng)驅(qū)動運行時,會自動運行gt911_probe,在這個函數(shù)中會進行各種初始化操作。另外注意匹配列表,這里的“goodix,gt911”對應(yīng)設(shè)備樹中添加的設(shè)備節(jié)點,兩處的名字要一致。
/* 匹配列表 */
static const struct of_device_id gt911_of_match[] = {
? ?{.compatible = "goodix,gt911"},
? ?{/* Sentinel */}
};
/* i2c驅(qū)動結(jié)構(gòu)體 */
struct i2c_driver gt911_i2c_driver = {
? ?.driver = {
? ? ? ?.owner = THIS_MODULE,
? ? ? ?.name = "gt911", ? /* 驅(qū)動名字 用于和設(shè)備匹配 ?適用于沒有設(shè)備樹的情況*/
? ? ? ?.of_match_table =gt911_of_match, ?/* 設(shè)備樹匹配列表 */
? ?},
? ?.probe =gt911_probe,
? ?.remove =gt911_remove,
? ?.id_table = gt911_id, /* id配置列表 */
};
2.3.2 驅(qū)動的初始化流程
gt911_probe函數(shù)進行觸摸驅(qū)動的初始化,基本流程就是從設(shè)備樹獲取觸摸節(jié)點,然后進行IO的初始化,中斷和復(fù)位的初始化以及觸摸器件的初始化等。
static int gt911_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
? ?u8 ret = 0;
? ?gt911.client = client;
? ?printk("[BSP] gt911 driver and device has match!\r\n");
? ?/* 獲取設(shè)備樹中的中斷和復(fù)位引腳 */
? ?printk("[BSP] get gpios\r\n");
? ?gt911.irq_pin ? = of_get_named_gpio(client->dev.of_node, "irq-gpios", 0);
? ?gt911.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
? ?/* 初始化復(fù)位引腳 */
? ?ret = gt911_ts_reset(client, >911);
? ?/* 初始化gt911 */
? ?printk("[BSP] init gt911\r\n");
? ?gt911_write_reg(>911, GT_CTRL_REG, 2); ?/* 軟復(fù)位 */
? ?mdelay(100);
? ?gt911_write_reg(>911, GT_CTRL_REG, 0); ?/* 停止軟復(fù)位 */
? ?mdelay(100);
? ?/* input 注冊設(shè)備*/
? ?printk("[BSP] init input device\r\n");
? ?gt911.input ?= devm_input_allocate_device(&client->dev);
? ?
? ?/* 初始化input */
? ?gt911.input->name = client->name;
? ?gt911.input->id.bustype = BUS_I2C;
? ?gt911.input->dev.parent = &client->dev;
? ?
? ?/* 設(shè)置input設(shè)備需要上報哪些事件*/
? ?__set_bit(EV_SYN, gt911.input->evbit);
? ?__set_bit(EV_KEY, gt911.input->evbit); ? ? ?/* 按鍵事件 */
? ?__set_bit(EV_ABS, gt911.input->evbit); ? ? ?/* 重復(fù)事件 */
? ?/* 設(shè)置input設(shè)備需要上報哪些按鍵*/
? ?__set_bit(BTN_TOUCH, gt911.input->keybit); ?/* 觸摸值 */
? ?
? ?/* 多點觸摸 */
? ?input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0); ?/*觸摸點的數(shù)量 */
? ?input_set_abs_params(gt911.input, ABS_MT_POSITION_X,0, 800, 0, 0);
? ?input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,0, 480, 0, 0);
? ?/* 注冊input */
? ?ret = input_register_device(gt911.input);
? ?
? ?/* 最后初始化中斷 */
? ?ret = gt911_ts_irq(client, >911);
? ?
? ?printk("[BSP] %s done \r\n",__FUNCTION__);
? ?return 0;
}
2.3.3 復(fù)位與中斷的初始化
復(fù)位引腳的初始化主要就是拉低再拉高復(fù)位引腳,實現(xiàn)復(fù)位,主要內(nèi)容為:
/* 申請復(fù)位IO 并且默認(rèn)輸出高電平 */
devm_gpio_request_one(&client->dev,
? ? ? ? ? ? ? ? ? ? ?dev->reset_pin,
? ? ? ? ? ? ? ? ? ? ?GPIOF_OUT_INIT_HIGH,
? ? ? ? ? ? ? ? ? ? ?"gt911 reset");
gpio_set_value(dev->reset_pin, 0); ?/* 復(fù)位 */
msleep(10);
gpio_set_value(dev->reset_pin, 1); ?/* 停止復(fù)位 */
msleep(300);
中斷的初始化,包括IO的申請和中斷函數(shù)的注冊:
/* 申請復(fù)位 IO */
devm_gpio_request_one(&client->dev,
? ? ? ? ? ? ? ? ? ? ?dev->irq_pin,
? ? ? ? ? ? ? ? ? ? ?GPIOF_IN,
? ? ? ? ? ? ? ? ? ? ?"gt911 irq");
/* 申請中斷 ?*/
devm_request_threaded_irq(&client->dev,
? ? ? ? ? ? ? ? ? ? ? ? ?client->irq,
? ? ? ? ? ? ? ? ? ? ? ? ?NULL,
? ? ? ? ? ? ? ? ? ? ? ? ?gt911_irq_handler, /* 中斷處理函數(shù) */
? ? ? ? ? ? ? ? ? ? ? ? ?IRQF_TRIGGER_FALLING | IRQF_ONESHOT, /* 觸發(fā)方式 */
? ? ? ? ? ? ? ? ? ? ? ? ?client->name,
? ? ? ? ? ? ? ? ? ? ? ? ?>911);
2.3.4 中斷處理函數(shù)
這里僅貼出gt911_irq_handler的主要內(nèi)容,基本思路是先讀取0x814E(GT_GSTID_REG)這一個寄存器,判斷觸摸點的數(shù)量,然后再讀取對應(yīng)的坐標(biāo)點數(shù)據(jù)寄存器,依次上報數(shù)據(jù)。
/* -----讀取觸摸信息寄存器----- */
ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1);
if(data == 0x00) /* 沒有觸摸數(shù)據(jù)*/
{ ?
? ?goto fail;
}
else
{ ?/* 統(tǒng)計觸摸信息 */
? ?status ? ? ? = data >> 7; ? ? ? ? ?// bit7:1表示坐標(biāo)(或按鍵)已經(jīng)準(zhǔn)備好,主控可以讀取 ?0 表示未就緒,數(shù)據(jù)無效
? ?large_detect = (data >> 6) & 0x01; // bit6:
? ?touch_num ? ?= data & 0x0f; ? ? ? ?// bit3~0:屏上的坐標(biāo)點個數(shù)
}
if(touch_num) /* 有觸摸按下 */
{
? ?/* -----讀取具體的觸摸點數(shù)據(jù)寄存器----- */
? ?gt911_read_regs(dev, GT_TP1_REG, buf, BUFFER_SIZE);
? ?id = buf[0]; // 數(shù)據(jù)中的第一個觸摸點的id
? ?touch_index |= (0x01<<id);
? ?/* 上報每一個觸摸點坐標(biāo) */
? ?for (i = 0; i < 5; i++)
? ?{
? ? ? ?if ((touch_index & (0x01<<i)))
? ? ? ?{
? ? ? ? ? ?input_x = (buf[pos + 1] | (buf[pos + 2] << 8)) & 0x0fff; ?// x坐標(biāo)
? ? ? ? ? ?input_y = (buf[pos + 3] | (buf[pos + 4] << 8)) & 0x0fff; ?// y坐標(biāo)
? ? ? ? ? ?input_mt_slot (dev->input, id); // 產(chǎn)生ABS_MT_SLOT 事件 報告是哪個觸摸點的坐標(biāo)
? ? ? ? ? ?input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true); ? // 指定手指觸摸 ?連續(xù)觸摸
? ? ? ? ? ?input_report_abs(dev->input, ABS_MT_POSITION_X, input_x); ? // 上報觸摸點坐標(biāo)信息
? ? ? ? ? ?input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y); ? // 上報觸摸點坐標(biāo)信息
? ? ? ? ? ?printk("[%d](%d, %d) ", id, input_x, input_y);
? ? ? ? ? ?report_num++;
? ? ? ? ? ?if (report_num < touch_num)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?pos += 8;
? ? ? ? ? ? ? ?id = buf[pos];
? ? ? ? ? ? ? ?touch_index |= (0x01<<id);
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?else
? ? ? ?{
? ? ? ? ? ?input_mt_slot(dev->input, i);
? ? ? ? ? ?input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); ? // 關(guān)閉手指觸摸
? ? ? ?}
? ?}
? ?printk("\r\n");
}
else if(last_index)/* 觸摸釋放 */
{
? ?for (i = 0; i < 5; i++)
? ?{
? ? ? ?if ((last_index & (0x01<<i)))
? ? ? ?{
? ? ? ? ? ?input_mt_slot(dev->input, i); /* 上報觸摸點 */
? ? ? ? ? ?input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); // 關(guān)閉手指觸摸
? ? ? ?}
? ?}
}
last_index = touch_index;
input_mt_report_pointer_emulation(dev->input, true);
input_sync(dev->input); ? /* 同步數(shù)據(jù) 數(shù)據(jù)上報完成 */
data = 0x00; ?/* 向0x814E寄存器寫0 ?不然就會一直進入中斷 */
gt911_write_regs(dev, GT_GSTID_REG, &data, 1); ? //寫入
總結(jié)一下GT911多點觸摸驅(qū)動的執(zhí)行流程:

3 使用Linux內(nèi)核自帶的驅(qū)動(未測試)
對于觸摸屏的驅(qū)動,NXP已經(jīng)編寫好了觸摸驅(qū)動,加以修改可以在自己的板子上使用。
不過我沒有測試成功,以后有時間再搞,所以這一部分內(nèi)容可以跳過。
我這個7寸屏的驅(qū)動型號為GT911,屬于 GOODIX 公司生產(chǎn)的觸摸芯片,該觸摸驅(qū)動已默認(rèn)添加到了Linux內(nèi)核中,位于:/drivers/input/touchscreen/goodix.c。

使用Linux內(nèi)核自代的驅(qū)動,還需要進行內(nèi)核配置。在Linux內(nèi)核源碼目錄,輸入以下指令打開內(nèi)核的圖形化配置:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
到達(dá)Linux內(nèi)核配置界面,然后按下路徑找到對應(yīng)的配置項:
-> Device Drivers
-> Input device support ? ? ? ?
?-> Touchscreens (INPUT_TOUCHSCREEN [=y])
? <*> ? Goodix I2C touchscreen ?
最終到達(dá)這個界面:

按下y勾選上星號,連按多次ESC退出,最后提示保存,按下y保存配置。
然后需要重新編譯zImage和設(shè)備樹,到Linux內(nèi)核源碼目錄,執(zhí)行之前的編寫的編譯腳本
./build_myboard.sh
編譯的時候會彈出Linux圖形配置界面, 不需要做任何的配置, 直接按兩下ESC鍵退出圖形界面
將編譯出zImage(arch/arm/boot目錄)和imx6ull-myboard.dtb (arch/arm/boot/dts目錄)復(fù)制到網(wǎng)絡(luò)啟動位置
cp arch/arm/boot/zImage ~/myTest/tftpboot/nxp/
cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
4 觸摸測試
使用自己編寫的觸摸驅(qū)動程序,進行測試。
4.1 編譯設(shè)備樹
首先是編譯設(shè)備樹,驗證添加的觸摸節(jié)點是否工作正常,在Linux內(nèi)核源碼目錄執(zhí)行下面的命令,重新編譯設(shè)備樹并拷貝到網(wǎng)絡(luò)啟動位置。
make imx6ull-myboard.dtb
cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
然后重啟開發(fā)板,可以先到如下位置,查看設(shè)備樹的節(jié)點是否正常:

4.2 編譯驅(qū)動文件
然后是編譯驅(qū)動文件,也就是gt911.c,編譯方式和之前一樣,在ubuntu中使用Makefile進行交叉編譯。
本篇暫未用到對應(yīng)的觸摸應(yīng)用程序,所有的觸摸坐標(biāo)打印都是在驅(qū)動程序中通過printk的方式進行內(nèi)核打印。
編譯完驅(qū)動后,將對應(yīng)的.ko文件復(fù)制到板子中。
4.3 測試觸摸點的坐標(biāo)輸出
先加載觸摸驅(qū)動,串口會打印出為觸摸分配的event,我這里是event2。
然后執(zhí)行下面的指令進行觸摸測試:
hexdump /dev/input/event2

將手指放到屏幕上,就可以在LCD屏幕上看到坐標(biāo)值的打印,比如將手指放到屏幕左下角,對應(yīng)輸出的值大致就是屏幕的最大位置(800,480):

GT911支持多點觸摸,驅(qū)動程序中也對多點數(shù)據(jù)進行了獲取和打印,將多個手指放到屏幕上,可以看到最多有5個觸摸點的坐標(biāo)打?。?/p>
5 總結(jié)
本篇主要介紹了多點觸摸芯片GT911的驅(qū)動編寫與使用,并通過將觸摸點實時打印的方式,測試觸摸功能。
