i.MX53 电容触摸屏驱动 - FT5x06


i.MX53 电容触摸屏驱动 - FT5x06


处理器

-- Freescale i.MX536

硬件平台

-- TX-IMX536

内核版本

-- Kernel-2.6.35.3

系统版本

-- Android2.3.4

触摸屏IC

-- FocalTech FT5306



<一> 电容屏工作原理

电容触摸屏CTP(Capacity Touch Panel)是利用人体的电流感应进行工作的。当用户触摸电容屏时,由于人体电场,用户手指和工作面形成一个耦合电容,因为工作面上接有高频信号,于是手指吸收走一个很小的电流,这个电流分别从屏的四个角上的电极中流出,且理论上流经四个电极的电流与手指头到四角的距离成比例,控制器通过对四个电流比例的精密计算,得出位置。
<二> 接口

查看 FT5306 datasheet ,外部接口如下:



I2C/SPI

        -- 与 HOST 进行通信,这里使用的是 I2C

INT

        -- 中断

WAKE 

        -- 唤醒

/RST 

        -- 复位


原理图接口如下:





I2C3_SCL 

        -- GPIO_3

I2C3_SDA 

        -- GPIO_6

WAKE 

        -- GPIO_2

INT 

        -- GPIO_16

/RST 

        -- GND


<三> 驱动分析

一、PLATFORM DEVICE

arch/arm/mach-mx5/mx53_loco.c

配置引脚功能
static iomux_v3_cfg_t mx53_loco_pads[] = {
   ......

/* I2C3 */

MX53_PAD_GPIO_3__I2C3_SCL,

MX53_PAD_GPIO_6__I2C3_SDA,


// GPIO_2

MX53_PAD_GPIO_2__GPIO1_2,


// GPIO_16

MX53_PAD_GPIO_16__GPIO7_11,

......

};

定义引脚
#define CAP_WAKE (0*32 + 2) // GPIO1_2
#define CAP_IRQ (6*32 + 11) // GPIO7_11

配置 I2C 平台信息
#ifdef CONFIG_TOUCHSCREEN_FT5x06
/* 初始化 FT5x06 */
static void ft5x06_ts_init_hw(void)
{

/* CAP_WAKE - GPIO_2 */

gpio_request(CAP_WAKE, "cap_ts_wake");

gpio_direction_output(CAP_WAKE, 0);

mdelay(10);

// 将 WAKE 引脚置高

gpio_set_value(CAP_WAKE, 1);

}

struct cpts_platform_data ft5x06_ts_info = {

.init_ts_hw = ft5x06_ts_init_hw,

};
#endif

static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
#ifdef CONFIG_TOUCHSCREEN_FT5x06

{

// tf5x06_ts 要与驱动中匹配, 0x70>>1 为 FT5x06 I2C slave 地址

I2C_BOARD_INFO("ft5x06_ts", 0x70>>1),

.platform_data = &ft5x06_ts_info,

// 中断引脚

.irq = gpio_to_irq(CAP_IRQ),

},

#endif
};

在平台初始化函数 mxc_board_init 中,将 i2c2 注册进系统
static void __init mxc_board_init(void)
{

......

i2c_register_board_info(2, mxc_i2c2_board_info,

ARRAY_SIZE(mxc_i2c2_board_info));

......

}

对于 FT5x06 I2C slave 地址,不明白是怎么确定的, datasheet 中说明如下:


那么 A[3:0] 到底是多少呢?


二、PLATFORM DRIVER

drivers/input/touchscreen/ft5x06_ts.c

定义 ft5x06_i2c_driver 结构体
static const struct i2c_device_id ft5x06_ts_id[] = {

{ "ft5x06_ts", 0 },

{ }

};
MODULE_DEVICE_TABLE(i2c, ft5x06_ts_id);

static struct i2c_driver ft5x06_i2c_driver = {

.driver = {

.name = "ft5x06_ts",

.owner = THIS_MODULE,

},


.probe = ft5x06_ts_probe,

.remove = __devexit_p(ft5x06_ts_remove),

.id_table = ft5x06_ts_id,

};

