AM335x(TQ335x)學習筆記——觸控式螢幕驅動編寫
前面幾篇文章已經通過配置DTS的方式完成了多個驅動的移植,接下來我們解決TQ335x的觸控驅動問題。由於種種原因,TQ335x的觸控式螢幕驅動是以模組方式提供的,且Linux官方核心中也沒有帶該觸控式螢幕的驅動原始碼,單純的配置DTS是無法完成TQ335x的觸控驅動移植工作的,因此,本文參考核心中原有的pixcir_i2c_ts驅動編寫TQ335x的觸控式螢幕(TN92)驅動。
在之前移植TQ210時,我已經編寫過TQ210的觸控式螢幕驅動,我的TQ335x還是使用的TQ210的屏,因此,難度不是很大。這裡需要說明一點,在TQ210驅動移植時對多點觸控協議的理解還不夠深入,當時編寫的驅動單點觸控是可以正常使用的,但是多點觸控不對(這次編寫TQ335x的觸控驅動是才意識到的)。但是編寫的TQ210驅動多點觸控實際上使用的多點觸控的A協議,但是用錯了一些地方,本文基於TQ335x的重新編寫的觸控驅動是按照多點觸控B協議編寫,使用tslib測試正常,文章末尾有效果圖。
TN92觸控式螢幕使用的觸控晶片是GT811,下面我們來分析下觸控式螢幕驅動的編寫。
(1) 檢視原理圖
從觸控式螢幕原理圖中可以看到,GT811與開發板相連的引腳有四條,分別是SDA、SDL、INT和RESET。其中,I2C_SDA和I2C_SDL是連線到AM335x的I2C1埠上的,用來與SoC通訊;INT引腳是連線在GPIO1的27號引腳上的,在檢測到觸控時GT811通過該引腳向SoC發起中斷請求;RESET引腳接到SoC的GPIO1的26號引腳上的,是用來接收SoC復位操作,對於本文,SoC就是AM335x。
其中,GT811與SoC的管腳連線資訊可以從底板原理圖中找到,SDA和SCL的我就不往外貼了,INT和RESET的連線關係如下:
YP連線到了GT811的RESET腳上,然後通過短路帽與GPIO1_26連結,YM連線到GT811的INT腳上,通過短路帽與GPIO1_27連結,因此,需要將GPIO1_27配置為終端輸入引腳,GPIO1_26配置為輸出引腳。此外,檢視GT811的晶片手冊可獲得INT和RESET的操作資訊:
1. GT811檢測到觸控時會拉低中斷引腳,因此,GPIO1_27需要配置為下降沿觸發。
2. GT811的RESET腳為低電平有效,因此,上電時需要拉低RESET引腳。
3. GT811的RESET引腳自帶上拉,因此,使用GPIO1_26將GT811的RESET拉低復位後切換為懸浮輸入太即可。
瞭解這些資訊後我們就可以開始分析驅動結構了。(2) DTS配置Platform資訊
通過前面的分析,我們知道需要配置AM335x的四條引腳才能使GT811正常工作。其中,GT811通過I2C介面連線到AM335x的I2C1上,因此,需要配置AM335x的I2C1的兩條引腳為I2C功能;GPIO1_27需要配置為中斷輸入、下降沿觸發,中斷號也可以確定下來了,就是GPIO1的27號角(核心能將引腳轉換為中斷號);最後就是RESET腳,驅動初始化GT811時需要操作RESET引腳,且GT811為拉低復位,故可將GPIO1的26角設為輸出電平狀態。通過前面幾篇文章的學習,我們知道DTS可以配置pinmux,然後分析核心自帶的DTS檔案可以,DTS也可以將連線資訊傳遞給核心。經過分析及參考,我最TQ335x.dts檔案做了如下修改:
Step1. 檢查I2C引腳的pinmux配置
查詢i2c1可以找到i2c1節點,該節點的pinctrl-0只想的phandler對i2c1的兩個引腳進行了相關的配置,因此,不需任何修改。
Step2. 配置INT和RESET引腳
在am33xx_pinmux節點內新增引腳配置資訊,具體內容如下:
gt811_ts_pins: gt811_ts_pins {
pinctrl-single,pins = <
0x68 (PIN_INPUT_PULLUP | MUX_MODE7)
0x6c (PIN_INPUT_PULLUP | MUX_MODE7)
>;
};
Step3. 在i2c1節點內新增GT811裝置資訊
GT811的裝置節點內需要提供以下資訊,i2c裝置地址、pinmux配置、中斷資訊、螢幕大小等,具體如下(這裡就不細說了,有什麼不清楚的可以留言討論)。
[email protected] {
compatible = "gt811,gt811_ts";
pinctrl-names = "default";
pinctrl-0 = <>811_ts_pins>;
reg = <0x5d>;
interrupt-parent = <&gpio1>;
interrupts = <27 2>;
gpios = <&gpio1 26 0>;
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
touchscreen-swap = <1>;
touchscreen-revert-x = <1>;
touchscreen-revert-y = <1>;
};
至此,DTS的配置工作就完成了,下面就是GT811的驅動編寫。(3)驅動編寫
驅動編寫根之前TQ210相比,沒有多少變化,都是採用的新式I2C裝置驅動架構,重要的區別在於支援了多點觸控,驅動的詳細分析我就不多說了,具體可以參考韋東山老師的視訊教程(絕對物有所值)。下面是GT811的多點觸控驅動原始碼。從GT811裝置節點中獲取座標資訊的部分我直接貼出來了,完整的程式碼還是請到資源裡下載,還是有點貴哈,不過這個驅動可以直接拿去使用了,不需要任何修改。下面是不完整的程式碼,可以打印出觸控點座標,該程式碼已經把本驅動的核心的部分都寫出來了。其實很多朋友不需要下載原始碼就可以補充完善這個驅動了,而且我真心的希望閱讀本文的朋友不需要下載原始碼就能自己寫出觸控驅動。
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
struct gt811_ts_platdata
{
u32 size_x;
u32 size_y;
u32 size_p;
u32 swap;
u32 revert_x;
u32 revert_y;
u32 reset_pin;
u32 interrupt_pin;
u32 ponits_max;
struct i2c_client *client;
struct input_dev *input;
struct work_struct work;
};
static const struct of_device_id gt811_ts_of_match[] = {
{ .compatible = "gt811,gt811_ts", .data = NULL },
{ }
};
static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, int len){
struct i2c_msg msg;
msg.flags=!I2C_M_RD;
msg.addr=client->addr;
msg.len=len;
msg.buf=data;
return i2c_transfer(client->adapter,&msg, 1);
}
static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len){
struct i2c_msg msgs[2];
msgs[0].flags=!I2C_M_RD;
msgs[0].addr=client->addr;
msgs[0].len=2;
msgs[0].buf=&buf[0];
msgs[1].flags=I2C_M_RD;
msgs[1].addr=client->addr;
msgs[1].len=len-2;
msgs[1].buf=&buf[2];
return i2c_transfer(client->adapter,msgs, 2);
}
static void gt811_ts_handler(struct work_struct *work)
{
struct gt811_ts_platdata *pdata = container_of(work, struct gt811_ts_platdata, work);
struct device *dev = &pdata->client->dev;
uint8_t buffer[36] = {0x07, 0x21, 0};
uint8_t count, index, flags, position;
int x, y;
buffer[0] = 0x0f;
buffer[1] = 0xff;
if (i2c_write_bytes(pdata->client,buffer,2) < 0) {
dev_err(dev, "Failed to write wakeup message.\n");
goto reenable_irq;
}
buffer[0] = 0x07;
buffer[1] = 0x21;
if (i2c_read_bytes(pdata->client, buffer, sizeof(buffer)) < 0) {
dev_err(dev, "Failed to read touch message.\n");
goto reenable_irq;
}
buffer[0] = 0x80;
buffer[1] = 0x00;
if (i2c_write_bytes(pdata->client, buffer, 2) < 0) {
dev_err(dev, "Failed to write sleep message.\n");
goto reenable_irq;
}
buffer[25] = buffer[19];
buffer[19] = 0;
flags = buffer[2]&0x1f;
while (flags) {
if (!(flags&0x1)) {
continue;
}
if (index < 3) {
position = 4 + index * 5;
}
else{
position = 25 + (index - 3) * 5;
}
x = (buffer[position] << 8) | buffer[position + 1];
y = (buffer[position + 2] << 8) | buffer[position + 3];
if(pdata->swap) {
swap(x, y);
}
if(pdata->revert_x){
x = pdata->size_x - x;
}
if(pdata->revert_y){
y = pdata->size_y - y;
}
printk("point:(x:%03d, y:%03d)\n", x, y);
}
// 組織檢測出來的觸控點資訊上報到輸入子系統節點即可
reenable_irq:
enable_irq(pdata->client->irq);
}
static irqreturn_t gt811_ts_isr(int irq, void *dev_id)
{
struct gt811_ts_platdata* pdata = (struct gt811_ts_platdata*)dev_id;
disable_irq_nosync(pdata->client->irq);
schedule_work(&pdata->work);
return IRQ_HANDLED;
}
static int gt811_ts_initilize(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct gt811_ts_platdata *pdata = (struct gt811_ts_platdata*)i2c_get_clientdata(client);
int status = 0, count = 0;
uint8_t version[4] = {0x7, 0x17, 0};
uint8_t config[] = {
0x06,0xA2,
0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0xE2,0x53,0xD2,0x53,0xC2,0x53,
0xB2,0x53,0xA2,0x53,0x92,0x53,0x82,0x53,0x72,0x53,0x62,0x53,0x52,0x53,0x42,0x53,
0x32,0x53,0x22,0x53,0x12,0x53,0x02,0x53,0xF2,0x53,0x0F,0x13,0x40,0x40,0x40,0x10,
0x10,0x10,0x0F,0x0F,0x0A,0x35,0x25,0x0C,0x03,0x00,0x05,0x20,0x03,0xE0,0x01,0x00,
0x00,0x34,0x2C,0x36,0x2E,0x00,0x00,0x03,0x19,0x03,0x08,0x00,0x00,0x00,0x00,0x00,
0x14,0x10,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x40,
0x30,0x3C,0x28,0x00,0x00,0x00,0x00,0xC0,0x12,0x01
};
config[62] = 480 >> 8;
config[61] = 480 & 0xff;
config[64] = 800 >> 8;
config[63] = 800 & 0xff;
if (!gpio_is_valid(pdata->reset_pin)) {
dev_err(dev, "The reset pin number is invalid.\n");
return -EINVAL;
}
count = 3;
while (count--) {
gpio_direction_output(pdata->reset_pin, 0);
msleep(10);
gpio_direction_output(pdata->reset_pin, 1);
msleep(100);
if (i2c_read_bytes(client, version, sizeof(version)) < 0) {
dev_err(dev, "Failed to get the version of GT811, try again...\n");
status = -ENODEV;
}
else {
dev_info(dev, "Gt811 detected, version(%04x)...\n", (version[2]<<8)|version[3]);
status = 0;
break;
}
}
if (status) {
return status;
}
count = 3;
while (count--) {
if (i2c_write_bytes(client, config, sizeof(config)) < 0) {
dev_err(dev, "Failed to configure the GT811, try again...\n");
status = -EINVAL;
}
else {
dev_info(dev, "Gt811 configue succeed\n");
status = 0;
break;
}
}
return status;
}
static struct gt811_ts_platdata *gt811_ts_parse_devtree(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *node;
struct gt811_ts_platdata *pdata;
enum of_gpio_flags flags;
node = dev->of_node;
if (!node) {
dev_err(dev, "The of_node is NULL.\n");
return ERR_PTR(-ENODEV);
}
pdata = devm_kzalloc(dev, sizeof(struct device_node), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "No enough memory left.\n");
return ERR_PTR(-ENOMEM);
}
pdata->reset_pin = of_get_gpio_flags(node, 0, &flags);
if (pdata->reset_pin < 0) {
dev_err(dev, "Get RST pin failed!\n");
return ERR_PTR(-EINVAL);
}
if (of_property_read_u32(node, "touchscreen-size-x", &pdata->size_x )) {
dev_err(dev, "Failed to get the touch screen x size.\n");
return ERR_PTR(-EINVAL);
}
if (of_property_read_u32(node, "touchscreen-size-y", &pdata->size_y)) {
dev_err(dev, "Failed to get the touch screen y size.\n");
return ERR_PTR(-EINVAL);
}
if (of_property_read_u32(node, "touchscreen-size-p", &pdata->size_p)) {
pdata->size_p = 255;
}
if (of_property_read_u32(node, "touchscreen-swap", &pdata->swap)) {
pdata->swap = 1;
}
if (of_property_read_u32(node, "touchscreen-revert-x", &pdata->revert_x)) {
pdata->revert_x = 1;
}
if (of_property_read_u32(node, "touchscreen-revert-x", &pdata->revert_y)) {
pdata->revert_y = 1;
}
return pdata;
}
static int gt811_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct gt811_ts_platdata *pdata = dev_get_platdata(dev);
struct input_dev *input;
int error = 0;
if (!of_match_device(of_match_ptr(gt811_ts_of_match), dev)) {
dev_err(dev, "Failed to match.\n");
return -EINVAL;
}
if (!pdata) {
pdata = gt811_ts_parse_devtree(client);
if (IS_ERR(pdata)) {
dev_err(dev, "Get device data from device tree failed!\n");
error = -EINVAL;
goto failed_exit;
}
}
pdata->client = client;
i2c_set_clientdata(client, pdata);
input = devm_input_allocate_device(dev);
if (!input) {
dev_err(dev, "Failed to allocate input device\n");
error = -ENOMEM;
goto pdata_free;
}
pdata->input = input;
input->name = client->name;
input->id.bustype = BUS_I2C;
input->id.product = 0xBEEF;
input->id.vendor =0xDEAD;
input->dev.parent = &client->dev;
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
input_set_abs_params(input, ABS_X, 0, pdata->size_x, 0, 0);
input_set_abs_params(input, ABS_Y, 0, pdata->size_y, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->size_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->size_y, 0, 0);
error = input_mt_init_slots(input, 5, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(dev, "Failed to initialize the multi-touch slots.\n");
goto input_free;
}
input_set_drvdata(input, pdata);
error = input_register_device(input);
if (error) {
dev_err(dev, "Register input device failed!\n");
goto input_free;
}
if (gt811_ts_initilize(client)) {
dev_err(dev, "Failed to initialize GT811.\n");
}
INIT_WORK(&pdata->work, gt811_ts_handler);
error = devm_request_any_context_irq(dev, client->irq, gt811_ts_isr,
IRQF_TRIGGER_FALLING, client->name, pdata);
if (error) {
dev_err(dev, "Failed to request irq(number:%d)\n", client->irq);
goto input_free;
}
return 0;
input_free:
devm_kfree(dev, input);
pdata_free:
devm_kfree(dev, pdata);
failed_exit:
return error;
}
static int gt811_ts_remove(struct i2c_client *client)
{
struct gt811_ts_platdata *pdata = (struct gt811_ts_platdata*)i2c_get_clientdata(client);
devm_free_irq(&client->dev, client->irq, i2c_get_clientdata(client));
input_unregister_device(pdata->input);
devm_kfree(&client->dev, pdata);
return 0;
}
static const struct i2c_device_id gt811_ts_id[] = {
{ "gt811_ts", 0 },
{ }
};
static struct i2c_driver gt811_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "gt811_ts",
.of_match_table = of_match_ptr(gt811_ts_of_match),
},
.probe = gt811_ts_probe,
.remove = gt811_ts_remove,
.id_table = gt811_ts_id,
};
module_i2c_driver(gt811_ts_driver);
MODULE_AUTHOR("girlkoo <[email protected]>");
MODULE_DESCRIPTION("Gt811 I2C Touchscreen Driver");
MODULE_LICENSE("GPL");
(4) 使用tslib工具測試
tslib的編譯方法請參考本部落格的另一片文章,連結如下:
本文就不再重複tslib的配置方法。
(5)效果展示
(6) 完整驅動程式碼
請到本人的資源中下載TQ335x的觸控式螢幕驅動原始碼,連結如下:
本文作者:girlkoo
相關推薦
AM335x(TQ335x)學習筆記——觸控式螢幕驅動編寫
前面幾篇文章已經通過配置DTS的方式完成了多個驅動的移植,接下來我們解決TQ335x的觸控驅動問題。由於種種原因,TQ335x的觸控式螢幕驅動是以模組方式提供的,且Linux官方核心中也沒有帶該觸控式螢幕的驅動原始碼,單純的配置DTS是無法完成TQ335x的觸控驅動移植工作
AM335x(TQ335x)學習筆記——LCD驅動移植
TI的LCD控制器驅動是非常完善的,共通的地方已經由驅動封裝好了,與按鍵一樣,我們可以通過DTS配置完成LCD的顯示。下面,我們來討論下使用DTS方式配置核心完成LCD驅動的思路。 (1)初步分析 由於TQ335x使用的晶片是AM335x,故仍然可以參考am335x-evm
S5PV210(TQ210)學習筆記——觸控式螢幕驅動編寫
電阻式觸控式螢幕的驅動比較簡單,可以採用輸入子系統驅動框架來編寫,而電容式觸控式螢幕的驅動程式相對比較複雜,因為電容觸控一般採用I2C引腳才控制,我在自己編寫電容觸控驅動的時候鬱悶了好幾天,當然,並不是因為I2C電容觸控驅動繁瑣,主要是天嵌TQ210的觸控式螢幕驅動程式是以模組方式提供的,並不開發原始
AM335x(TQ335x)學習筆記——USB驅動移植
對於AM335x來講,TI維護的USB驅動已經非常完善了,本文稱之為移植,實際上僅僅是配置核心選項使能USB HOST/OTG功能。廢話少說,直接動手開啟AM335x的USB驅動配置項。 Step1. 配置核心支援USB 預設的配置項沒有配置USB相關的選項,但是DTS已
AM335x(TQ335x)學習筆記——WM8960音效卡驅動移植
經過一段時間的除錯,終於調好了TQ335x的音效卡驅動。TQ335x採用的Codec是WM8960,本文來總結下WM8960驅動在AM335x平臺上的移植方法。Linux音效卡驅動架構有OSS和ALSA兩種架構,目前最常用的架構是ALSA,本文也使用ALSA架構對WM
AM335x(TQ335x)學習筆記——GPIO按鍵驅動移植
還是按照S5PV210的學習順序來,我們首先解決按鍵問題。TQ335x有六個使用者按鍵,分別是上、下、左、右、Enter和ESC。開始我想到的是跟學習S5PV210時一樣,編寫輸入子系統驅動解決按鍵問題,但是瀏覽driver/input/keyboard目錄時意外的發現了g
AM335x(TQ335x)學習筆記——u-boot-2014.10移植
最近移植了下u-boot-2014.10到TQ335x,如果基於am335x evm進行移植,需要修改的地方並不多。 由於TI的am335x evm開發使用了一個eeprom儲存了板載配置資訊,用來區分不同板子的型號的,而TQ335x沒有這個eeprom,因此,需要修改ee
【迅為iTop4412學習筆記】14.編寫一個LED驅動
宣告 以下都是我剛開始看驅動視訊的個人強行解讀,如果有誤請指出,共同進步。 本節目標 編寫一個LED驅動 本節我們就真正的來驅動一個板子上的LED(微控制器第一節就是點燈,沒想到Linux要學這麼久…) 我們首先理清思路。 註冊裝
【迅為iTop4412學習筆記】9.編寫簡單應用呼叫驅動
宣告 以下都是我剛開始看驅動視訊的個人強行解讀,如果有誤請指出,共同進步。 本節目標 編寫簡單應用呼叫驅動 編寫應用就和我們平時寫c檔案差不多了。 首先要包含一些標頭檔案 // 這不解釋 #include <stdio.h>
【知了堂學習筆記】java 編寫幾種常見排序算法
第一個 public 調用 ati print 所有 eth string quick 排序的分類: 一.交換排序 所謂交換,就是根據序列中兩個記錄鍵值的比較結果來對換這兩個記錄在序列中的位置,交換排序的特點是:將鍵值較大的記錄向序列的尾部移動,鍵值較小的記錄向序列的前部
ROS學習筆記16(編寫簡單的訊息釋出器和訂閱器 (Python))
1 編寫釋出者節點 “節點”是連線到ROS網路的可執行檔案ROS術語。在這裡,我們將建立一個持續廣播訊息的釋出者(“talker”)節點。 將目錄更改為您在早期教程中建立的的beginner_tutorials包,並建立一個包: $ roscd beginner_tut
ROS學習筆記18 (編寫簡單的伺服器和客戶端 (C++))
1 編寫Service節點 這裡,我們將建立一個簡單的service節點("add_two_ints_server"),該節點將接收到兩個整型數字,並返回它們的和。 進入先前你在catkin workspace教程中所建立的beginner_tutorials包所在的目錄
ROS學習筆記19 (編寫簡單的伺服器和客戶端 (Python))
1 編寫服務端節點 我們會建立服務端節點 ("add_two_ints_server") ,節點接收兩個整型數字,並返回和 進入beginner_tutorials包 $ roscd beginner_tutorials 確保你確保已經在之前建立好AddTwoInts
ARM學習筆記之驅動程式篇七----字元裝置驅動
1.11 字元裝置驅動模型 在任何一種驅動模型中,裝置都會用核心中的一種結構來描述。字元裝置在核心中使用struct cdev來描述。 struct cdev{ struct kobject kobj; struct module *owner; const stru
STM32學習筆記-FSMC驅動LCD
文章目錄 一、FSMC原理 1.為什麼可以用FSMC驅動LCD? 2.定址問題 3.時序模式 二、程式碼部分 一、FSMC原理 大容量,且引腳數在 100 腳以上的 STM32F103 晶片都帶有 FS
MongoDB學習筆記~官方驅動巢狀陣列物件的更新
回到目錄 對於陣列物件mongodb本身是支援的,不過對於陣列的更新,mongodb的Csharp驅動目前只支援一級,即你的物件裡包含陣列,而陣列又包括陣列,這表示兩層,這在更新子陣列時,Csharp驅動是不支援的,今天要說的就是如何讓它支援子陣列的更新,下面是我給出的資料結構 在Mongodb的Cs
MongoDB學習筆記~官方驅動的原生Curd操作
回到目錄 MongoDB的官方C#驅動,讓我們使用起來也很方便,但對於Linq開發人員來說,可能有些不瞭解,所以,我還是將官方驅動進行了二次封裝,而對於一個比較個性化的mongo操作,使用我封裝的也很難實現,所以,還是需要大家直接使用官方驅動的原生寫法的,下面進行一個介紹。 Insert操作 mo
[RK3399][Android7.1] 學習筆記 DRM驅動程式開發(介紹)
平臺 核心版本 安卓版本 rk Linux4.4 Android7.1 1. 簡介 DRM 全稱是 Direct Rendering Mana
iQQ 學習筆記3 :編寫程式碼打包Ant指令碼
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project default="create_run_jar" name="Create Runnable Jar for Project test with librarie
新手學習筆記之Spring編寫AOP半自動代理
invoke proc pro aspect 新手 接口 src info nbsp 1.導包: 2.目標類 package oyb.service; public interface UserService { public void add(