多点触控

项目介绍 :多点电容式触摸屏控制芯片Guitar,采用投射式电容检测原理,由15个驱动通道与10个感应通道组成触摸检测网络,通过内置模拟放大电路、数字运算模块,及高性能MPU得到实时准确的触 摸信息,并通过I2C传输给主控芯片。实现“所点 即所得”的非凡用户体验。 Guitar可同时识别5个触摸点位的实时准确 位置,移动轨迹及触摸力度。并可根据主控需要,读取相应点数的触摸信息。本项目主要通过i2c子系统及input子系统完成驱 动与硬件的命令和信息交互,掌握常见触摸屏的工作原理 。

l三个部分对多点触控进行支持
1)在注册input子系统时需要添加对多点触控支持,代码如下(ps原有的设置不能少):
input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); //设置多点触控模式下的压力值的范围为0-255
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_HEIGHT, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_WIDTH, 0, 0);
2)需要通过算法实现对多点信息的记录,这部分头文件已经支持
a,基本数据结构
struct point_node
{
unsigned int num; //触点编号
unsigned int state; //触点状态,state?down:up
};
struct point_queue //触点信息结构体
{
int length; //触点数目
struct point_node pointer[MAX_FINGER_NUM]; //触点数组
};

b,数据操作接口(实现过程请查阅goodix_touch_v2.2)
//删除触点信息
static void del_point(struct point_queue *point_list)
//添加触点信息
static int add_point(struct point_queue *point_list, int num)
//将某个触点的状态修改为抬起(up)
static int set_up_point(struct point_queue *point_list, int num)
c,处理思路(data[32]为从控制芯片获取到的数据)
  1,定义一个全局的struct point_queue变量finger_list
定义了无符号8bit变量的静态局部变量 finger_bit 用于存储上一次的触点标志(触摸屏控制芯片中的0x00寄存器的值,用于判断触点数)
  2,仅保留data[1]的低5位数据(因为芯片仅支持最多5点触控),然后将新的标志和旧的做比较,方法如下:
finger = finger_bit^data[1];//'^'是异或运算,如果标志在前后发生变化,那么finger上对应就为1
finger_bit=0 && data[1]=0 表示没有动作,如果仅finger_bit=0,那么当前标志没变,剩下一种情况就是标志发生变化,就需要去对finger_list进行处理(使用接口)
根据finger低5位的数值去处理数据,循环操作,当finger的某位为1时表示有变化,如果finger_bit上对应的位上为1那么表示抬起,调用set_up_point,反之为按下,调用add_point.
  3需要注意,处理完后用finger_bit去保存data[1],然后在上报玩信息后清空finger_list。
3)多点信息的上报,在原来的上报过程中加入一段多点上报,在驱动中,输入子系统中用mt(multi-touch)协议来处理多点触控:
if(finger_list.length > 0 && finger_list.pointer[0].state == FLAG_DOWN)
{
input_report_abs(goodix->input_dev, ABS_X, (800-pos[0][0]));
input_report_abs(goodix->input_dev, ABS_Y, pos[0][1]);

if(finger_list.length > 0)
{
input_report_abs(goodix->input_dev, ABS_PRESSURE, pressure[0]);
input_report_key(goodix->input_dev, BTN_TOUCH,  finger_list.pointer[0].state);      
}
/* 加上下面的循环即可支持多点上报 */
for(count = 0; count < finger_list.length; count++)
{
input_report_abs(goodix->input_dev, ABS_MT_POSITION_X, (800 -pos[count][0]));
input_report_abs(goodix->input_dev, ABS_MT_POSITION_Y, pos[count][1]);
input_report_abs(goodix->input_dev, ABS_MT_PRESSURE, pressure[count]);
input_report_abs(goodix->input_dev, ABS_MT_TOUCH_MAJOR, pressure[count]);
input_mt_sync(goodix->input_dev);
}
input_sync(goodix->input_dev);
del_point(&finger_list);


4)、单点和多点上报互不影响,因为用户读取单点和多点是分开读取的,所以即使上报了单点坐标,再多点上报时重复上报第一个坐标不会重复读取。
5)、无论单点还是多点上报结束后都要input_sync()同步,而多点时input_mt_sync()是在每一个点完成后要同步一次。
code:
ABS_X 0x00 0
ABS_Y 0x01 1
ABS_PRESSURE 0x18 24
ABS_MT_TOUCH_MAJOR 0x30 48
ABS_MT_POSITION_X 0x35 53
ABS_MT_POSITION_Y 0x36 54
ABS_MT_PRESSURE 0x3a 58
BTN_TOUCH 0x14a 330