在入口函数中注册 ft5x06_i2c_driver, 出口函数中注销 ft5x06_i2c_driver
static int __init ft5x06_ts_init(void)
{

// 注册 i2c driver - ft5x06_i2c_driver

return i2c_add_driver(&ft5x06_i2c_driver);

}

static void __exit ft5x06_ts_exit(void)
{

// 注销 i2c driver - ft5x06_i2c_driver

i2c_del_driver(&ft5x06_i2c_driver);

}

module_init(ft5x06_ts_init);
module_exit(ft5x06_ts_exit);

probe 函数
static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

struct cpts_platform_data *pdata = client->dev.platform_data;

struct ft5x06_ts_data *ft5x06_ts;

struct input_dev *input_dev;

int err = 0;

unsigned char uc_reg_value;

#if CFG_SUPPORT_TOUCH_KEY

int i;

#endif


printk(" FT5x06 ts probe .\n ");


/* check i2c functionality */

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))

{

err = -ENODEV;

goto exit_check_functionality_failed;

}


/* 为 ft5x06_ts 分配内存 */

ft5x06_ts = kzalloc(sizeof(*ft5x06_ts), GFP_KERNEL);

if (!ft5x06_ts)

{

err = -ENOMEM;

goto exit_alloc_data_failed;

}


// 获取中断引脚

ft5x06_ts->irq = client->irq;


/* set i2c client data */

i2c_set_clientdata(client, ft5x06_ts);

ft5x06_ts->client = client;


// 初始化工作队列, 并绑定处理函数 ft5x06_ts_pen_irq_work

INIT_WORK(&ft5x06_ts->pen_event_work, ft5x06_ts_pen_irq_work);


/* 创建单线程工作队列 */

ft5x06_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));

if (!ft5x06_ts->ts_workqueue) 

{

err = -ESRCH;

goto exit_create_singlethread;

}


/* 调用平台设备中定义的 ft5x06_ts_init_hw 函数 */

if (pdata->init_ts_hw)

pdata->init_ts_hw();


/* 请求中断,并绑定处理函数 ft5x06_ts_interrupt */

err = request_irq(client->irq, ft5x06_ts_interrupt,

IRQF_TRIGGER_FALLING, client->dev.driver->name, ft5x06_ts);

if (err < 0)

{

dev_err(&client->dev, "ft5x06_probe: request irq failed\n");

goto exit_irq_request_failed;

}


// 禁止中断并返回

disable_irq_nosync(client->irq);


/* 为输入设备结构体分配空间, 并对其主要成员进行初始化 */

input_dev = input_allocate_device();

if (!input_dev) {

err = -ENOMEM;

dev_err(&client->dev, "failed to allocate input device\n");

goto exit_input_dev_alloc_failed;

}


// 获取 input_dev

ft5x06_ts->input_dev = input_dev;


#ifdef CONFIG_ANDROID

/* android 多点触摸 */

set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);

set_bit(ABS_MT_POSITION_X, input_dev->absbit);

set_bit(ABS_MT_POSITION_Y, input_dev->absbit);

set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);


input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);

input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);

input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);

input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);

input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, CFG_MAX_TOUCH_POINTS, 0, 0);


set_bit(EV_KEY, input_dev->evbit);

set_bit(EV_ABS, input_dev->evbit);


#if CFG_SUPPORT_TOUCH_KEY

/* touch 按键 */

set_bit(EV_SYN, input_dev->evbit);

set_bit(BTN_TOUCH, input_dev->keybit);

input_dev->keycode = tsp_keycodes;

for(i = 0; i < CFG_NUMOFKEYS; i++)

{

input_set_capability(input_dev, EV_KEY, ((int*)input_dev->keycode)[i]);

tsp_keystatus[i] = KEY_RELEASE;

}

#endif


#else

set_bit(EV_KEY, input_dev->evbit);

