多点电容触摸驱动(基于gt911)

        多点电容触摸的驱动使用到的知识主要有input子系统、gpio硬件中断、iic子系统,tslib测试等知识点,下面将针对多点电容触摸屏的驱动开发的四个知识点进行展开,以及gt911芯片的知识,官方gt9xx驱动的移植。

一、gt911芯片

        gt911芯片是可以直接使用的,因为出场的时候已经代码已经烧录进去了,上电之后就可以直接检测到芯片的int引脚产生的中断脉冲,配置对应的引脚为中断,就可以观察到中断的触发。gt911芯片使用需要注意的有,芯片地址的脉冲配置,数据判断寄存器,中断清除。

(1)根据reset复位引脚和int中断引脚的脉冲的配置,设置芯片的iic通讯地址,

多点电容触摸驱动(基于gt911)_第1张图片

 

        但是gt911的iic通讯地址,只有低7位有效,所以需要将0xBA/0xBB的地址右移一位,得到iic的通讯地址为:0x5d。

(2)触摸点的数据缓冲区是否有数据,以及触摸点的个数,是一个重要的寄存器进行储存的。

多点电容触摸驱动(基于gt911)_第2张图片

 (3)在中断里将数据读取走之后,需要对“0x814E”寄存器写入0,不然int引脚就会一直产生中断的脉冲。

多点电容触摸驱动(基于gt911)_第3张图片

多点电容触摸驱动(基于gt911)_第4张图片

二、iic子系统

        韦老师的开发板是gt911的芯片通讯的iic是使用的imx6ull的i2c,设备树中gt911的设备结点时挂载到i2c的结点下面的。编写i2c的时候可以先测试与设备树的匹配是否成功,当iic设备与设备树中的结点匹配成功之后,就会调用probe函数,这个可以测试是否匹配成功。在匹配成功之后,就可以写iic的发送和接收函数,这时候可以对gt911的其中一个可以读写的寄存器,先对寄存器写入,然后对寄存器进行读取,观察写入和读出的数据是否相同,相同就说明iic读写是成功的。

        注意iic通讯使用的通讯频率。

三、gpio硬件中断(中断线程化)

        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子系统

            多点电容的触摸屏的input上报是这里新的知识点,前面没有学习过的,分为多触点形式的上报和单触点形式的上报,需要有屏幕按下和抬起的两种情况的上报,两种情况缺少一种,在下面tslib的测试过程中就会有问题。下面将从input上报的重要函数,input初始化和上报的流程案例进行讲解。

        前面已经有input子系统上报的相关知识,这里只进行新的知识的记录。

1、input上报的重要函数和数据

(1)MT的两种协议

Type A :适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据 ( 此类型在实际使
用中非常少! )
Type B :适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个
触摸点的信息, FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力。
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 */

(2)上传时间的间隔函数

        对于 Type A 类型的设备,通过 input_mt_sync() 函数来隔离不同的触摸点数据信息,此函数
原型如下所示:
void input_mt_sync(struct input_dev *dev)
        此函数只要一个参数,类型为 input_dev ,用于指定具体的 input_dev 设备。 input_mt_sync()
函数会触发 SYN_MT_REPORT 事件,此事件会通知接收者获取当前触摸数据,并且准备接收
下一个触摸点数据。
         对于 Type B 类型的设备,上报触摸点信息的时候需要通过 input_mt_slot()函数区分是哪一
个触摸点, input_mt_slot() 函数原型如下所示:
void input_mt_slot(struct input_dev *dev, int slot)
        此函数有两个参数,第一个参数是 input_dev 设备,第二个参数 slot 用于指定当前上报的是
哪个触摸点信息。 input_mt_slot() 函数会触发 ABS_MT_SLOT 事件,此事件会告诉接收者当前
正在更新的是哪个触摸点 (slot) 的数据。
        Type B 设备驱动需要给每个识别出来的触摸点分配一个 slot ,后面使用这个 slot 来上报触
摸点信息。可以通过 slot ABS_MT_TRACKING_ID 来新增、替换或删除触摸点。一个非负数
ID 表示一个有效的触摸点, -1 这个 ID 表示未使用 slot 。一个以前不存在的 ID 表示这是一个
新加的触摸点,一个 ID 如果再也不存在了就表示删除了。