goodix_touch_v3.0.h

#ifndef __GOODIX_TOUCH_V3_H__
#define __GOODIX_TOUCH_V3_H__


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/earlysuspend.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/irq.h>
#define GOODIX_I2C_NAME "Goodix-TS"
#define TOUCH_MAX_HEIGHT 7680
#define TOUCH_MAX_WIDTH 5120
#if 0
#define SCREEN_MAX_HEIGHT 480
#define SCREEN_MAX_WIDTH 272
#else
#define SCREEN_MAX_HEIGHT 800
#define SCREEN_MAX_WIDTH 480
#endif

#define MAX_FINGER_NUM 5 //最大支持手指数(<=5)
#define FLAG_UP 0
#define FLAG_DOWN 1

/* need modify if platform changed */
#define  INT_PORT  S5P_EXT_INT0(4) //Int IO port;GPH0CON[4]/EXT_INT[4]
#define  INT_FUNC           gpio_to_irq(INT_PORT)
/* need modify if platform changed */
#define SHUTDOWN_PORT S5P_EXT_INT1(3) //SHUTDOWN管脚号  GPH1CON[3]/EXT_INT[11]  

typedef int (*pos_t)[2];
struct goodix_priv {
struct i2c_client *cli;
struct input_dev *input_dev;
struct work_struct  work;
void *privata_data;
};

struct point_node
{
unsigned int num;
unsigned int state;
};

struct point_queue
{
int length;
struct point_node pointer[MAX_FINGER_NUM];
};

struct point_queue  finger_list; //record the fingers list 
static void del_point(struct point_queue *point_list)
{
int count = point_list->length-1;
int position;
for(; count >= 0; count--) //note: must search from tail to head
            if(point_list->pointer[count].state == FLAG_UP)
            {             
if(point_list->length == 0 )
return ;
position = count;
for(; position < MAX_FINGER_NUM -1; position++)
point_list->pointer[position] = point_list->pointer[position+1];
point_list->length--;
            }
}

static int add_point(struct point_queue *point_list, int num)
{
if(point_list->length >= MAX_FINGER_NUM || num < 0 )
return -1;
point_list->pointer[point_list->length].num = num;
point_list->pointer[point_list->length].state = FLAG_DOWN;
point_list->length++;
return 0;
}

static int set_up_point(struct point_queue *point_list, int num)
{
int count = 0;
if(point_list->length <= 0 || num < 0 || num > MAX_FINGER_NUM)
return -1; //no data
for(; count < point_list->length; count++)
if(point_list->pointer[count].num == num)
{
            point_list->pointer[count].state = FLAG_UP;
            return 0;
}
else continue;
return -1;
}