set_bit(EV_ABS, input_dev->evbit);

set_bit(BTN_TOUCH, input_dev->keybit);

set_bit(ABS_X, input_dev->absbit);

set_bit(ABS_Y, input_dev->absbit);

set_bit(ABS_PRESSURE, input_dev->absbit);

input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);

input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);

input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0);

#endif


// 设置输入设备的名字

input_dev->name = client->name;


/* 注册输入设备 */

err = input_register_device(input_dev);

if (err)

{

dev_err(&client->dev,"ft5x06_ts_probe: failed to register input device: %s\n",

dev_name(&client->dev));

goto exit_input_register_device_failed;

}


#ifdef CONFIG_HAS_EARLYSUSPEND

/* 休眠唤醒 */

ft5x06_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;

ft5x06_ts->early_suspend.suspend = ft5x06_ts_suspend;

ft5x06_ts->early_suspend.resume = ft5x06_ts_resume;

register_early_suspend(&ft5x06_ts->early_suspend);

#endif


// make sure CTP already finish startup process

msleep(150);


/* get some register information */

uc_reg_value = ft5x06_read_fw_ver(client);

ft5x06_read_reg(client, FT5x06_REG_PERIODACTIVE, &uc_reg_value);

ft5x06_read_reg(client, FT5x06_REG_THGROUP, &uc_reg_value);


// 使能中断

enable_irq(client->irq);


printk(" FT5x06 ts probed .\n ");

return 0;


/* 出错处理 */

exit_input_register_device_failed:

input_free_device(input_dev);

exit_input_dev_alloc_failed:

free_irq(client->irq, ft5x06_ts);

exit_irq_request_failed:

cancel_work_sync(&ft5x06_ts->pen_event_work);

destroy_workqueue(ft5x06_ts->ts_workqueue);

exit_create_singlethread:

printk("==singlethread error =\n");

i2c_set_clientdata(client, NULL);

kfree(ft5x06_ts);

exit_alloc_data_failed:
exit_check_functionality_failed:

return err;

}

中断处理函数 ft5x06_ts_interrupt
static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
{

struct ft5x06_ts_data *ft5x06_ts = dev_id;


// 禁止中断,立即返回

disable_irq_nosync(ft5x06_ts->irq);


/* 调度工作队列 */

if (!work_pending(&ft5x06_ts->pen_event_work))

{

/* 与 ft5x06_ts->pen_event_work 关联的 ft5x06_ts_pen_irq_work 函数开始执行 */

queue_work(ft5x06_ts->ts_workqueue, &ft5x06_ts->pen_event_work);

}


// 中断已处理, 返回 IRQ_HANDLED

return IRQ_HANDLED;

}

ft5x06_ts_pen_irq_work 函数
static void ft5x06_ts_pen_irq_work(struct work_struct *work)
{

int ret = -1;

struct ft5x06_ts_data *ft5x06_ts = container_of(work, struct ft5x06_ts_data, pen_event_work);


// 读取数据

ret = ft5x06_read_data(ft5x06_ts->client);

if (ret == 0)

{

// 读取成功,上报

ft5x06_report_value(ft5x06_ts->client);

}


// 使能中断

enable_irq(ft5x06_ts->irq);

}

读取数据函数 ft5x06_read_data
static int ft5x06_read_data(struct i2c_client *client)
{

struct ft5x06_ts_data *data = i2c_get_clientdata(client);

struct ts_event *event = &data->event;

u8 buf[CFG_POINT_READ_BUF] = {0};

int ret = -1;

int i;


/* 读 I2C 数据 */

ret = ft5x06_i2c_rxdata(client, buf, CFG_POINT_READ_BUF);

if (ret < 0) {

printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);

return ret;

}


/* 获取触点个数 */

memset(event, 0, sizeof(struct ts_event));

event->touch_point = buf[2] & 0x07;


if (event->touch_point > CFG_MAX_TOUCH_POINTS)

{

event->touch_point = CFG_MAX_TOUCH_POINTS;

}


