处理器
-- 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)
{
#if CFG_SUPPORT_TOUCH_KEYstruct 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;
#endifint i;
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;
#ifdef CONFIG_ANDROID}
// 获取 input_dev
ft5x06_ts->input_dev = input_dev;
#if CFG_SUPPORT_TOUCH_KEY/* 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);
/* 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}
#endifset_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);
// 设置输入设备的名字
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}
#endif/* 休眠唤醒 */
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);
// 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:/* 出错处理 */
exit_input_dev_alloc_failed:input_free_device(input_dev);
exit_irq_request_failed:free_irq(client->irq, ft5x06_ts);
exit_create_singlethread:cancel_work_sync(&ft5x06_ts->pen_event_work);
destroy_workqueue(ft5x06_ts->ts_workqueue);
exit_alloc_data_failed:printk("==singlethread error =\n");
i2c_set_clientdata(client, NULL);
kfree(ft5x06_ts);
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)
{
#ifdef CONFIG_ANDROIDstruct ft5x06_ts_data *data = i2c_get_clientdata(client);
struct ts_event *event = &data->event;
int i;
/* 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);
}
#if CFG_SUPPORT_TOUCH_KEY}
/* 触摸按键区域 */
else
{
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 处理了。