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 ,外部接口如下:

i.MX53 电容触摸屏驱动 - FT5x06_第1张图片

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;

你可能感兴趣的:(i.MX53 电容触摸屏驱动 - FT5x06)