【i.MX6ULL】驱动开发12——电容触摸驱动实践(上)
上篇文章介绍了LCD屏幕的使用,这个屏幕还有触摸功能,本篇就来介绍LCD的触摸功能的使用。
关于触摸的内容有点多,分为上下两篇进行讲解,本篇先介绍触摸驱动的编写以及将触摸点坐标实时打印出来进行测试,先有一个整体的使用感受,下篇文章再介绍具体的触摸上报协议以及图形化的测试方法。
文章目录
- 1 触摸介绍
- 1.1 硬件原理图
- 2 编写触摸驱动代码
- 2.1 修改设备树
- 2.1.1 IIC引脚
- 2.1.2 复位引脚
- 2.1.3 中断引脚
- 2.1.4 IIC设备添加GT911
- 2.2 触摸芯片数据寄存器
- 2.3 编写驱动程序
- 2.3.1 IIC驱动架构
- 2.3.2 驱动的初始化流程
- 2.3.3 复位与中断的初始化
- 2.3.4 中断处理函数
- 3 使用Linux内核自带的驱动(未测试)
- 4 触摸测试
- 4.1 编译设备树
- 4.2 编译驱动文件
- 4.3 测试触摸点的坐标输出
- 5 总结
- 附:视频演示
1 触摸介绍
LCD的触摸功能,本质就是显示屏上再叠加一层透明的触摸屏,实现触摸的方式与LCD进行交互。
触摸屏分为电阻触摸屏和电容触摸屏。
- 电阻触摸屏是一种传感器,其结构是薄膜加上玻璃的结构,两结构相邻的一面上均涂有ITO(一种导电性和透明性很好的)涂层。当触摸操作时,两层结构挤压接触,经由感应器传出相应的电信号,通过运算转化为屏幕上的X、Y值。
- 电容技术触摸屏CTP(Capacity Touch Panel)是利用人体的电流感应进行工作的。电容屏是一块四层复合玻璃屏,电容式触摸屏就是支持多点触摸的人机交互方式,普通电阻式触摸屏只能进行单一点的触控。
1.1 硬件原理图
本篇使用的是野火的7寸电容触摸屏,分辨率和屏幕一样,800x480。触摸驱动芯片我GT911,是IIC接口的芯片。
触摸芯片有四个引脚:
- SDA:触摸芯片的IIC 通信引脚
- SCL:触摸芯片的IIC 通信引脚
- RSTN:触摸芯片的复位引脚
- INT:触摸芯片的中断引脚
对应板子原理图的触摸接口如下:
对应屏幕原理图的触摸接口如下:
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,这两个引脚在设备树中以及默认添加了,无需修改:
2.1.2 复位引脚
&iomuxc节点中添加:
/*my gt911*/
pinctrl_tsc_reset: tscresetgrp {fsl,pins = </* used for tsc reset */MX6UL_PAD_LCD_RESET__GPIO3_IO04 0x10b0>;
};
2.1.3 中断引脚
&iomuxc_snvs节点中添加:
/*my gt911*/
pinctrl_tsc_irq: tsc_irq {fsl,pins = </* used for tsc irq */MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x4001b8b0>;
}
2.1.4 IIC设备添加GT911
GT911触摸驱动作为一个IIC设备挂载在IIC1总线上,找到IIC1节点:
需要在 IIC1 设备节点下追加相应的子节点:
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 触摸芯片数据寄存器
查看GT911的数据手册,找到寄存器相关的表格:
主要关注以下这些寄存器,它们是用来读取触摸坐标点的:
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”**对应设备树中添加的设备节点,两处的名字要一致。
/* 匹配列表 */
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的初始化,中断和复位的初始化以及触摸器件的初始化等。
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 复位与中断的初始化
复位引脚的初始化主要就是拉低再拉高复位引脚,实现复位,主要内容为:
/* 申请复位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的申请和中断函数的注册:
/* 申请复位 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)**这一个寄存器,判断触摸点的数量,然后再读取对应的坐标点数据寄存器,依次上报数据。
/* -----读取触摸信息寄存器----- */
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]; // 数据中的第一个触摸点的idtouch_index |= (0x01<<id);/* 上报每一个触摸点坐标 */for (i = 0; i < 5; i++){if ((touch_index & (0x01<<i))){input_x = (buf[pos + 1] | (buf[pos + 2] << 8)) & 0x0fff; // x坐标input_y = (buf[pos + 3] | (buf[pos + 4] << 8)) & 0x0fff; // y坐标input_mt_slot (dev->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<<id);}}else{input_mt_slot(dev->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<<i))){input_mt_slot(dev->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多点触摸驱动的执行流程:
3 使用Linux内核自带的驱动(未测试)
对于触摸屏的驱动,NXP已经编写好了触摸驱动,加以修改可以在自己的板子上使用。
不过我没有测试成功,以后有时间再搞,所以这一部分内容可以跳过。
我这个7寸屏的驱动型号为GT911,属于 GOODIX 公司生产的触摸芯片,该触摸驱动已默认添加到了Linux内核中,位于:/drivers/input/touchscreen/goodix.c。
使用Linux内核自代的驱动,还需要进行内核配置。在Linux内核源码目录,输入以下指令打开内核的图形化配置:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
到达Linux内核配置界面,然后按下路径找到对应的配置项:
-> Device Drivers -> Input device support -> Touchscreens (INPUT_TOUCHSCREEN [=y])<*> Goodix I2C touchscreen
最终到达这个界面:
按下y勾选上星号,连按多次ESC退出,最后提示保存,按下y保存配置。
然后需要重新编译zImage和设备树,到Linux内核源码目录,执行之前的编写的编译脚本
./build_myboard.sh
编译的时候会弹出Linux图形配置界面, 不需要做任何的配置, 直接按两下ESC键退出图形界面
将编译出zImage(arch/arm/boot目录)和imx6ull-myboard.dtb (arch/arm/boot/dts目录)复制到网络启动位置
cp arch/arm/boot/zImage ~/myTest/tftpboot/nxp/
cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
4 触摸测试
使用自己编写的触摸驱动程序,进行测试。
4.1 编译设备树
首先是编译设备树,验证添加的触摸节点是否工作正常,在Linux内核源码目录执行下面的命令,重新编译设备树并拷贝到网络启动位置。
make imx6ull-myboard.dtb
cp arch/arm/boot/dts/imx6ull-myboard.dtb ~/myTest/tftpboot/nxp/
然后重启开发板,可以先到如下位置,查看设备树的节点是否正常:
4.2 编译驱动文件
然后是编译驱动文件,也就是gt911.c,编译方式和之前一样,在ubuntu中使用Makefile进行交叉编译。
本篇暂未用到对应的触摸应用程序,所有的触摸坐标打印都是在驱动程序中通过printk的方式进行内核打印。
编译完驱动后,将对应的.ko文件复制到板子中。
4.3 测试触摸点的坐标输出
先加载触摸驱动,串口会打印出为触摸分配的event,我这里是event2。
然后执行下面的指令进行触摸测试:
hexdump /dev/input/event2
将手指放到屏幕上,就可以在LCD屏幕上看到坐标值的打印,比如将手指放到屏幕左下角,对应输出的值大致就是屏幕的最大位置(800,480):
GT911支持多点触摸,驱动程序中也对多点数据进行了获取和打印,将多个手指放到屏幕上,可以看到最多有5个触摸点的坐标打印:
5 总结
本篇主要介绍了多点触摸芯片GT911的驱动编写与使用,并通过将触摸点实时打印的方式,测试触摸功能。
附:视频演示
https://www.bilibili.com/video/BV1sZ4y1Q7da?spm_id_from=333.999.0.0
【i.MX6ULL】驱动开发12——电容触摸驱动实践(上)相关推荐
- STM32MP157驱动开发——多点电容触摸屏驱动
STM32MP157驱动开发--多点电容触摸屏驱动 一.简介 二.电容触摸屏驱动框架简介 多点触摸(MT)协议详解 三.驱动开发 1.添加 FT5426 设备节点 2.FT5426 节点配置 3.驱动 ...
- i.MX 6ULL 驱动开发 六:beep 驱动
一.原理分析 通过原理图可以确定 beep 连接到 SNVS_TAMPER1 引脚上.根据 beep 原理,当 SNVS_TAMPER1 输出低电平时,beep 鸣叫. 通过数据手册确定 SNVS_T ...
- Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
文章目录 全系列传送门 引言 驱动介绍 Hello World 1. 包含头文件 2. 驱动模块的入口和出口 3. 声明信息 4. 功能实现 完整代码 编译 第一种方法 第二种方法 编译成模块 第一步 ...
- Linux SD卡驱动开发(五) —— SD 卡驱动分析Core补充篇
Core层中有两个重要函数 mmc_alloc_host 用于构造host,前面已经学习过,这里不再阐述:另一个就是 mmc_add_host,用于注册host 前面探测函数s3cmci_probe, ...
- Linux驱动开发(外传)---驱动开发调试方法
前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...
- STM32MP157驱动开发——Linux块设备驱动
STM32MP157驱动开发--Linux块设备驱动 一.简介 二.驱动开发 1.使用请求队列的方式 2.测试① 3.不使用请求队列的方式 4.测试② 参考文章:[正点原子]I.MX6U嵌入式Linu ...
- Linux驱动开发:字符设备驱动开发实战
Linux驱动开发:字符设备驱动开发实战 一.工程创建 VSCode 创建工程,设置 C/C++ 配置,导入 linux kernel 源码目录,方便 vscode 写代码自动补全,vscode 配置 ...
- Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)
一.前言 块设备主要为存储设备设计的框架. 在前面章节Linux下驱动开发_块设备驱动开发(内存模拟存储) 里介绍了块设备驱动编写思路,并且利用内存模拟了硬件存储,完成了块设备驱动开发测试.这一篇文章 ...
- linux驱动开发 | 第一个字符驱动
一.驱动框架编写 1.编写驱动文件 打开linux内核,全局搜索module_init函数. linux内核中已经有了很多厂家写好的驱动模块,所以我们完全可以参考他们的代码. #include < ...
- RK3399驱动开发 | 06 - GT911触摸屏驱动调试及驱动浅析(Linux 5.4内核)
更新内容 更新时间 完成初稿 2022-09-21 文章目录 一.GT911 1. 触摸芯片 2. 原理图 二.驱动调试 1. 测试gt911是否正常通信 2. 添加驱动 3. 添加设备树描述 4. ...
最新文章
- debian手动安装java两种方法
- R语言使用ggplot2包使用geom_density()函数绘制密度图(自定义颜色填充、线条色彩、分组、均值线)实战(density plot)
- mysql建表影响效率_MySQL建表查询优化技巧
- shell获取ip的值
- queue double java_一文弄懂java中的Queue家族
- Redux 进阶 - react 全家桶学习笔记(二)
- jdbc executebatch 非事务_jdbc技术
- postgresql授权和撤销
- C#Brush的使用(转载)
- JAVA集合系列(3):ArrayList扩容原理分析
- PDFObject的使用(转)
- 大米云主机nodejs服务部署
- Windows10安装Vmware12
- 神鬼传奇客户端解包图片(ui\loadmap)
- 摩尔定律终结与科学大停滞
- 梳理19年上半年图文记录笔记(ios篇)
- ecshop模板教程——类似淘宝滚屏漂浮返回
- qt在window实现调取本机摄像头或者外设摄像头二维码识别升级版
- 狂写2万字带你快速上手React
- 浅析SSL/TLS的会话流程和源码实现