主要内容---电容触摸屏驱动
1,触摸屏的基本工作原理
2, 电容触摸屏的驱动框架
3,电容触摸屏读取坐标的原理和硬件初始化
4,linux下多点触摸的协议
5,电容触摸屏的驱动编写--gt811
soc:s5pv210
触摸屏芯片:gt811
驱动编程:
1, 提供i2c client信息
Mach-smdkv210.c (arch\arm\mach-s5pv210)
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
/* To Be Updated */
{ I2C_BOARD_INFO("gt811_ts", 0x5d), },
};
make zImage -j2
更新内核:
cp -raf arch/arm/boot/zImage /tftpboot
[root@farsight devices]# ls
0-001b 0-0050 2-005d i2c-0 i2c-1 i2c-2
[root@farsight devices]# pwd
/sys/bus/i2c/devices
[root@farsight 2-005d]# ls
modalias name power subsystem uevent
[root@farsight 2-005d]# cat name
gt811_ts
2, 编写从设备驱动:
EINT14---GPH1_6
RESET ---GPD0_3
硬件初始化:
1, 设置INT引脚为输入态, RESET设置成高电平(内部上拉)
2, RESET输出为低, 延时1ms,转成输入态
3,延时至少20ms,通过i2c寻址判断是否有响应
4, 如果有响应就分一次或多次初始化配置106个寄存器
linux下多点触摸的协议:
两点:
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT //表示第一个点上报完毕
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT //表示第二个点上报完毕
SYN_REPORT // 所有点都上报完毕
如果用代码去实现:
input_event(dev, EV_ABS, ABS_MT_POSITION_X, 333);
input_event(dev, EV_ABS, ABS_MT_POSITION_Y, 133);
input_mt_sync(dev);
|
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
input_event(dev, EV_ABS, ABS_MT_POSITION_X, 433);
input_event(dev, EV_ABS, ABS_MT_POSITION_Y, 533);
input_mt_sync(dev);
input_sync(dev)
|
input_event(dev, EV_SYN, SYN_REPORT, 0);
触摸屏驱动框架:
触摸屏的坐标读取:
代码示例:
gt811_drv.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <plat/gpio-cfg.h>
#define TS_INT_GPIO S5PV210_GPH1(6)
#define TS_RESET_GPIO S5PV210_GPD0(3)
#define TS_MAX_WIDTH 800
#define TS_MAX_HEIGHT 480
//设计一个全局的设备对象
struct ts_dev{
int irqno;
struct i2c_client *client;
struct input_dev *ts_input;
struct work_struct ts_work;
};
struct ts_dev *gt811_dev;
int gt811_i2c_write(struct i2c_client *client, char *buf, int count)
{
int ret;
struct i2c_msg msg ;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = buf;
msg.len = count;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret==1?count:ret;
}
int gt811_i2c_read(struct i2c_client *client, char *buf, int count)
{
int ret;
struct i2c_msg msg ;
msg.addr = client->addr;
msg.flags = I2C_M_RD;
msg.buf = buf;
msg.len = count;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret==1?count:ret;
}
// char buf[128]={0x6, 0xa2, };
// 调用: gt811_i2c_read_combine(client, buf, 128)
int gt811_i2c_read_combine(struct i2c_client *client, char *buf, int count)
{
int ret;
struct i2c_msg msg[2] ;
//先写寄存器的地址---2个字节
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = &buf[0];
msg[0].len = 2;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = &buf[2];
msg[1].len = count-2;
ret = i2c_transfer(client->adapter, msg, 2);
return ret==2?count-2:ret;
}
static int gt811_tp_init(void)
{
int ret =-1;
uint8_t config_info[] = {
0x06,0xA2, //寄存器地址
0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0x05,0x55,0x15,0x55,0x25,0x55,\
0x35,0x55,0x45,0x55,0x55,0x55,0x65,0x55,0x75,0x55,0x85,0x55,0x95,0x55,0xA5,0x55,\
0xB5,0x55,0xC5,0x55,0xD5,0x55,0xE5,0x55,0xF5,0x55,0x1B,0x03,0x00,0x00,0x00,0x13,\
0x13,0x13,0x0F,0x0F,0x0A,0x50,0x30,0x0D,0x03,0x00,0x05,0x58,0x02,0x00,0x04,0x00,\
0x00,0x32,0x2C,0x34,0x2E,0x00,0x00,0x04,0x14,0x22,0x04,0x00,0x00,0x00,0x00,0x00,\
0x20,0x14,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x30,\
0x25,0x28,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x01\
};
/*配置屏的大小*/
printk("62:%x\n",config_info[62]);
printk("61:%x\n",config_info[61]);
printk("64:%x\n",config_info[64]);
printk("63:%x\n",config_info[63]);
config_info[62] = TS_MAX_WIDTH >> 8;
config_info[61] = TS_MAX_WIDTH & 0xff;
config_info[64] = TS_MAX_HEIGHT>> 8;
config_info[63] = TS_MAX_HEIGHT & 0xff;
ret = gt811_i2c_write(gt811_dev->client, config_info, ARRAY_SIZE(config_info));
if(ret < 0)
{
printk(KERN_ERR "GT811 Send config failed!\n");
return ret;
}
return 0;
}
static void gt811_touch_report(s32 id,u16 x,u16 y, u8 w)
{
swap(x,y);
input_report_abs(gt811_dev->ts_input, ABS_MT_POSITION_X, x);
input_report_abs(gt811_dev->ts_input, ABS_MT_POSITION_Y, y);
input_report_abs(gt811_dev->ts_input, ABS_MT_PRESSURE, w);
input_report_abs(gt811_dev->ts_input, ABS_MT_TRACKING_ID, id);
input_mt_sync(gt811_dev->ts_input);
printk("input_x=%d,input_y=%d,input_w=%d, id =%d \n", x, y, w, id);
}
void gt811_irq_work(struct work_struct *work)
{
// 1, 从触控ic中读取坐标数据
uint8_t point_data[36] = {0x7,0x21,0};
uint8_t read_position = 0; //校验码位置
uint8_t position = 0;
uint8_t track_id[5];
uint8_t point_count=0;
uint8_t point_index = 0;
int count;
uint8_t check_sum=0;
int ret;
uint16_t ts_x;
uint16_t ts_y;
uint8_t ts_p;
ret = gt811_i2c_read_combine(gt811_dev->client, point_data, ARRAY_SIZE(point_data));
if(ret < 0)
{
printk("gt811_i2c_read_combine error\n");
return ;
}
// 2, 解析坐标数据
// 2.1,比对校验码
switch(point_data[2] & 0x1f)
{
case 0:
read_position = 3;
break;
case 1:
for(count=2; count<9; count++)
check_sum += (int)point_data[count];
read_position = 9;
break;
case 2:
case 3:
for(count=2; count<14;count++)
check_sum += (int)point_data[count];
read_position = 14;
break;
default:
for(count=2; count<35;count++)
check_sum += (int)point_data[count];
read_position = 35;
}
//进行比较
if(check_sum != point_data[read_position])
{
printk(KERN_ERR "coor chksum error!\n");
}
// 2.2 解析出坐标
// 必须知道当前有几个点
int i;
uint8_t finger_data = point_data[2] & 0x1f;
uint8_t tmp_data = finger_data;
for(i=0; i<5; i++)
{
if(tmp_data & 0x1)//被点击
{
// track_id记录了具体那几个点
track_id[point_count++] = i;//i表示第几个点
}
tmp_data = tmp_data>>1;
}
//读取坐标出来
for(i=0; i<point_count; i++)
{
if(track_id[i] != 3)
{
// 0, 1, 2, 4
if(track_id[i] < 3){
// 0, 1, 2
position = 4 + (5 * track_id[i]);
}else{
position = 30;
}
ts_x = point_data[position]<<8 | point_data[position+1];
ts_y = point_data[position+2]<<8 | point_data[position+3];
ts_p = point_data[position+4];
}else{
ts_x = point_data[19]<<8 | point_data[26];
ts_y = point_data[27]<<8 | point_data[28];
ts_p = point_data[29];
}
// 3, 上报给用户
gt811_touch_report(track_id[i], ts_x, ts_y, ts_p);
}
input_sync(gt811_dev->ts_input);
}
irqreturn_t gt811_irq_svc(int irqno, void *dev_id)
{
//printk("-----------------%s---------------\n", __FUNCTION__);
//启动下半部
schedule_work(>811_dev->ts_work);
return IRQ_HANDLED;
}
int gt811_ts_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("-----------------%s---------------\n", __FUNCTION__);
int ret;
// 1, 实例化全局设备对象
gt811_dev = kzalloc(sizeof(struct ts_dev), GFP_KERNEL);
// 2, 记录当前的client
gt811_dev->client = client;
// 3, 硬件初始化-- i2c_tranfer + 中断初始化
/*
1, 设置INT引脚为输入态, RESET设置成高电平(内部上拉)
2, RESET输出为低, 延时1ms,转成输入态
3,延时至少20ms,通过i2c寻址判断是否有响应
4, 如果有响应就分一次或多次初始化配置106个寄存器
*/
gpio_request(TS_INT_GPIO, "TS_INT");
gpio_direction_input(TS_INT_GPIO);
gpio_free(TS_INT_GPIO);
gpio_request(TS_RESET_GPIO, "reset_gpio");
s3c_gpio_setpull(TS_RESET_GPIO, S3C_GPIO_PULL_UP);
gpio_free(TS_RESET_GPIO);
//稍微延时一下
msleep(5);
gpio_request(TS_RESET_GPIO, "reset_gpio");
gpio_direction_output(TS_RESET_GPIO, 0);
msleep(1);
gpio_direction_input(TS_RESET_GPIO);
gpio_free(TS_RESET_GPIO);
msleep(50);
//测试是否有数据
char value = 1;
ret = i2c_master_send(gt811_dev->client, &value, 1);
if(ret > 0)
{
printk("gt811 device found ok\n");
}else{
printk("gt811 device not found \n");
kfree(gt811_dev);
return -ENODEV;
}
// 初始化配置106个寄存器--将值写入到寄存器中
gt811_tp_init();
//先初始化中断下半部
// 参数1--初始化哪个对象 参数2--下半部要做的事情
INIT_WORK(>811_dev->ts_work, gt811_irq_work);
//申请中断
gt811_dev->irqno = IRQ_EINT(14);
ret = request_irq(gt811_dev->irqno, gt811_irq_svc, IRQF_TRIGGER_FALLING, "ts_irq", NULL);
if(ret != 0)
{
printk("request_irq error \n");
kfree(gt811_dev);
return ret;
}
//4, 分配input device
gt811_dev->ts_input = input_allocate_device();
// 5,初始化input device
//提供额外的信息给用户查看--/sys/class/input/eventX/device
gt811_dev->ts_input->name = "gt811_ts";
gt811_dev->ts_input->phys = "ts/input0";
gt811_dev->ts_input->uniq = "gt811/ts";
gt811_dev->ts_input->id.bustype = BUS_I2C;
gt811_dev->ts_input->id.vendor = 0x2B2B;
gt811_dev->ts_input->id.product = 0x0008;
gt811_dev->ts_input->id.version = 0x0002;
//能够产生哪些数据类型--绝对数据
//gt811_dev->ts_input->evbit[0] |= BIT_MASK(EV_ABS);
__set_bit(EV_ABS, gt811_dev->ts_input->evbit);
//产生哪些绝对数据
__set_bit(ABS_X, gt811_dev->ts_input->absbit);
__set_bit(ABS_Y, gt811_dev->ts_input->absbit);
__set_bit(ABS_PRESSURE, gt811_dev->ts_input->absbit);
__set_bit(ABS_MT_POSITION_X, gt811_dev->ts_input->absbit);
__set_bit(ABS_MT_POSITION_Y, gt811_dev->ts_input->absbit);
__set_bit(ABS_MT_PRESSURE, gt811_dev->ts_input->absbit);
//绝对值是有最大和最小的
/*
参数1: input设备
参数2: 坐标方向
参数3: 最小值
参数4:最大值
参数5:表示误差是多少,一般可以直接设置成0
参数6: 表示中心位置,一般都是设置为0
*/
input_set_abs_params(gt811_dev->ts_input, ABS_X, 0, 800, 0, 0);
input_set_abs_params(gt811_dev->ts_input, ABS_Y, 0, 480, 0, 0);
input_set_abs_params(gt811_dev->ts_input, ABS_PRESSURE, 0, 256, 0, 0);
input_set_abs_params(gt811_dev->ts_input, ABS_MT_POSITION_X, 0, 800, 0, 0);
input_set_abs_params(gt811_dev->ts_input, ABS_MT_POSITION_Y, 0, 480, 0, 0);
input_set_abs_params(gt811_dev->ts_input, ABS_MT_PRESSURE, 0, 256, 0, 0);
// 6, 注册input device
ret = input_register_device(gt811_dev->ts_input);
return 0;
}
int gt811_ts_drv_remove(struct i2c_client *client)
{
printk("-----------------%s---------------\n", __FUNCTION__);
free_irq(gt811_dev->irqno, NULL);
input_unregister_device(gt811_dev->ts_input);
input_free_device(gt811_dev->ts_input);
kfree(gt811_dev);
return 0;
}
const struct i2c_device_id gt_id_table[] = {
{"gt811_ts", 0x811},
{"gt911_ts", 0x911},
i2c_adapter
struct i2c_driver gt811_drv = {
.probe = gt811_ts_drv_probe,
.remove = gt811_ts_drv_remove,
.driver = {
.name = "gt811_ts_drv", //不会用于比对
},
.id_table = gt_id_table,
};
static int __init gt811_drv_init(void)
{
//注册一个i2c driver
return i2c_add_driver(>811_drv);
}
static void __exit gt811_drv_exit(void)
{
i2c_del_driver(>811_drv);
}
module_init(gt811_drv_init);
module_exit(gt811_drv_exit);
MODULE_LICENSE("GPL");