一,了解ts的基础知识
参考:E:\peter\2010\高级驱动\6day\ts驱动专题.xls
二,电容触摸屏的驱动框架
参考:E:\peter\2010\高级驱动\6day\6day_photo\3_ts驱动框架.tif
三,电容触摸屏获取坐标的原理
参考:E:\peter\2010\高级驱动\6day\6day_photo\4_读取坐标原理.tif
四,电容触摸屏的驱动编程
1,添加从设备信息
arch/arm/mach-s5pv210/mach-smdkv210.c
|
static void __init smdkv210_machine_init(void)
|
i2c_register_board_info(2, smdkv210_i2c_devs2,ARRAY_SIZE(smdkv210_i2c_devs2));
|
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
/* To Be Updated */
{ I2C_BOARD_INFO("gt811_ts", 0x5d), }, //触摸屏从设备信息
};
//重新编译内核,并更新:
make -j2 zImage
cp arch/arm/boot/zImage /tftpboot/
//在开发板中测试
[root@farsight /]# ls /sys/bus/i2c/devices/
0-001b 0-0050 2-005d i2c-0 i2c-1 i2c-2
[root@farsight /]# cat /sys/bus/i2c/devices/2-005d/name
gt811_ts
2,触摸屏驱动代码实现------//框架
//参考代码:gt811_drv_v1
编译,并在开发板中测试:
[root@farsight /drv_module]# insmod gt811_i2c_drv.ko
----------^_^ gt811_i2c_drv_init---------------
----------^_^ gt811_i2c_drv_probe---------------
[root@farsight /drv_module]# ls /sys/bus/i2c/drivers
dummy gt811_s5pv210
[root@farsight /drv_module]# ls /sys/bus/i2c/drivers/gt811_s5pv210/
2-005d bind module uevent unbind
3,触摸屏驱动代码实现------//实现probe函数中的代码
//参考代码:gt811_drv_v2
编译,并在开发板中测试:
[root@farsight /drv_module]# insmod gt811_i2c_drv.ko
----------^_^ gt811_i2c_drv_init---------------
----------^_^ gt811_i2c_drv_probe---------------
input: Unspecified device as /devices/virtual/input/input0
[root@farsight /drv_module]# ls /dev/event0 -l
crw-rw---- 1 0 0 13, 64 Jan 1 00:39 /dev/event0
[root@farsight /drv_module]# ls /sys/bus/i2c/drivers/
dummy gt811_s5pv210
[root@farsight /drv_module]# ls /sys/bus/i2c/drivers/gt811_s5pv210/
2-005d bind module uevent unbind
[root@farsight /drv_module]# cat /sys/bus/i2c/drivers/gt811_s5pv210/2-005d/name
gt811_ts
//如果同时加载了多个输入设备,则:
[root@farsight /drv_module]# insmod input_simple_key_drv.ko
--------------^_^ input_simple_key_drv_init---------------
input: Unspecified device as /devices/virtual/input/input1
[root@farsight /drv_module]# ls -l /dev/event*
crw-rw---- 1 0 0 13, 64 Jan 1 00:39 /dev/event0
crw-rw---- 1 0 0 13, 65 Jan 1 00:41 /dev/event1
//如何区分上面的两个设备节点对应哪个设备?
//给input_dev添加额外信息
//添加额外信息
gt811_dev->i_dev->name = "gt811_ts";
gt811_dev->i_dev->phys = "ts_gt811";
gt811_dev->i_dev->uniq = "/ts/gt811";
gt811_dev->i_dev->id.bustype = 0x4;
gt811_dev->i_dev->id.product = 0x5;
gt811_dev->i_dev->id.vendor = 0x123;
gt811_dev->i_dev->id.version = 0x1;
//在开发板中测试:
[root@farsight /drv_module]# cat /sys/class/input/event0/device/name
gt811_ts
[root@farsight /drv_module]# cat /sys/class/input/event0/device/uniq
/ts/gt811
[root@farsight /drv_module]# cat /sys/class/input/event0/device/phys
ts_gt811
[root@farsight /drv_module]# cat /sys/class/input/event0/device/id/bustype
0004
[root@farsight /drv_module]# cat /sys/class/input/event0/device/id/product
0005
[root@farsight /drv_module]# cat /sys/class/input/event0/device/id/vendor
0123
[root@farsight /drv_module]# cat /sys/class/input/event0/device/id/version
0001
4,触摸屏驱动代码实现------//触控IC初始化
INT ---- gph1_6
RESET --- gpd0_3
//硬件初始化步骤
1》设置INT为输入态,RESET为内部拉高
2》RESET输出低,延时1ms,转为输入态
3》延时至少20ms ,i2c寻址gt811--监测gt811是否存在,且没有损坏
4》如果有响应,则分一次或者多次初始化配置寄存器:[0x6A2]-[0x70B]共 106 个寄存器
//参考代码:gt811_drv_v3
5,触摸屏驱动代码实现------//中断处理函数:中断下半部
//参考代码:gt811_drv_v4
//使用工作队列实现中断下半部
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; //下半部执行函数指针:typedef void (*work_func_t)(struct work_struct *work);
};
//初始化工作队列 --------在probe中调用
INIT_WORK(struct work_struct *work,work_func_t _func)
//启动中断下半部 ------- 在中断处理函数中调用
int schedule_work(struct work_struct *work)
//在开发板中测试:
[root@farsight /drv_module]# insmod gt811_i2c_drv.ko
----------^_^ gt811_i2c_drv_init---------------
----------^_^ gt811_i2c_drv_probe---------------
input: gt811_ts as /devices/virtual/input/input5
gt811 device found OK!
----------^_^ gt811_ts_init---------------
config_info[62] = 0x3
config_info[61] = 0x20
config_info[64] = 0x1
config_info[63] = 0xe0
[root@farsight /drv_module]# input_x = 314,input_y = 189,input_p = 6,input_id = 0
input_x = 314,input_y = 189,input_p = 6,input_id = 0
input_x = 314,input_y = 189,input_p = 6,input_id = 0
input_x = 314,input_y = 189,input_p = 6,input_id = 0 //按下一个点
input_x = 408,input_y = 191,input_p = 6,input_id = 0 //按下两个点
input_x = 357,input_y = 319,input_p = 10,input_id = 1
input_x = 408,input_y = 191,input_p = 6,input_id = 0
input_x = 308,input_y = 180,input_p = 5,input_id = 0 //按下三个点
input_x = 423,input_y = 240,input_p = 8,input_id = 1
input_x = 349,input_y = 407,input_p = 5,input_id = 2
input_x = 308,input_y = 181,input_p = 5,input_id = 0
input_x = 423,input_y = 239,input_p = 8,input_id = 1
input_x = 349,input_y = 407,input_p = 5,input_id = 2
input_x = 308,input_y = 181,input_p = 5,input_id = 0
input_x = 132,input_y = 154,input_p = 8,input_id = 0 //按下四个点
input_x = 407,input_y = 208,input_p = 3,input_id = 1
input_x = 521,input_y = 268,input_p = 6,input_id = 2
input_x = 406,input_y = 407,input_p = 5,input_id = 3
input_x = 131,input_y = 155,input_p = 10,input_id = 0
input_x = 407,input_y = 208,input_p = 5,input_id = 1
input_x = 521,input_y = 268,input_p = 6,input_id = 2
input_x = 406,input_y = 407,input_p = 5,input_id = 3
input_x = 131,input_y = 155,input_p = 10,input_id = 0
input_x = 407,input_y = 208,input_p = 5,input_id = 1
input_x = 521,input_y = 268,input_p = 6,input_id = 2
input_x = 406,input_y = 407,input_p = 5,input_id = 3
input_x = 283,input_y = 101,input_p = 6,input_id = 0 //按下五个点
input_x = 518,input_y = 133,input_p = 3,input_id = 1
input_x = 615,input_y = 188,input_p = 6,input_id = 2
input_x = 615,input_y = 298,input_p = 5,input_id = 3
input_x = 88,input_y = 452,input_p = 8,input_id = 4
input_x = 280,input_y = 101,input_p = 6,input_id = 0
input_x = 517,input_y = 133,input_p = 3,input_id = 1
input_x = 615,input_y = 188,input_p = 6,input_id = 2
input_x = 615,input_y = 298,input_p = 5,input_id = 3
input_x = 88,input_y = 452,input_p = 6,input_id = 4
示例代码
1、pdrv
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <plat/gpio-cfg.h>
#define TOUCH_MAX_WIDTH 800
#define TOUCH_MAX_HEIGHT 480
//设计全局设备对象
struct s5pv210_gt811{
int irqno;
struct input_dev *i_dev;
struct i2c_client *client;
struct work_struct ts_work;
};
struct s5pv210_gt811 *gt811_dev;
int gt811_read_combine(struct i2c_client * client,char *buf,int count)
{
int ret;
struct i2c_msg msg[2];
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = &buf[0];
msg[1].addr = client->addr;
msg[1].flags = 1;
msg[1].len = count - 2;
msg[1].buf = &buf[2];
ret = i2c_transfer(client->adapter, msg, 2);
return (ret == 2) ? count-2 : ret;
}
//单点上报
void gt811_touch_report(uint8_t id, uint16_t x, uint16_t y, uint8_t p)
{
input_report_abs(gt811_dev->i_dev, ABS_MT_POSITION_X, x);
input_report_abs(gt811_dev->i_dev, ABS_MT_POSITION_X, y);
input_report_abs(gt811_dev->i_dev, ABS_MT_PRESSURE, p);
input_report_abs(gt811_dev->i_dev, ABS_MT_TRACKING_ID, id);
input_sync(gt811_dev->i_dev);
printk("input_x = %d,input_y = %d,input_p = %d,input_id = %d\n",x,y,p,id);
}
//实现中断下半部
void gt811_irq_fun(struct work_struct *work)
{
int ret,count,i;
uint8_t point_data[36] = {0x7,0x21,0};
uint8_t position; //记录校验和的位置
uint8_t check_sum = 0;
uint8_t tmp;
uint8_t track_id[5]; //被按下的点的编号
uint8_t point_count = 0; //被按下的点的个数
uint8_t point_position;
uint16_t ts_x,ts_y;
uint8_t ts_p;
// 1, 获取所有坐标数据
ret = gt811_read_combine(gt811_dev->client,point_data,ARRAY_SIZE(point_data));
if(ret < 0){
printk(" gt811_read_combine error\n");
}
// 2, 分许被按下的点的数据
// 2.1 比对校验和是否正确
switch(point_data[2] & 0x1f){
case 0: // 没有点被按下
position = 4;
for(count = 2; count < position; count++)
check_sum += point_data[count];
break;
case 1:
position = 9;
for(count = 2; count < position; count++)
check_sum += point_data[count];
break;
case 2:
case 3:
position = 14;
for(count = 2; count < position; count++)
check_sum += point_data[count];
break;
default:
position = 35;
for(count = 2; count < position; count++)
check_sum += point_data[count];
}
if(check_sum != point_data[position])
printk("check_sum error!\n");
// 2.2 判断有几个点被按下,分别是哪些点
tmp = point_data[2] & 0x1f;
for(i = 0; i < 5; i++){
if(tmp & 0x1){
track_id[point_count++] = i;
}
tmp >>= 1;
}
// 2.3 保存被按下的点的信息
for(i = 0; i< point_count; i++){ //每循环一次保存一个点的坐标
if(track_id[i] != 3){ // 编号为 0, 1, 2, 4 的点
if(track_id[i] < 3){ // 0, 1, 2
point_position = 4+ 5 * track_id[i];
}else{ // 4
point_position = 30;
}
ts_x = point_data[point_position]<< 8 | point_data[point_position+1];
ts_y = point_data[point_position+2]<< 8 | point_data[point_position+3];
ts_p = point_data[point_position+4];
}else{ // 编号为 3的点
point_position = 19;
ts_x = point_data[point_position]<< 8 | point_data[point_position+7];
ts_y = point_data[point_position+8]<< 8 | point_data[point_position+9];
ts_p = point_data[point_position+10];
}
// 3,上报数据 ----一个点坐标
gt811_touch_report(track_id[i],ts_x,ts_y,ts_p);
}
input_sync(gt811_dev->i_dev);
}
//中断处理函数
irqreturn_t gt811_svc_handler(int irqno, void * dev)
{
//启动中断下半部执行
schedule_work(>811_dev->ts_work);
return IRQ_HANDLED;
}
//初始化触控IC中的106个寄存器
static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len)
{
struct i2c_msg msg;
int ret=-1; //发送设备地址
msg.flags= 0;
//写消息
msg.addr=client->addr;
msg.len=len;
msg.buf=data;
ret=i2c_transfer(client->adapter,&msg, 1);
return ret;
}
int gt811_ts_init(void)
{
short 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("----------^_^ %s---------------\n",__FUNCTION__);
config_info[62] = TOUCH_MAX_WIDTH >> 8;
config_info[61] = TOUCH_MAX_WIDTH & 0xff;
config_info[64] = TOUCH_MAX_HEIGHT >> 8;
config_info[63] = TOUCH_MAX_HEIGHT & 0xff;
printk("config_info[62] = 0x%x\n",config_info[62]);
printk("config_info[61] = 0x%x\n",config_info[61]);
printk("config_info[64] = 0x%x\n",config_info[64]);
printk("config_info[63] = 0x%x\n",config_info[63]);
ret = i2c_write_bytes(gt811_dev->client, config_info, ARRAY_SIZE(config_info));
if(ret < 0) {
dev_info(>811_dev->client->dev, "GT811 Send config failed!\n");
return ret;
}
return 0;
}
int gt811_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
{
int ret;
char value;
printk("----------^_^ %s---------------\n",__FUNCTION__);
// 1,实例化全局设备对象
gt811_dev = kzalloc(sizeof(struct s5pv210_gt811), GFP_KERNEL);
// 2, 记录client对象
gt811_dev->client = client;
// 3, 分配input_dev对象空间
gt811_dev->i_dev = input_allocate_device();
// 4,初始化input_dev对象
//添加额外信息
gt811_dev->i_dev->name = "gt811_ts";
gt811_dev->i_dev->phys = "ts_gt811";
gt811_dev->i_dev->uniq = "/ts/gt811";
gt811_dev->i_dev->id.bustype = 0x4;
gt811_dev->i_dev->id.product = 0x5;
gt811_dev->i_dev->id.vendor = 0x123;
gt811_dev->i_dev->id.version = 0x1;
__set_bit(EV_ABS, gt811_dev->i_dev->evbit);
__set_bit(ABS_X, gt811_dev->i_dev->absbit);
__set_bit(ABS_Y, gt811_dev->i_dev->absbit);
__set_bit(ABS_PRESSURE, gt811_dev->i_dev->absbit);
//参数1 ------input_dev对象
//参数2 ------ 坐标,如,ABS_X,ABS_Y
//参数3 ------ 最小值
//参数4 ------ 最大值
//参数5 ------ 误差范围,一般为0
//参数6 ------ 中心位置,一般为0
input_set_abs_params(gt811_dev->i_dev, ABS_X, 0, 800, 0, 0);
input_set_abs_params(gt811_dev->i_dev, ABS_Y, 0, 480, 0, 0);
input_set_abs_params(gt811_dev->i_dev, ABS_PRESSURE, 0, 255, 0, 0);
// 5,注册input_dev对象
ret = input_register_device(gt811_dev->i_dev);
// 6,硬件初始化 --- 初始化触控IC+中断申请
// 6.1 初始化触控IC
// 1》 设置INT为输入态,RESET为内部拉高
gpio_request(S5PV210_GPH1(6), "TS_INT");
gpio_direction_input(S5PV210_GPH1(6));
gpio_free(S5PV210_GPH1(6));
gpio_request(S5PV210_GPD0(3), "TS_RESET");
s3c_gpio_setpull(S5PV210_GPD0(3), S3C_GPIO_PULL_UP);//内部拉高
gpio_free(S5PV210_GPD0(3));
// 2》 RESET输出低,延时1ms,转为输入态
msleep(5);
gpio_request(S5PV210_GPD0(3), "TS_RESET");
gpio_direction_output(S5PV210_GPD0(3), 0);
msleep(1);
gpio_direction_input(S5PV210_GPD0(3));
gpio_free(S5PV210_GPD0(3));
// 3》 延时至少20ms ,i2c寻址gt811--监测gt811是否存在,且没有损坏
msleep(80);
//i2c寻址---监测gt811
value = 1;
ret = i2c_master_send(gt811_dev->client, &value, 1);
if(ret > 0){
printk("gt811 device found OK!\n");
}else{
printk("gt811 device no found !\n");
input_unregister_device(gt811_dev->i_dev);
input_free_device(gt811_dev->i_dev);
kfree(gt811_dev);
return -ENODEV;
}
// 4》 如果有响应,则分一次或者多次初始化配置寄存器:[0x6A2]-[0x70B]共 106 个寄存器
gt811_ts_init();
// 6.2 中断申请
gt811_dev->irqno = IRQ_EINT(14);
ret = request_irq(gt811_dev->irqno, gt811_svc_handler, IRQF_TRIGGER_FALLING, "eint14_ts", NULL);
// 初始化工作队列
INIT_WORK(>811_dev->ts_work, gt811_irq_fun);
return 0;
}
int gt811_i2c_drv_remove(struct i2c_client *client)
{
printk("----------^_^ %s---------------\n",__FUNCTION__);
free_irq(gt811_dev->irqno, NULL);
input_unregister_device(gt811_dev->i_dev);
input_free_device(gt811_dev->i_dev);
kfree(gt811_dev);
return 0;
}
struct i2c_device_id gt811_id_table[] = {
{"gt811_ts",0x1234},
{"gt911_ts",0x5678},
};
// 1, 实例化i2c_driver对象
struct i2c_driver gt811_drv = {
.probe = gt811_i2c_drv_probe,
.remove = gt811_i2c_drv_remove,
.driver = {
.name = "gt811_s5pv210",
},
.id_table = gt811_id_table,
};
static int __init gt811_i2c_drv_init(void)
{
printk("----------^_^ %s---------------\n",__FUNCTION__);
// 2,注册i2c_driver对象
return i2c_add_driver(>811_drv);
}
static void __exit gt811_i2c_drv_exit(void)
{
printk("----------^_^ %s---------------\n",__FUNCTION__);
i2c_del_driver(>811_drv);
}
module_init(gt811_i2c_drv_init);
module_exit(gt811_i2c_drv_exit);
MODULE_LICENSE("GPL");