i.MX6ULL终结者|i.MX6ULL终结者Linux 电容触摸屏实验实验程序编写


文章目录

    • 1 修改设备树文件
    • 2 编写多点电容触摸驱动
【i.MX6ULL终结者|i.MX6ULL终结者Linux 电容触摸屏实验实验程序编写】
1 修改设备树文件 1、添加FT5426的pinctrl信息
FT5426 触摸芯片用到了 4 个 IO,一个复位 IO、一个中断 IO、I2C2 的 SCL 和 SDA,所以我们需要先在设备树中添加 IO 相关的信息。复位 IO 和中断 IO 是普通的 GPIO,因此这两个 IO可以放到同一个节点下去描述,I2C2 的 SCL 和 SDA 属于 I2C2,因此这两个要放到同一个节点下去描述。首先是复位 IO 和中断 IO,topeet_emmc_4_3.dts 文件里面默认有个名为“pinctrl_tsc”的节点,如果被删除了的话就自行创建,在此节点下添加触摸屏的复位 IO 和中断 IO 信息,修改以后的“pinctrl_tsc”节点内容如下所示:
1 pinctrl_tsc: tscgrp { 2fsl,pins = < 3MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0 /* TSC_RST */ 4MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0xF080 /* TSC_INT */ 5>; 6 };

继续添加 I2C2 的 SCL 和 SDA 这两个 IO 信息,topeet_emmc_4_3.dts 里面默认就已经添加了 I2C2 的 IO 信息,这是 NXP 官方添加的,所以不需要我们去修改。找到“pinctrl_i2c1”节点,此节点就是用于描述 I2C2 的 IO 信息,节点内容如下所示:
1 pinctrl_i2c2: i2c2grp { 2fsl,pins = < 3MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0 4MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 5>; 6 };

最后在检查一下这四个引脚有没有被其他外设使用。如果有的话就需要屏蔽掉。
2、添加FT5426节点
FT5426 这个触摸 IC 挂载 I2C2 下,因此需要向 I2C2 节点下添加一个子节点,此子节点用于描述 FT5426,添加完成以后的 I2C2 节点内容如下所示:
1 &i2c2 { 2clock_frequency = <100000>; 3pinctrl-names = "default"; 4pinctrl-0 = <&pinctrl_i2c2>; 5status = "okay"; 6 7 /****************************/ 8 /* 省略掉其他的设备节点 */ 9 /****************************/ 10 11 12ft5426: ft5426@38 { 13compatible = "edt,edt-ft5426"; 14reg = <0x38>; 15pinctrl-names = "default"; 16pinctrl-0 = <&pinctrl_tsc>; 17interrupt-parent = <&gpio1>; 18interrupts = <9 0>; 19reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>; 20interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; 21}; 22 };