(3)Type A 上报时序

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
1 行,通过 ABS_MT_POSITION_X 事件上报第一个触摸点的 X 坐标数据,通过
input_report_abs 函数实现,下面同理。
2 行,通过 ABS_MT_POSITION_Y 事件上报第一个触摸点的 Y 坐标数据。
3 行,上报 SYN_MT_REPORT 事件,通过调用 input_mt_sync 函数来实现。
4 行,通过 ABS_MT_POSITION_X 事件上报第二个触摸点的 X 坐标数据。
5 行,通过 ABS_MT_POSITION_Y 事件上报第二个触摸点的 Y 坐标数据。
6 行,上报 SYN_MT_REPORT 事件,通过调用 input_mt_sync 函数来实现。
7 行,上报 SYN_REPORT 事件,通过调用 input_sync 函数实现。
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);

(4)Type B上报时序

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_SLOT 事件,也就是触摸点对应的 SLOT 。每次上报一个触摸点坐
标之前要先使用 input_mt_slot 函数上报当前触摸点 SLOT ,触摸点的 SLOT 其实就是触摸点 ID
需要由触摸 IC 提供。
2 行,根据 Type B 的要求,每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID ,通过
修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换或删除。具体用到
的函数就是 input_mt_report_slot_state ,如果是添加一个新的触摸点,那么此函数的第三个参数
active 要设置为 true linux 内核会自动分配一个 ABS_MT_TRACKING_ID 值,不需要用户去指
定具体的 ABS_MT_TRACKING_ID 值。
3 行,上报触摸点 0 X 轴坐标,使用函数 input_report_abs 来完成。
4 行,上报触摸点 0 Y 轴坐标,使用函数 input_report_abs 来完成。
5~8 行,和第 1~4 行类似,只是换成了上报触摸点 0 (X,Y) 坐标信息
9 行,当所有的触摸点坐标都上传完毕以后就得发送 SYN_REPORT 事件,使用 input_sync
函数来完成。
当一个触摸点移除以后,同样需要通过 SLOT 关联的 ABS_MT_TRACKING_ID 来处理,
时序如下所示:
1 ABS_MT_TRACKING_ID -1
2 SYN_REPORT
1 行,当一个触摸点 (SLOT) 移除以后,需要通过 ABS_MT_TRACKING_ID 事件发送一
-1 给内核。方法很简单,同样使用 input_mt_report_slot_state 函数来完成,只需要将此函数的
第三个参数 active 设置为 false 即可,不需要用户手动去设置 -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);

(5)input数据上报的重要函数

多点电容触摸驱动(基于gt911)_第5张图片

 

多点电容触摸驱动(基于gt911)_第6张图片

多点电容触摸驱动(基于gt911)_第7张图片

多点电容触摸驱动(基于gt911)_第8张图片

 多点电容触摸驱动(基于gt911)_第9张图片多点电容触摸驱动(基于gt911)_第10张图片

2、input上报的流程

        下面是自己在使用中的input上报系统的初始化和上报处理的代码示例:

(1)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);	/* 设备的注销 */
}

(2)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测试

        tslib的测试,前面已经将过,这里就注意触摸的input对应那些事件就好了。在文件“/etc/profile”文件中。

六、官方的驱动使用

        由于gt911没有硬件检测触摸的按下和松开,所以需要软件上进行处理,这里我写的驱动不知道如何解决次问题,所以可以采用gt911官方的驱动代码和设备树,韦东山也是用的官方的设备树和驱动,后面会将这部分整理的代码发到自己的gitee,方便后面自己使用。

七、将自己的触摸驱动加入到内核系统中

        (1)首先要验证自己的驱动是否可以使用,是否完成了所有的功能,当所有都验证之后,就可以进行下面的操作。

        (2)将自己写的驱动c文件拷贝到下面的文件夹:


/home/book/100ask_imx6ull-sdk/Linux-4.9.88/drivers/input

多点电容触摸驱动(基于gt911)_第11张图片

         (3)修改当前文件夹下的“Makefile”

多点电容触摸驱动(基于gt911)_第12张图片

              将自己的驱动文件添加进去。多点电容触摸驱动(基于gt911)_第13张图片

         (4)修改完成以后重新编译 linux 内核,然后用新的 zImage 启动开发板。如果驱动添加成功的话系统启动的时候就会输出如图 64.4.3.2 所示的信息:

注意:这时候触摸input对应的event可能会变,tslib测试的时候需要注意。

你可能感兴趣的:(单片机,嵌入式硬件)