多点电容触摸的驱动使用到的知识主要有input子系统、gpio硬件中断、iic子系统,tslib测试等知识点,下面将针对多点电容触摸屏的驱动开发的四个知识点进行展开,以及gt911芯片的知识,官方gt9xx驱动的移植。
gt911芯片是可以直接使用的,因为出场的时候已经代码已经烧录进去了,上电之后就可以直接检测到芯片的int引脚产生的中断脉冲,配置对应的引脚为中断,就可以观察到中断的触发。gt911芯片使用需要注意的有,芯片地址的脉冲配置,数据判断寄存器,中断清除。
(1)根据reset复位引脚和int中断引脚的脉冲的配置,设置芯片的iic通讯地址,
但是gt911的iic通讯地址,只有低7位有效,所以需要将0xBA/0xBB的地址右移一位,得到iic的通讯地址为:0x5d。
(2)触摸点的数据缓冲区是否有数据,以及触摸点的个数,是一个重要的寄存器进行储存的。
(3)在中断里将数据读取走之后,需要对“0x814E”寄存器写入0,不然int引脚就会一直产生中断的脉冲。
韦老师的开发板是gt911的芯片通讯的iic是使用的imx6ull的i2c,设备树中gt911的设备结点时挂载到i2c的结点下面的。编写i2c的时候可以先测试与设备树的匹配是否成功,当iic设备与设备树中的结点匹配成功之后,就会调用probe函数,这个可以测试是否匹配成功。在匹配成功之后,就可以写iic的发送和接收函数,这时候可以对gt911的其中一个可以读写的寄存器,先对寄存器写入,然后对寄存器进行读取,观察写入和读出的数据是否相同,相同就说明iic读写是成功的。
注意iic通讯使用的通讯频率。
gpio的硬件中断的配置代码前面是已将有了,但是这里并没有使用gpio的硬件中断,因为gt911的工作频率是非常高的,中断上报的频率也是非常频繁的,如果使用硬件中断进行处理的话,由于硬件中断的优先级是最高的,这时候芯片就需要频繁的去处理中断,导致芯片效率变低。就算使用中断的上半部和下半部进行处理的话,中断的下半部使用软件中断也是优先级比普通的任务函数高的,使用任务进行处理,中断的上半部分也是较占资源的,所以这里采用中断线程化来处理这种比较频繁的中断。中断线程化之后,创建的资源是不需要程序员手动释放的,因为普通的任务线程是由系统管理的,在其不使用了之后就会手动释放。
中断线程化设计到两个函数,使用还是比较简单的,下面将对两个函数的使用给出示例:(中断线程化的函数都是带“devm_”前缀的)。
下面是使用中断线程化函数进行gpio的创建:
/* 申请复位IO */
if (gpio_is_valid(dev->reset_pin)) /* gpio_is_valid判断gpio的pin是否存在 */
{
/* 申请复位IO,并且默认输出高电平 */
ret = devm_gpio_request_one(&dev->i2cdrv->dev, //设备
dev->reset_pin, //gpio
GPIOF_OUT_INIT_HIGH, //默认输出高电平
"gt9147 reset"); //gpio1的名字
if (ret)
{
return ret;
}
}
/* 申请中断IO*/
if (gpio_is_valid(dev->irq_pin)) {
/* 申请复位IO,并且默认输出高电平 */
ret = devm_gpio_request_one(&dev->i2cdrv->dev,
dev->irq_pin, GPIOF_OUT_INIT_HIGH,
"gt9147 int");
if (ret) {
return ret;
}
}
下面是对应gpio的中断的申请:
/* 中断的出发需要在硬件初始化之后 */
static int gt911_ts_irq(g_ptI2cDev dev)
{
int ret = 0;
memset(dev->irq_name, 0, 20);
strcpy(dev->irq_name, "gt911_irq");
/* 2,申请中断,client->irq就是IO中断, */
ret = devm_request_threaded_irq(&dev->i2cdrv->dev, //设备
dev->i2cdrv->irq, //中断号
NULL, //默认
gt911_handler, //终端函数
IRQ_TYPE_EDGE_FALLING | IRQF_ONESHOT, //下降沿出发|不允许中断嵌套,本次结束才能处理下一个
dev->irq_name, //终端的名字
dev); //给中断函数传入的
if (ret) {
dev_err(&dev->i2cdrv->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
下面是对应的中断线程化的线程中断函数:
static irqreturn_t gt911_handler(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
不需要手动释放。
多点电容的触摸屏的input上报是这里新的知识点,前面没有学习过的,分为多触点形式的上报和单触点形式的上报,需要有屏幕按下和抬起的两种情况的上报,两种情况缺少一种,在下面tslib的测试过程中就会有问题。下面将从input上报的重要函数,input初始化和上报的流程案例进行讲解。
前面已经有input子系统上报的相关知识,这里只进行新的知识的记录。
852 #define ABS_MT_SLOT 0x2f /* MT slot being modified */
853 #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
854 #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
855 #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
856 #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
857 #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
858 #define ABS_MT_POSITION_X 0x35 /* Center X touch position */
859 #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
860 #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
861 #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
862 #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
863 #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
864 #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
865 #define ABS_MT_TOOL_X 0x3c /* Center X tool position */
866 #define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
void input_mt_sync(struct input_dev *dev)
void input_mt_slot(struct input_dev *dev, int slot)
1 ABS_MT_POSITION_X x[0]
2 ABS_MT_POSITION_Y y[0]
3 SYN_MT_REPORT
4 ABS_MT_POSITION_X x[1]
5 ABS_MT_POSITION_Y y[1]
6 SYN_MT_REPORT
7 SYN_REPORT
116 for (i = 0; i < MAX_FINGERS; i++) {
117 if (!finger[i].is_valid)
118 continue;
119
120 input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
121 input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
122 input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
123 input_mt_sync(input_dev);
124 count++;
125 }
......
140
141 /* SYN_REPORT */
142 input_sync(input_dev);
1 ABS_MT_SLOT 0
2 ABS_MT_TRACKING_ID 45
3 ABS_MT_POSITION_X x[0]
4 ABS_MT_POSITION_Y y[0]
5 ABS_MT_SLOT 1
6 ABS_MT_TRACKING_ID 46
7 ABS_MT_POSITION_X x[1]
8 ABS_MT_POSITION_Y y[1]
9 SYN_REPORT
1 ABS_MT_TRACKING_ID -1
2 SYN_REPORT
86 for (i = 0; i < MAX_TOUCHES; i++) {
87 input_mt_slot(input, i);
88
89 finger = &touchdata->finger[i];
90
91 touch = touchdata->status & (1 << i);
92 input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
93 if (touch) {
94 x = finger->x_low | (finger->x_high << 8);
95 y = finger->y_low | (finger->y_high << 8);
96
97 input_report_abs(input, ABS_MT_POSITION_X, x);
98 input_report_abs(input, ABS_MT_POSITION_Y, y);
99 }
100 }
101
102 input_mt_report_pointer_emulation(input, false);
103 input_sync(input);
下面是自己在使用中的input上报系统的初始化和上报处理的代码示例:
static int Gt922_Init(void)
{
uint8_t reset_reg;
/* gt911的初始化 */
/* 3,初始化GT911 */
reset_reg = 0x02;
gt911_write_regs(&I2c, GT_CTRL_REG, &reset_reg, 1); /* 软复位 */
gt911_read_regs(&I2c, GT_CTRL_REG, &reset_reg, 1);
printk("reset_reg:0x%x == 0x02", reset_reg);
mdelay(100);
reset_reg = 0x0;
gt911_write_regs(&I2c, GT_CTRL_REG, &reset_reg, 1); /* 停止软复位 */
mdelay(100);
gt911_read_regs(&I2c, GT_CTRL_REG, &reset_reg, 1);
printk("reset_reg:0x%x == 0x00", reset_reg);
mdelay(1000);
return 0;
}
static int Input_Init(void)
{
int ret;
/* input的初始化,使用前缀“devm_”的函数申请input设备,
可以不用自己手动释放,系统管理资源,会自己free掉 */
I2c.input = devm_input_allocate_device(&I2c.i2cdrv->dev);
if (!I2c.input) {
printk("devm_input_allocate_device is error\r\n");
ret = -ENOMEM;
}
memset(I2c.input_name, 0, 20);
strcpy(I2c.input_name, "gt911_input");
/* input子系统参数配置 */
I2c.input->name = I2c.input_name;
I2c.input->id.bustype = BUS_I2C;
I2c.input->dev.parent = &I2c.i2cdrv->dev;
__set_bit(EV_KEY, I2c.input->evbit); /* 支持单点触摸 */
__set_bit(EV_ABS, I2c.input->evbit); /* 支持多点触摸 */
__set_bit(BTN_TOUCH, I2c.input->keybit); /* 对应的事件为触摸按键touch事件 */
I2c.max_x = TOUCH_MAP_MAX_X; /* lcd分辨率的x轴最大值 */
I2c.max_y = TOUCH_MAP_MAX_Y; /* lcd分辨率的y轴最大值 */
/* 单点 */
//input_set_abs_params(I2c.input, ABS_X, 0, I2c.max_x, 0, 0);
//input_set_abs_params(I2c.input, ABS_Y, 0, I2c.max_y, 0, 0);
/* 多点 */
input_set_abs_params(I2c.input, ABS_MT_POSITION_X,0, I2c.max_x, 0, 0);
input_set_abs_params(I2c.input, ABS_MT_POSITION_Y,0, I2c.max_y, 0, 0);
ret = input_mt_init_slots(I2c.input, MAX_SUPPORT_POINTS, 0); /* 支持的最多的触摸点数 */
if (ret)
{
printk(" input_mt_init_slots is error\r\n");
return ret;
}
ret = input_register_device(I2c.input); /* 进行注册 */
if (ret)
{
printk("input_register_device is error\r\n");
return ret;
}
return 0;
}
static void Input_Exit(void)
{
input_unregister_device(I2c.input); /* 设备的注销 */
}
/* 有触摸点时候进行上报 */
input_mt_slot(dev->input, slot_id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
input_report_abs(dev->input, ABS_MT_TRACKING_ID, slot_id);
input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);
input_sync(dev->input);
/* 没有触摸点或者触摸点松开时候的上报 */
input_mt_slot(dev->input, slot_id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
input_report_abs(dev->input, ABS_MT_TRACKING_ID, -1);
input_sync(dev->input);
/* 中断退出之前,要对寄存器0x814E写入0,不然会持续触发中断 */
dt911_flage = 0x00;
gt911_write_regs(dev, GT_GSTID_REG, &dt911_flage, 1);
tslib的测试,前面已经将过,这里就注意触摸的input对应那些事件就好了。在文件“/etc/profile”文件中。
由于gt911没有硬件检测触摸的按下和松开,所以需要软件上进行处理,这里我写的驱动不知道如何解决次问题,所以可以采用gt911官方的驱动代码和设备树,韦东山也是用的官方的设备树和驱动,后面会将这部分整理的代码发到自己的gitee,方便后面自己使用。
(1)首先要验证自己的驱动是否可以使用,是否完成了所有的功能,当所有都验证之后,就可以进行下面的操作。
(2)将自己写的驱动c文件拷贝到下面的文件夹:
/home/book/100ask_imx6ull-sdk/Linux-4.9.88/drivers/input
(3)修改当前文件夹下的“Makefile”
(4)修改完成以后重新编译 linux 内核,然后用新的 zImage 启动开发板。如果驱动添加成功的话系统启动的时候就会输出如图 64.4.3.2 所示的信息:
注意:这时候触摸input对应的event可能会变,tslib测试的时候需要注意。