第 12 行,触摸屏所使用的 FT5426 芯片节点,挂载 I2C2 节点下,FT5426 的器件地址为0X38。
第 14 行,reg 属性描述 FT5426 的器件地址为 0x38。
第 16 行,pinctrl-0 属性描述 FT5426 的复位 IO 和中断 IO 所使用的节点为 pinctrl_tsc。
第 17 行,interrupt-parent 属性描述中断 IO 对应的 GPIO 组为 GPIO1。
第 18 行,interrupts 属性描述中断 IO 对应的是 GPIO1 组的 IOI09。
第 19 行,reset-gpios 属性描述复位 IO 对应的 GPIO 为 GPIO5_IO09。
第 20 行,interrupt-gpios 属性描述中断 IO 对应的 GPIO 为 GPIO1_IO09。
2 编写多点电容触摸驱动 本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/20_multitouch
创建ft5426.c驱动文件,内容如下:
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 17 #define MAX_SUPPORT_POINTS5/* 5点触摸 */ 18 #define TOUCH_EVENT_DOWN0x00/* 按下 */ 19 #define TOUCH_EVENT_UP0x01/* 抬起 */ 20 #define TOUCH_EVENT_ON0x02/* 接触 */ 21 #define TOUCH_EVENT_RESERVED0x03/* 保留 */ 22 23 /* FT5X06寄存器相关宏定义 */ 24 #define FT5X06_TD_STATUS_REG0X02/*状态寄存器地址 */ 25 #define FT5x06_DEVICE_MODE_REG0X00/* 模式寄存器 */ 26 #define FT5426_IDG_MODE_R0XA4/* 中断模式 */ 27 #define FT5X06_READLEN29/* 要读取的寄存器个数 */ 28 29 struct ft5x06_dev { 30struct device_node*nd; /* 设备节点 */ 31int irq_pin,reset_pin; /* 中断和复位IO */ 32int irqnum; /* 中断号 */ 33void *private_data; /* 私有数据 */ 34struct input_dev *input; /* input结构体 */ 35struct i2c_client *client; /* I2C客户端 */ 36 }; 37 38 static struct ft5x06_dev ft5x06; 39 40 /* 41* @description: 复位FT5X06 42* @param - client: 要操作的i2c 43* @param - multidev: 自定义的multitouch设备 44* @return: 0,成功; 其他负值,失败 45*/ 46 static int ft5x06_ts_reset(struct i2c_client *client, struct ft5x06_dev *dev) 47 { 48int ret = 0; 49 50if (gpio_is_valid(dev->reset_pin)) {/* 检查IO是否有效 */ 51/* 申请复位IO,并且默认输出低电平 */ 52ret = devm_gpio_request_one(&client->dev, 53dev->reset_pin, GPIOF_OUT_INIT_LOW, 54"edt-ft5x06 reset"); 55if (ret) { 56return ret; 57} 58 59msleep(5); 60gpio_set_value(dev->reset_pin, 1); /* 输出高电平,停止复位 */ 61msleep(300); 62} 63 64return 0; 65 } 66 67 /* 68* @description : 从FT5X06读取多个寄存器数据 69* @param - dev:ft5x06设备 70* @param - reg:要读取的寄存器首地址 71* @param - val:读取到的数据 72* @param - len:要读取的数据长度 73* @return: 操作结果 74*/ 75 static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len) 76 { 77int ret; 78struct i2c_msg msg[2]; 79struct i2c_client *client = (struct i2c_client *)dev->client; 80 81/* msg[0]为发送要读取的首地址 */ 82msg[0].addr = client->addr; /* ft5x06地址 */ 83msg[0].flags = 0; /* 标记为发送数据 */ 84msg[0].buf = ® /* 读取的首地址 */ 85msg[0].len = 1; /* reg长度*/ 86 87/* msg[1]读取数据 */ 88msg[1].addr = client->addr; /* ft5x06地址 */ 89msg[1].flags = I2C_M_RD; /* 标记为读取数据*/ 90msg[1].buf = val; /* 读取数据缓冲区 */ 91msg[1].len = len; /* 要读取的数据长度*/ 92 93ret = i2c_transfer(client->adapter, msg, 2); 94if(ret == 2) { 95ret = 0; 96} else { 97ret = -EREMOTEIO; 98} 99return ret; 100 } 101 102 /* 103* @description : 向ft5x06多个寄存器写入数据 104* @param - dev:ft5x06设备 105* @param - reg:要写入的寄存器首地址 106* @param - val:要写入的数据缓冲区 107* @param - len:要写入的数据长度 108* @return:操作结果 109*/ 110 static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg, u8 *buf, u8 len) 111 { 112u8 b[256]; 113struct i2c_msg msg; 114struct i2c_client *client = (struct i2c_client *)dev->client; 115 116b[0] = reg; /* 寄存器首地址 */ 117memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */ 118 119msg.addr = client->addr; /* ft5x06地址 */ 120msg.flags = 0; /* 标记为写数据 */ 121 122msg.buf = b; /* 要写入的数据缓冲区 */ 123msg.len = len + 1; /* 要写入的数据长度 */ 124 125return i2c_transfer(client->adapter, &msg, 1); 126 } 127 128 /* 129* @description : 向ft5x06指定寄存器写入指定的值,写一个寄存器 130* @param - dev:ft5x06设备 131* @param - reg:要写的寄存器 132* @param - data: 要写入的值 133* @return:无 134*/ 135 static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data) 136 { 137u8 buf = 0; 138buf = data; 139ft5x06_write_regs(dev, reg, &buf, 1); 140 } 141 142 /* 143* @description: FT5X06中断服务函数 144* @param - irq: 中断号 145* @param - dev_id: 设备结构。 146* @return: 中断执行结果 147*/ 148 static irqreturn_t ft5x06_handler(int irq, void *dev_id) 149 { 150struct ft5x06_dev *multidata = https://www.it610.com/article/dev_id; 151 152u8 rdbuf[29]; 153int i, type, x, y, id; 154int offset, tplen; 155int ret; 156bool down; 157 158offset = 1; /* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */ 159tplen = 6; /* 一个触摸点有6个寄存器来保存触摸值 */ 160 161memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */ 162 163/* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */ 164ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN); 165if (ret) { 166goto fail; 167} 168 169/* 上报每一个触摸点坐标 */ 170for (i = 0; i < MAX_SUPPORT_POINTS; i++) { 171u8 *buf = &rdbuf[i * tplen + offset]; 172 173/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下: 174* bit7:6Event flag0:按下 1:释放 2:接触 3:没有事件 175* bit5:4保留 176* bit3:0X轴触摸点的11~8位。 177*/ 178type = buf[0]>> 6; /* 获取触摸类型 */ 179if (type == TOUCH_EVENT_RESERVED) 180continue; 181 182/* 我们所使用的触摸屏和FT5X06是反过来的 */ 183x = ((buf[2] << 8) | buf[3]) & 0x0fff; 184y = ((buf[0] << 8) | buf[1]) & 0x0fff; 185 186/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下: 187* bit7:4Touch ID触摸ID,表示是哪个触摸点 188* bit3:0Y轴触摸点的11~8位。 189*/ 190id = (buf[2] >> 4) & 0x0f; 191down = type != TOUCH_EVENT_UP; 192 193input_mt_slot(multidata->input, id); 194input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down); 195 196if (!down) 197continue; 198 199input_report_abs(multidata->input, ABS_MT_POSITION_X, x); 200input_report_abs(multidata->input, ABS_MT_POSITION_Y, y); 201} 202 203input_mt_report_pointer_emulation(multidata->input, true); 204input_sync(multidata->input); 205 206 fail: 207return IRQ_HANDLED; 208 209 } 210 211 /* 212* @description: FT5x06中断初始化 213* @param - client: 要操作的i2c 214* @param - multidev: 自定义的multitouch设备 215* @return: 0,成功; 其他负值,失败 216*/ 217 static int ft5x06_ts_irq(struct i2c_client *client, struct ft5x06_dev *dev) 218 { 219int ret = 0; 220 221/* 1,申请中断GPIO */ 222if (gpio_is_valid(dev->irq_pin)) { 223ret = devm_gpio_request_one(&client->dev, dev->irq_pin, 224GPIOF_IN, "edt-ft5x06 irq"); 225if (ret) { 226dev_err(&client->dev, 227"Failed to request GPIO %d, error %d\n", 228dev->irq_pin, ret); 229return ret; 230} 231} 232 233/* 2,申请中断,client->irq就是IO中断, */ 234ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 235ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 236client->name, &ft5x06); 237if (ret) { 238dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); 239return ret; 240} 241 242return 0; 243 } 244 245/* 246* @description: i2c驱动的probe函数,当驱动与 247*设备匹配以后此函数就会执行 248* @param - client: i2c设备 249* @param - id: i2c设备ID 250* @return: 0,成功; 其他负值,失败 251*/ 252 static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) 253 { 254int ret = 0; 255 256ft5x06.client = client; 257 258/* 1,获取设备树中的中断和复位引脚 */ 259ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0); 260ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0); 261 262/* 2,复位FT5x06 */ 263ret = ft5x06_ts_reset(client, &ft5x06); 264if(ret < 0) { 265goto fail; 266} 267 268/* 3,初始化中断 */ 269ret = ft5x06_ts_irq(client, &ft5x06); 270if(ret < 0) { 271goto fail; 272} 273 274/* 4,初始化FT5X06 */ 275ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 进入正常模式*/ 276ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426中断模式 */ 277 278/* 5,input设备注册 */ 279ft5x06.input = devm_input_allocate_device(&client->dev); 280if (!ft5x06.input) { 281ret = -ENOMEM; 282goto fail; 283} 284ft5x06.input->name = client->name; 285ft5x06.input->id.bustype = BUS_I2C; 286ft5x06.input->dev.parent = &client->dev; 287 288__set_bit(EV_KEY, ft5x06.input->evbit); 289__set_bit(EV_ABS, ft5x06.input->evbit); 290__set_bit(BTN_TOUCH, ft5x06.input->keybit); 291 292input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0); 293input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0); 294input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0); 295input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0); 296ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0); 297if (ret) { 298goto fail; 299} 300 301ret = input_register_device(ft5x06.input); 302if (ret) 303goto fail; 304 305return 0; 306 307 fail: 308return ret; 309 } 310 311 /* 312* @description: i2c驱动的remove函数,移除i2c驱动的时候此函数会执行 313* @param - client: i2c设备 314* @return: 0,成功; 其他负值,失败 315*/ 316 static int ft5x06_ts_remove(struct i2c_client *client) 317 { 318/* 释放input_dev */ 319input_unregister_device(ft5x06.input); 320return 0; 321 } 322 323 324 /* 325*传统驱动匹配表 326*/ 327 static const struct i2c_device_id ft5x06_ts_id[] = { 328{ "edt-ft5206", 0, }, 329{ "edt-ft5426", 0, }, 330{ /* sentinel */ } 331 }; 332 333 /* 334* 设备树匹配表 335*/ 336 static const struct of_device_id ft5x06_of_match[] = { 337{ .compatible = "edt,edt-ft5206", }, 338{ .compatible = "edt,edt-ft5426", }, 339{ /* sentinel */ } 340 }; 341 342 /* i2c驱动结构体 */ 343 static struct i2c_driver ft5x06_ts_driver = { 344.driver = { 345.owner = THIS_MODULE, 346.name = "edt_ft5x06", 347.of_match_table = of_match_ptr(ft5x06_of_match), 348}, 349.id_table = ft5x06_ts_id, 350.probe= ft5x06_ts_probe, 351.remove= ft5x06_ts_remove, 352 }; 353 354 /* 355* @description : 驱动入口函数 356* @param: 无 357* @return: 无 358*/ 359 static int __init ft5x06_init(void) 360 { 361int ret = 0; 362 363ret = i2c_add_driver(&ft5x06_ts_driver); 364 365return ret; 366 } 367 368 /* 369* @description : 驱动出口函数 370* @param: 无 371* @return: 无 372*/ 373 static void __exit ft5x06_exit(void) 374 { 375i2c_del_driver(&ft5x06_ts_driver); 376 } 377 378 module_init(ft5x06_init); 379 module_exit(ft5x06_exit); 380 MODULE_LICENSE("GPL"); 381 MODULE_AUTHOR("topeet");