/* 处理触摸数据 */

#ifdef CONFIG_ANDROID

/* android 多点触摸处理 */

for (i = 0; i < event->touch_point; i++)

{

event->au16_x[i] = (s16)(buf[3 + 6*i] & 0x0F)<<8 | (s16)buf[4 + 6*i];

event->au16_y[i] = (s16)(buf[5 + 6*i] & 0x0F)<<8 | (s16)buf[6 + 6*i];

event->au8_touch_event[i] = buf[0x3 + 6*i] >> 6;

event->au8_finger_id[i] = (buf[5 + 6*i])>>4;

//printk("%d, %d\n", event->au16_x[i], event->au16_y[i]);

}

#else

if(event->touch_point == 1) {

event->au16_x[0] = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];

event->au16_y[0] = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];

}

#endif


event->pressure = 200;


return 0;

}

上报函数 ft5x06_report_value
static void ft5x06_report_value(struct i2c_client *client)
{

struct ft5x06_ts_data *data = i2c_get_clientdata(client);

struct ts_event *event = &data->event;

int i;


#ifdef CONFIG_ANDROID
   /* android 多点触摸处理 */

for (i = 0; i < event->touch_point; i++)

{

//printk("touch point[%d,%d]", event->au16_x[i], event->au16_y[i]);


/* LCD 显示区域 */

if (event->au16_x[i] < SCREEN_MAX_X && event->au16_y[i] < SCREEN_MAX_Y)

{

event->au16_x[i] = SCREEN_MAX_X - event->au16_x[i];

event->au16_y[i] = SCREEN_MAX_Y - event->au16_y[i];


/* 上报触摸信息 */

input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->au16_x[i]);

input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]);

input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);

input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->au8_finger_id[i]);


if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2)

{

input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);

}

else

{

input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0);

}

}

/* 触摸按键区域 */

else

{

#if CFG_SUPPORT_TOUCH_KEY

if (event->au16_x[i] >= SCREEN_MAX_X)

{

//printk("key x = %d\n", event->au16_y[i]);

// 处理触摸按键

ft5x06_touch_key_process(data->input_dev, event->au16_x[i],

event->au16_y[i], event->au8_touch_event[i]);

}

#endif

}


// 单点同步

input_mt_sync(data->input_dev);

}
#else

if(event->touch_point == 1) 

{

input_report_abs(data->input_dev, ABS_X, event->au16_x[0]);

input_report_abs(data->input_dev, ABS_Y, event->au16_y[0]);

input_report_abs(data->input_dev, ABS_PRESSURE, 1);

}

#endif

// 多点同步

input_sync(data->input_dev);


if (event->touch_point == 0) 

{

ft5x06_ts_release(client);

return ;

}

} /*end ft5x06_report_value*/

触摸按键处理函数 ft5x06_touch_key_process
#if CFG_SUPPORT_TOUCH_KEY
int ft5x06_touch_key_process(struct input_dev *dev, int x, int y, int touch_event)
{

int i;

int key_id;


/* MENU */

if ( y < (SCREEN_MAX_Y) && y > (SCREEN_MAX_Y - 20))

{

key_id = 0;

}

/* HOME */

else if ( y < (SCREEN_MAX_Y - 50) && y > (SCREEN_MAX_Y - 70))

{

key_id = 1;

}

/* BACK */

else if ( y < (SCREEN_MAX_Y - 90) && y > (SCREEN_MAX_Y - 110))

{

key_id = 2;

}

else

{

key_id = 0xf;

}


for(i = 0; i <CFG_NUMOFKEYS; i++ )

{

if(tsp_keystatus[i])

{

/* 触摸按键释放,上报键值 */

input_report_key(dev, tsp_keycodes[i], 0);

//printk("[FTS] %s key is release. Keycode : %d\n", tsp_keyname[i], tsp_keycodes[i]);

tsp_keystatus[i] = KEY_RELEASE;

}

else if( key_id == i )

{

if( touch_event == 0)

{

/* 触摸按键按下,上报键值 */

input_report_key(dev, tsp_keycodes[i], 1);

//printk( "[FTS] %s key is pressed. Keycode : %d\n", tsp_keyname[i], tsp_keycodes[i]);

tsp_keystatus[i] = KEY_PRESS;

}

}

}


return 0;

}
#endif