static int goodix_analysis_data(uint8_t *point_data, struct goodix_priv *goodix, pos_t pos, unsigned int *pressure)
{
static uint8_t  finger_bit=0;
uint8_t finger=0;
int position =0;
int count = 0, read_position = 0;
int check_sum = 0;
//The bit indicate which fingers pressed down
switch(point_data[1]& 0x1f)
{
case 0:
case 1:
for(count=1; count<8; count++)
check_sum += (int)point_data[count];

if((check_sum%256) != point_data[8])
goto XFER_ERROR;
break;
case 2:
case 3:
for(count=1; count<13;count++)
check_sum += (int)point_data[count];
if((check_sum%256) != point_data[13])
goto XFER_ERROR;
break;
default: //(point_data[1]& 0x1f) > 3
for(count=1; count<34;count++)
check_sum += (int)point_data[count];
if((check_sum%256) != point_data[34])
goto XFER_ERROR;
}

point_data[1]&=0x1f;
finger = finger_bit^point_data[1];
if(finger == 0 && point_data[1] == 0)
goto NO_ACTION; //no fingers and no action

else if(finger == 0) //the same as last time
goto BIT_NO_CHANGE;
//check which point(s) DOWN or UP
for(position = 0; (finger !=0)&& (position < MAX_FINGER_NUM);  position++)
{
if((finger&0x01) == 1) //current bit is 1?
{ //NO.postion finger is change

if(((finger_bit>>position)&0x01)==1) //NO.postion finger is UP
set_up_point(&finger_list, position);
else 
add_point(&finger_list, position);

}
finger>>=1;
}


BIT_NO_CHANGE:
for(count = 0; count < finger_list.length; count++)
{
if(finger_list.pointer[count].state == FLAG_UP)
{
pos[count][0] = pos[count][1] = 0;
pressure[count] = 0;
continue;
}

if(finger_list.pointer[count].num < 3)
read_position = finger_list.pointer[count].num*5 + 3;
else if (finger_list.pointer[count].num == 4)
read_position = 29;

if(finger_list.pointer[count].num != 3)
{
pos[count][0] = (unsigned int) (point_data[read_position]<<8) + (unsigned int)( point_data[read_position+1]);
pos[count][1] = (unsigned int)(point_data[read_position+2]<<8) + (unsigned int) (point_data[read_position+3]);
pressure[count] = (unsigned int) (point_data[read_position+4]);
}
else 
{
pos[count][0] = (unsigned int) (point_data[18]<<8) + (unsigned int)( point_data[25]);
pos[count][1] = (unsigned int)(point_data[26]<<8) + (unsigned int) (point_data[27]);
pressure[count] = (unsigned int) (point_data[28]);
}

#ifdef GOODIX_TS_DEBUG
if(count == 3)
dev_info(&(goodix->cli->dev), "Original data: Count:%d Index:%d X:%d Y:%d\n",count, finger_list.pointer[count].num,  pos[count][0], pos[count][1]);
#endif
 
pos[count][0] = (TOUCH_MAX_WIDTH - pos[count][0])*SCREEN_MAX_WIDTH/TOUCH_MAX_WIDTH;//y,
pos[count][1]=  pos[count][1]*SCREEN_MAX_HEIGHT/TOUCH_MAX_HEIGHT ; //x,
swap(pos[count][0] ,pos[count][1]); 
#ifdef GOODIX_TS_DEBUG
if(count == 3)
dev_info(&(goodix->cli->dev), " Coordinate: Count:%d Index:%d X:%d Y:%d\n",count, finger_list.pointer[count].num, pos[count][0], pos[count][1]);
#endif
}

finger_bit=point_data[1];
return 0;

XFER_ERROR:
NO_ACTION:
return -1;
}

#endif

goodix_touch_v3.0.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/earlysuspend.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/irq.h>
#include "driverapp.h"

#define debug_goodix(fmt, arg...)  printk(KERN_INFO fmt, ##arg);

/* other cpt touch can use this workqueue */
static struct workqueue_struct *goodix_wq;

static const struct i2c_device_id goodix_ts_id[] = {
{ GOODIX_I2C_NAME, 0 },
{ }
};
static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len)
{
/* just one i2c_msg OK. */
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = !I2C_M_RD;
msg.len = len;
msg.buf = data;


return 1-i2c_transfer(client->adapter,&msg,1);
}
static int i2c_read_bytes(struct i2c_client *client, uint8_t *data, int len)
{
/* u need two i2c_msg */
struct i2c_msg msg[2];

msg[0].addr = client->addr;
msg[0].flags = !I2C_M_RD;
msg[0].len = 1;
msg[0].buf = data;


msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = len-1;
msg[1].buf = data+1;


return 2-i2c_transfer(client->adapter,msg,2);
}