第 29~36 行,定义一个设备结构体,存放多点电容触摸设备相关属性信息。
第 38 行,定义一个名为 ft5x06 的全局变量,变量类型就是上面定义的 ft5x06_dev 结构体。
第 46~63 行,ft5x06_ts_reset 函数,用于初始化 FT5426 触摸芯片,其实就是设置 FT5426的复位 IO 为高电平,防止芯片复位。注意在第 52 行使用 devm_gpio_request_one 函数来申请复位 IO,关于“devm_”前缀的作用已经在前面做了详细的讲解。使用“devm_”前缀的API 函数申请的资源不需要我们手动释放,内核会处理,所以这里使用 devm_gpio_request_one函数申请 IO 以后不需要我们在卸载驱动的时候手动去释放此 IO。
第 73~98 行,ft5x06_read_regs 函数,用于连续的读取 FT5426 内部寄存器数据,就是 I2C读取函数。
第 98~124 行,ft5x06_write_regs 函数,用于向 FT5426 寄存器写入连续的数据,也就是 I2C写函数。
第 133~138 行,ft5x06_write_reg 函数,对 ft5x06_write_regs 函数的简单封装,向 FT5426 指定寄存器写入一个数据,用于配置 FT5426。
第 146~207 行,ft5x06_handler 函数,触摸屏中断服务函数,触摸点坐标的上报就是在此函数中完成的。第 162 行通过 ft5x06_read_regs 函数读取 FT5426 的所有触摸点信息寄存器数据,从 0X02 这个地址开始,一共 29 个寄存器。第 168~199 行的 for 循环就是一个一个的上报触摸点坐标数据,使用Type B时序,这个我们已经在前面说了很多次了。最后在202行通过input_sync函数上报 SYN_REPORT 事件。如果理解了前面讲解的 Type B 时序,那么此函数就很好看懂。
第 215~241 行 , ft5x06_ts_irq 函数,初始化 FT5426 的中断 IO ,第 221 行使用
devm_gpio_request_one 函数申请中断 IO。第 232 行使用函数 devm_request_threaded_irq 申请中断,中断处理函数为 ft5x06_handler。
第 250~307 行,当 I2C 设备与驱动匹配以后此函数就会执行,一般在此函数中完成一些初始化工作。我们重点来看一下 277~299 行是关于 input_dev 设备的初始化,第 277~284 行申请并简单的初始化 input_dev。第 286 和 288行设置 input_dev需要上报的事件为 EV_KEY 和 EV_ABS,需要上报的按键码为 BTN_TOUCH。EV_KEY 是按键事件,用于上报触摸屏是否被按下,相当于把触摸屏当做一个按键。EV_ABS 是触摸点坐标数据,BTN_TOUCH 表示将触摸屏的按下和抬起用作 BTN_TOUCH 按键。第 290~293 行调用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。第 294 行调用 input_mt_init_slots 函数初始化 slots,也就是最大触摸点数量,FT5426 是个 5 点电容触摸芯片,因此一共 5 个 slot。最后在299 行调用 input_register_device 函数向系统注册 input_dev。
第 316~321 行,当卸载驱动的时候 ft5x06_ts_remove 函数就会执行,因为前面很多资源我们都是用“devm_”前缀函数来申请的,因此不需要手动释放。此函数只需要调用input_unregister_device 来释放掉前面添加到内核中的 input_dev。
第 322 行~结束,剩下的就是 I2C 驱动框架那一套。
i.MX6ULL终结者|i.MX6ULL终结者Linux 电容触摸屏实验实验程序编写
文章图片

    推荐阅读