寄存器写函数 ft5x06_write_reg
static int ft5x06_write_reg(struct i2c_client *client, u8 addr, u8 para)
{

u8 buf[3];

int ret = -1;


// I2C 地址

buf[0] = addr;


// 写入的数据

buf[1] = para;


/* I2C 发送数据 */

ret = ft5x06_i2c_txdata(client, buf, 2);

if (ret < 0) 

{

pr_err("write reg failed! %#x ret: %d", buf[0], ret);

return -1;

}


return 0;

}

寄存器读函数 ft5x06_read_reg
static int ft5x06_read_reg(struct i2c_client *client, u8 addr, u8 *pdata)
{

int ret;

u8 buf[2];

struct i2c_msg msgs[2];


// 寄存器地址

buf[0] = addr;


/* 先写 I2C 地址,写模式 */

msgs[0].addr = client->addr;

msgs[0].flags = 0;

msgs[0].len = 1;

msgs[0].buf = buf;


/* 再写I2C地址,读模式 */

msgs[1].addr = client->addr;

msgs[1].flags = I2C_M_RD;

msgs[1].len = 1;

msgs[1].buf = buf;


/* 进行 I2C 数据传传输 */

ret = i2c_transfer(client->adapter, msgs, 2);

if (ret < 0)

pr_err("msg %s i2c read error: %d\n", __func__, ret);


// 将读取的数据存入 pdata

*pdata = buf[0];


return ret;

}

I2C 读取数据函数 ft5x06_i2c_rxdata
static int ft5x06_i2c_rxdata(struct i2c_client *client, char *rxdata, int length)
{

int ret;


struct i2c_msg msgs[] = {

/* 先写 I2C 设备地址,写模式 */

{

.addr = client->addr,

.flags = 0,

.len = 1,

.buf = rxdata,

},

/* 再写I2C设备地址,读模式 */

{

.addr = client->addr,

.flags = I2C_M_RD,

.len = length,

.buf = rxdata,

},

};


/* 进行数据传输 */

ret = i2c_transfer(client->adapter, msgs, 2);

if (ret < 0)

pr_err("msg %s i2c read error: %d\n", __func__, ret);


return ret;

}

I2C 发送数据函数 ft5x06_i2c_txdata
static int ft5x06_i2c_txdata(struct i2c_client *client, char *txdata, int length)
{

int ret;


struct i2c_msg msg[] = {

/* 直接将数据写入 I2C 设备地址 */

{
.addr = client->addr,
.flags = 0,
.len = length,
.buf = txdata,

},

};


/* 进行数据传输 */

ret = i2c_transfer(client->adapter, msg, 1);

if (ret < 0)

pr_err("%s i2c write error: %d\n", __func__, ret);


return ret;

}

remove 函数
static int __devexit ft5x06_ts_remove(struct i2c_client *client)
{

struct ft5x06_ts_data *ft5x06_ts;


ft5x06_ts = i2c_get_clientdata(client);

unregister_early_suspend(&ft5x06_ts->early_suspend);

free_irq(client->irq, ft5x06_ts);

input_unregister_device(ft5x06_ts->input_dev);

kfree(ft5x06_ts);

cancel_work_sync(&ft5x06_ts->pen_event_work);

destroy_workqueue(ft5x06_ts->ts_workqueue);

i2c_set_clientdata(client, NULL);


return 0;

}

总结:

当触摸产生时,坐标值等相关信息会存储到触摸 IC 的相应寄存器中,驱动要做的就是通过 I2C 从触摸 IC 的相应寄存器读出坐标值等信息,然后上报,其他的事情就交给 android 处理了。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值