static int goodix_read_panel(struct goodix_priv *goodix, uint8_t *data, int size)
{
return i2c_read_bytes(goodix->cli, data, size);
}


static int goodix_init_panel(struct goodix_priv *goodix)
{


uint8_t config_info[54]={0x30, 0x19,0x05,0x05,0x28,0x02,0x14,0x14,0x10,0x32,0xF8,0x14,0x00,0x1E,0x00,0xED,0xCB,
0xA9,0x87,0x65,0x43,0x21,0x01,0x00,0x00,0x35,0x2E,0x4D,0xC0,0x20,0x01,0x01,0x83,
0x50,0x3C,0x1E,0x28,0x00,0x33,0x2C,0x01,0xEC,0x00,0x3C,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01};


return i2c_write_bytes(goodix->cli, config_info, sizeof(config_info));
}

static void goodix_ts_work_func(struct work_struct *work)
{
uint8_t  point_data[35]={ 0 };
int ret = 0;
int count;
int pos[MAX_FINGER_NUM][2];
unsigned int pressure[MAX_FINGER_NUM];
disable_irq(INT_FUNC);
struct goodix_priv *goodix = container_of(work, struct goodix_priv, work);  

ret = goodix_read_panel(goodix, point_data, sizeof(point_data));


ret = goodix_analysis_data(point_data, goodix, pos, pressure);


if(finger_list.length > 0 && finger_list.pointer[0].state == FLAG_DOWN)
{
input_report_abs(goodix->input_dev, ABS_X, (800-pos[0][0]));
input_report_abs(goodix->input_dev, ABS_Y, pos[0][1]);

if(finger_list.length > 0)
{
input_report_abs(goodix->input_dev, ABS_PRESSURE, pressure[0]);
input_report_key(goodix->input_dev, BTN_TOUCH,  finger_list.pointer[0].state);      
}
printk("%d: len = %d,up_down = %d \n",__LINE__,finger_list.length,finger_list.pointer[0].state);
for(count = 0; count < finger_list.length; count++)
{/* 多点 */
input_report_abs(goodix->input_dev, ABS_MT_POSITION_X, (800 -pos[count][0]));
input_report_abs(goodix->input_dev, ABS_MT_POSITION_Y, pos[count][1]);
input_report_abs(goodix->input_dev, ABS_MT_PRESSURE, pressure[count]);
input_report_abs(goodix->input_dev, ABS_MT_TOUCH_MAJOR, pressure[count]);
input_mt_sync(goodix->input_dev);
}
input_sync(goodix->input_dev);
del_point(&finger_list);
enable_irq(INT_FUNC);
return ;


}

static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
struct goodix_priv *ts = dev_id;


queue_work(goodix_wq, &ts->work);

return IRQ_HANDLED;
}


static int goodix_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct goodix_priv *goodix;
int ret;
int retry = 5;
    /*allocate memory for goodix — kmalloc*/
goodix = (struct goodix_priv *)kzalloc(sizeof(*goodix),GFP_KERNEL);

ret = gpio_request(SHUTDOWN_PORT, "GOODIX_TS SHUTDOWN PORT");
if (ret < 0)  {
dev_err(&client->dev, "Failed to request GPIO:%d, ERRNO:%d\n",(int)SHUTDOWN_PORT,ret);
goto err_check_functionality_failed;
}

/* set gpio output & 0; goodix step into normal mode */
gpio_direction_output(SHUTDOWN_PORT, 0); 

ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
if (ret == 0) {
dev_err(&client->dev, "Soc no I2C function, ERRNO:%d\n", ret);
goto err_check_functionality_failed;
}

msleep(16);//step into normal mode, need 16ms
dev_info(&client->dev, "chip goodix work...\t i2c connecttion work...\t now we request irq and input_dev\n");
/*init goodix variable*/
i2c_set_clientdata(client,goodix);

goodix->cli = client;

/* init work of workqueue*/
INIT_WORK(&(goodix->work),goodix_ts_work_func);

