电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【i.MX6ULL】驱动开发12——电容触摸驱动实践(上)
分 享
扫描二维码分享
【i.MX6ULL】驱动开发12——电容触摸驱动实践(上)
嵌入式
linux
触摸屏
码农爱学习
关注
发布时间: 2022-01-13
丨
阅读: 389
上篇文章介绍了LCD屏幕的使用,这个屏幕还有触摸功能,本篇就来介绍LCD的触摸功能的使用。 关于触摸的内容有点多,分为上下两篇进行讲解,本篇先介绍**触摸驱动的编写**以及**将触摸点坐标实时打印出来**进行测试,先有一个整体的使用感受,下篇文章再介绍具体的**触摸上报协议**以及**图形化的测试方法**。 [TOC] # 1 触摸介绍 LCD的触摸功能,本质就是显示屏上再叠加一层透明的触摸屏,实现触摸的方式与LCD进行交互。 触摸屏分为**电阻触摸屏**和**电容触摸屏**。 - 电阻触摸屏是一种传感器,其结构是薄膜加上玻璃的结构,两结构相邻的一面上均涂有ITO(一种导电性和透明性很好的)涂层。当触摸操作时,两层结构**挤压接触**,经由感应器传出相应的电信号,通过运算转化为屏幕上的X、Y值。 - 电容技术触摸屏CTP(Capacity Touch Panel)是利用**人体的电流感应**进行工作的。电容屏是一块四层复合玻璃屏,电容式触摸屏就是支持**多点触摸**的人机交互方式,普通电阻式触摸屏只能进行单一点的触控。 ## 1.1 硬件原理图 本篇使用的是野火的7寸电容触摸屏,分辨率和屏幕一样,800x480。触摸驱动芯片我GT911,是IIC接口的芯片。 ![](https://cf04.ickimg.com/bbsimages/202201/f906176b067f49dbcbe1abbe26a07404.jpg) 触摸芯片有四个引脚: - SDA:触摸芯片的IIC 通信引脚 - SCL:触摸芯片的IIC 通信引脚 - RSTN:触摸芯片的复位引脚 - INT:触摸芯片的中断引脚 对应板子原理图的触摸接口如下: ![](https://cf04.ickimg.com/bbsimages/202201/a000ffe2f09e13d043b7002150fb90a8.png) 对应屏幕原理图的触摸接口如下: ![](https://cf04.ickimg.com/bbsimages/202201/c98d8ebe5dd7a0d8907555249bab838b.png) # 2 编写触摸驱动代码 触摸芯片用到**IIC通信**,还要用到**复位引脚**和**中断引脚**,因此需要先在设备树中对引脚信息进行配置。 ## 2.1 修改设备树 修改imx6ull_myboard.dts文件。 在设备树中把触摸要用到的引脚追加到 iomuxc即可。 | 引脚 | 功能 | | ------------- | ------------------------------------------------------- | | UART4_RX_DATA | 复用为 I2C1_SDA,用作 IIC1 的 SDA 引脚 | | UART4_TX_DATA | 复用为 I2C1_SCL,用作 IIC1 的 SCL 引脚 | | LCD_RST | 复用为 GPIO3_IO04 用作触摸芯片的复位引脚 | | SNVS_TAMPER9 | 复用为 GPIO5_IO09 用作触摸芯片的 irq 引脚,接收触摸中断 | 需要注意的是,SNVS_TAMPER9 引脚被复用为 GPIO5_IO09,需要追加到 iomuxc_snvs 节点。 ### 2.1.1 IIC引脚 触摸芯片用到的是IIC1,这两个引脚在设备树中以及默认添加了,无需修改: ![](https://cf04.ickimg.com/bbsimages/202201/44a74984fedd0dc1d8a7d1ad8e3ef34f.png) ### 2.1.2 复位引脚 **&iomuxc**节点中添加: ```c /@@*my gt911*/ pinctrl_tsc_reset: tscresetgrp { fsl,pins = < /@@* used for tsc reset */ MX6UL_PAD_LCD_RESET__GPIO3_IO04 0x10b0 >; }; ``` ![](https://cf04.ickimg.com/bbsimages/202201/47dfc015bde7f62f7e1bdfb74655afee.png) ### 2.1.3 中断引脚 **&iomuxc_snvs**节点中添加: ```c /@@*my gt911*/ pinctrl_tsc_irq: tsc_irq { fsl,pins = < /@@* used for tsc irq */ MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x4001b8b0 >; } ``` ![](https://cf04.ickimg.com/bbsimages/202201/2ba0e1f02ad323765b3a026c352e7e0c.png) ### 2.1.4 IIC设备添加GT911 GT911触摸驱动作为一个IIC设备挂载在IIC1总线上,找到IIC1节点: ![](https://cf04.ickimg.com/bbsimages/202201/336c8c37cea674bcc6eab9402325677b.png) 需要在 IIC1 设备节点下追加相应的子节点: ```c 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>; }; ``` 修改后: ![](https://cf04.ickimg.com/bbsimages/202201/5639eee08b2859b30c219b59b05c3ef3.png) reg = <0x5d>是GT911触摸芯片在 IIC1总线上的地址。 ## 2.2 触摸芯片数据寄存器 查看GT911的数据手册,找到**寄存器**相关的表格: ![](https://cf04.ickimg.com/bbsimages/202201/458aab8a4831548751e08456e5cd7ef9.png) 主要关注以下这些寄存器,它们是用来**读取触摸坐标点**的: | Addr | Access | bit7~bit0 | | ---------- | ------ | ------------------------------------------------------------ | | **0x814E** | R/W | **buffer status**(7) large detect(6) Reserved(5~4) **number of touch points**(3~0) | | **0x814F** | R | track id | | 0x8150 | R | point 1 x coordinate (low byte) | | 0x8151 | R | point 1 x coordinate (high byte) | | 0x8152 | R | point 1 y coordinate (low byte) | | 0x8153 | R | point 1 y coorte (high byte) | | 0x8154 | R | Point 1 size (low byte) | | 0x8155 | R | Point 1 size (high byte) | | 0x8156 | R | Reserved | | **0x8157** | R | track id | | ... | ... | ... | | **0x815F** | R | track id | | ... | ... | ... | | **0x8167** | R | track id | | ... | ... | ... | | **0x816F** | R | track id | | 0x8170 | R | point 5 x coordinate (low byte) | | 0x8171 | R | point 5 x coordinate (high byte) | | 0x8172 | R | point 5 y coordinate (low byte) | | 0x8173 | R | point 5 y coordinate (high byte) | | 0x8174 | R | Point 5 size (low byte) | | 0x8175 | R | Point 5 size (high byte) | | 0x8176 | R | Reserved | - **0x814E**:这个寄存器很重要,它是可读可写的寄存器,通过读取该寄存器,可以知道当前**是否有**触摸点(由**最高位**表示),以及**有几个**触摸点(由**低3位**表示) - **0x814F**~0x8156:是第一组触摸的坐标数据 - **0x814F**:是触摸点的追踪id,GT911支持5点触摸,这里id的取值为0~4 - 0x8150:触摸点1的x坐标(低字节) - 0x8151:触摸点1的x坐标(高字节)y - 0x8152:触摸点1的y坐标(低字节) - 0x8153:触摸点1的y坐标(高字节) - 0x8154~0x8156:暂不使用 - **0x8157**以后的寄存器:与第一组触摸的坐标数据的含义类似,一个有五组 > 注:GT911支持硬件追踪触摸点,因此为每个触摸点提供了一个track id,举个简单的例子,当5个手指依次触摸到屏幕时,5组坐标寄存器中的track id会依次是0、1、2、3、4,**当松开第1个手指时**,即track id为0的点没有了,此时5组坐标寄存器,是**只有前45组坐标寄存器有数据,track id会依次是1、2、3、4**(理解这个很重要,因为我一开始想当然的误认为,移开第1个手指时,是第1组坐标寄存器没数据了) ## 2.3 编写驱动程序 新建gt911.c文件作为驱动文件 触摸芯片GT911的使用,本质是使用IIC通信,进行数据的读写,因为触摸屏的驱动,实际就是**IIC驱动**。另外,触摸的数据是通过中断的方式触发的,因此触摸驱动的编写,涉及到**中断的处理**。在中断时,读取到触摸数据后,要传递到应用层,这里是使用Linux的**input子系统**(这也是Linux的一种软件分层设计的方式)。 所以,编写触摸驱动,主要涉及3点: - IIC协议的驱动 - 中断的处理(获取触摸数据) - input子系统(将触摸数据传递到应用层) ### 2.3.1 IIC驱动架构 GT911的驱动按照IIC驱动来写,当驱动运行时,会自动运行**gt911_probe**,在这个函数中会进行各种初始化操作。另外注意匹配列表,这里的**“goodix,gt911”**对应设备树中添加的设备节点,两处的名字要一致。 ```c /@@* 匹配列表 */ static const struct of_device_id gt911_of_match[] = { {.compatible = "goodix,gt911"}, {/@@* Sentinel */} }; /@@* i2c驱动结构体 */ struct i2c_driver gt911_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = "gt911", /@@* 驱动名字 用于和设备匹配 适用于没有设备树的情况*/ .of_match_table =gt911_of_match, /@@* 设备树匹配列表 */ }, .probe =gt911_probe, .remove =gt911_remove, .id_table = gt911_id, /@@* id配置列表 */ }; ``` ### 2.3.2 驱动的初始化流程 **gt911_probe**函数进行触摸驱动的初始化,基本流程就是从设备树获取触摸节点,然后进行IO的初始化,中断和复位的初始化以及触摸器件的初始化等。 ```c 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"); /@@* 获取设备树中的中断和复位引脚 */ 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); /@@* 初始化复位引脚 */ ret = gt911_ts_reset(client, >911); /@@* 初始化gt911 */ printk("[BSP] init gt911\r\n"); gt911_write_reg(>911, GT_CTRL_REG, 2); /@@* 软复位 */ mdelay(100); gt911_write_reg(>911, GT_CTRL_REG, 0); /@@* 停止软复位 */ mdelay(100); /@@* input 注册设备*/ 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; /@@* 设置input设备需要上报哪些事件*/ __set_bit(EV_SYN, gt911.input->evbit); __set_bit(EV_KEY, gt911.input->evbit); /@@* 按键事件 */ __set_bit(EV_ABS, gt911.input->evbit); /@@* 重复事件 */ /@@* 设置input设备需要上报哪些按键*/ __set_bit(BTN_TOUCH, gt911.input->keybit); /@@* 触摸值 */ /@@* 多点触摸 */ input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0); /@@*触摸点的数量 */ 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 复位与中断的初始化 复位引脚的初始化主要就是拉低再拉高复位引脚,实现复位,主要内容为: ```c /@@* 申请复位IO 并且默认输出高电平 */ devm_gpio_request_one(&client->dev, dev->reset_pin, GPIOF_OUT_INIT_HIGH, "gt911 reset"); gpio_set_value(dev->reset_pin, 0); /@@* 复位 */ msleep(10); gpio_set_value(dev->reset_pin, 1); /@@* 停止复位 */ msleep(300); ``` 中断的初始化,包括IO的申请和中断函数的注册: ```c /@@* 申请复位 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, /@@* 中断处理函数 */ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, /@@* 触发方式 */ client->name, >911); ``` ### 2.3.4 中断处理函数 这里仅贴出gt911_irq_handler的主要内容,基本思路是先读取**0x814E(GT_GSTID_REG)**这一个寄存器,判断触摸点的数量,然后再读取对应的坐标点数据寄存器,依次上报数据。 ```c /@@* -----读取触摸信息寄存器----- */ ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1); if(data == 0x00) /@@* 没有触摸数据*/ { goto fail; } else { /@@* 统计触摸信息 */ status = data >> 7; // bit7:1表示坐标(或按键)已经准备好,主控可以读取 0 表示未就绪,数据无效 large_detect = (data >> 6) & 0x01; // bit6: touch_num = data & 0x0f; // bit3~0:屏上的坐标点个数 } if(touch_num) /@@* 有触摸按下 */ { /@@* -----读取具体的触摸点数据寄存器----- */ gt911_read_regs(dev, GT_TP1_REG, buf, BUFFER_SIZE); id = buf[0]; // 数据中的第一个触摸点的id touch_index |= (0x01<
input, id); // 产生ABS_MT_SLOT 事件 报告是哪个触摸点的坐标 input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true); // 指定手指触摸 连续触摸 input_report_abs(dev->input, ABS_MT_POSITION_X, input_x); // 上报触摸点坐标信息 input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y); // 上报触摸点坐标信息 printk("[%d](%d, %d) ", id, input_x, input_y); report_num++; if (report_num < touch_num) { pos += 8; id = buf[pos]; touch_index |= (0x01<
input, i); input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); // 关闭手指触摸 } } printk("\r\n"); } else if(last_index)/@@* 触摸释放 */ { for (i = 0; i < 5; i++) { if ((last_index & (0x01<
input, i); /@@* 上报触摸点 */ input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); // 关闭手指触摸 } } } last_index = touch_index; input_mt_report_pointer_emulation(dev->input, true); input_sync(dev->input); /@@* 同步数据 数据上报完成 */ data = 0x00; /@@* 向0x814E寄存器写0 不然就会一直进入中断 */ gt911_write_regs(dev, GT_GSTID_REG, &data, 1); //写入 ``` 总结一下GT911多点触摸驱动的执行流程: ![](https://cf04.ickimg.com/bbsimages/202201/b775b1e3c29e31812ba3a66edc5c627b.png) # 3 使用Linux内核自带的驱动(未测试) 对于触摸屏的驱动,NXP已经编写好了触摸驱动,加以修改可以在自己的板子上使用。 不过我没有测试成功,以后有时间再搞,所以**这一部分内容可以跳过**。 我这个7寸屏的驱动型号为GT911,属于 GOODIX 公司生产的触摸芯片,该触摸驱动已默认添加到了Linux内核中,位于:/drivers/input/touchscreen/goodix.c。 ![](https://cf04.ickimg.com/bbsimages/202201/005196e70bb9e8ed178139eece851f11.png) 使用Linux内核自代的驱动,还需要进行内核配置。在Linux内核源码目录,输入以下指令打开内核的图形化配置: ```sh make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig ``` 到达Linux内核配置界面,然后按下路径找到对应的配置项: ```sh -> Device Drivers -> Input device support -> Touchscreens (INPUT_TOUCHSCREEN [=y]) <*> Goodix I2C touchscreen ``` 最终到达这个界面: ![](https://cf04.ickimg.com/bbsimages/202201/0cb66f689a54d69ae864948f39a47a1d.png) 按下y勾选上星号,连按多次ESC退出,最后提示保存,按下y保存配置。 然后需要重新编译zImage和设备树,到Linux内核源码目录,执行之前的编写的编译脚本 ```sh ./build_myboard.sh ``` 编译的时候会弹出Linux图形配置界面, 不需要做任何的配置, 直接按两下ESC键退出图形界面 将编译出**zImage**(arch/arm/boot目录)和**imx6ull-myboard.dtb** (arch/arm/boot/dts目录)复制到网络启动位置 ```c cp arch/arm/boot/zImage ~/myTest/tftpboot/nxp/ cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/ ``` # 4 触摸测试 使用自己编写的触摸驱动程序,进行测试。 ## 4.1 编译设备树 首先是编译设备树,验证添加的触摸节点是否工作正常,在Linux内核源码目录执行下面的命令,重新编译设备树并拷贝到网络启动位置。 ```sh make imx6ull-myboard.dtb cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/ ``` 然后重启开发板,可以先到如下位置,查看设备树的节点是否正常: ![](https://cf04.ickimg.com/bbsimages/202201/20f9c7f49d3ffe7f62fb19d60085dd2d.png) ## 4.2 编译驱动文件 然后是编译驱动文件,也就是gt911.c,编译方式和之前一样,在ubuntu中使用Makefile进行交叉编译。 本篇暂未用到对应的触摸应用程序,所有的触摸坐标打印都是在驱动程序中通过**printk**的方式进行内核打印。 编译完驱动后,将对应的.ko文件复制到板子中。 ## 4.3 测试触摸点的坐标输出 先加载触摸驱动,串口会打印出为触摸分配的event,我这里是**event2**。 然后执行下面的指令进行触摸测试: ```sh hexdump /dev/input/event2 ``` ![](https://cf04.ickimg.com/bbsimages/202201/5ad569e3ce095bd2aad3a5daa07be47e.png) 将手指放到屏幕上,就可以在LCD屏幕上看到坐标值的打印,比如将手指放到屏幕左下角,对应输出的值大致就是屏幕的最大位置(800,480): ![](https://cf04.ickimg.com/bbsimages/202201/1c6e34fdd0adcb3bb261622088530759.jpg) GT911支持多点触摸,驱动程序中也对多点数据进行了获取和打印,将多个手指放到屏幕上,可以看到最多有5个触摸点的坐标打印: ![](https://cf04.ickimg.com/bbsimages/202201/d353814df4fbce795fe6f4d345e7b3ef.jpg) # 5 总结 本篇主要介绍了多点触摸芯片GT911的驱动编写与使用,并通过将触摸点实时打印的方式,测试触摸功能。 # 附:视频演示
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
码农爱学习
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交