/* input dev init and register */
goodix->input_dev = input_allocate_device();
if(IS_ERR(goodix->input_dev)){
debug_goodix("allocate input device fail!\n");
ret = PTR_ERR(goodix->input_dev);
goto err_check_functionality_failed;
}


goodix->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
set_bit(BTN_TOUCH, goodix->input_dev->keybit);
set_bit(ABS_X, goodix->input_dev->absbit);
set_bit(ABS_Y, goodix->input_dev->absbit);
set_bit(ABS_PRESSURE, goodix->input_dev->absbit);

input_set_abs_params(goodix->input_dev, ABS_X, 0, SCREEN_MAX_HEIGHT, 0, 0);
input_set_abs_params(goodix->input_dev, ABS_Y, 0, SCREEN_MAX_WIDTH, 0, 0);
input_set_abs_params(goodix->input_dev, ABS_PRESSURE, 0 , 255, 0, 0);
/* 多点 */
input_set_abs_params(goodix->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(goodix->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(goodix->input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_HEIGHT, 0, 0);
input_set_abs_params(goodix->input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_WIDTH, 0, 0);
finger_list.length = 0;
ret = input_register_device(goodix->input_dev);
if(ret){
debug_goodix("input device register fail\n");
ret = -EFAULT;
goto input_err;
}

/* request_irq */
ret = request_irq(INT_FUNC,goodix_ts_irq_handler,IRQ_TYPE_EDGE_FALLING,"touchscreen",goodix);
if(ret){
debug_goodix("request irq fail!\n");
goto irq_err;
}
/* init hardware*/

while(retry--)
ret = goodix_init_panel(goodix);
if(ret){
debug_goodix("init hardware fail\n");
goto hardware_err;
}
return 0;

hardware_err:
free_irq(INT_FUNC,goodix);
irq_err:
input_unregister_device(goodix->input_dev);
input_err:
input_free_device(goodix->input_dev);
err_check_functionality_failed:
kfree(goodix);
return ret;
}

static int goodix_ts_remove(struct i2c_client *client)
{
struct goodix_priv *goodix;
goodix = (struct goodix_priv *)i2c_get_clientdata(client);
free_irq(INT_FUNC,goodix);

input_unregister_device(goodix->input_dev);
input_free_device(goodix->input_dev);
kfree(goodix);
gpio_free(SHUTDOWN_PORT);

return 0;
}
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
.id_table = goodix_ts_id,
.class = I2C_CLASS_HWMON,
.driver = {
.name = GOODIX_I2C_NAME,
.owner = THIS_MODULE,
},
};


static int __devinit cpt_ts_init(void)
{
int ret;
printk(KERN_INFO "Touchscreen driver of guitar is installing...\n");
goodix_wq = create_workqueue("goodix_wq");//success means not null
if (!goodix_wq) {
printk(KERN_ALERT "creat workqueue faiked\n");
return -ENOMEM;

}


ret=i2c_add_driver(&goodix_ts_driver);
return ret;
}


static void __devexit cpt_ts_exit(void)
{
printk(KERN_INFO "Touchscreen driver of guitar is exiting...\n");
i2c_del_driver(&goodix_ts_driver);
if (goodix_wq)
destroy_workqueue(goodix_wq);
return ;
}


module_init(cpt_ts_init);
module_exit(cpt_ts_exit);


MODULE_DESCRIPTION("Goodix Touchscreen Driver");
MODULE_LICENSE("GPL");

goodix_dev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/irq.h>
static struct i2c_board_info dev_test_info = {
I2C_BOARD_INFO("Goodix-TS",0x55),
};

static struct i2c_client *dev_test_client;
static int dev_test_dev_init(void)
{
struct i2c_adapter *i2c_adap = NULL;


i2c_adap = i2c_get_adapter(0);

dev_test_client = i2c_new_device(i2c_adap,&dev_test_info);


return 0;
}

static void dev_test_dev_exit(void)
{
i2c_unregister_device(dev_test_client);
}

module_init(dev_test_dev_init);
module_exit(dev_test_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("sunplusedu");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值