MTK平台 CTP流程解析
mtk 平台tp相关,以联永的nt11004为例
一 硬件
tp硬件上主要有6根线:vdd,int,sda,scl,reset,gnd
vdd:电压值为2.8v
int:输入脚,低电平有效。int的触发电平需为电平触发
sda,scl为I2C脚
reset:低电平有效,tp正常工作模式此脚需为高电平。
gnd:地
tp里如有fireware,则无需初始化,只要vdd,reset,gnd接上后,触摸tp,int就有正常中断产生,如没请联系tp fae,此为判断tp是否有硬件问题。
二 软件
代码路径:
V:\MT6577\alps\mediatek\custom\common\kernel\touchpanel\nt11004\nt11004_driver.c
V:\mt6577\alps\mediatek\custom\basicom77_cu_ics2\kernel\touchpanel\nt11004\tpd_custom_nt11004.h
1 宏定义:
ProjectConfig.mk CUSTOM_KERNEL_TOUCHPANEL=nt11004
2 在dws中配置tp reset 和 int脚
3 跟相关tp原厂要份驱动代码,如果没有,就copy一份之前的,修改。
tp设备是通过module_init(tpd_driver_init)挂载到系统上的,tpd_driver_init中主要实现注册i2c设备和tp设备,具体挂载在哪一路i2c上需看原理图。
4 probe中申请int,创建tp时间处理线程,当有触摸时,tp产生中断,bb通过i2c总线读取相应的坐标,将坐标点传递给上层驱动。大致的流程图如下:
三 tp虚拟按键
目前智能机都采用全触控的方式实现手机的正常使用,故往往会用到tp做虚拟按键,下面也简单介绍下mtk tp虚拟按键的实现方式:
整个android的Virtual key的整个的简单框图如下:
APP------->
php?mod=tag&id=6090" target="_blank" class="relatedlink">Framework------->
Kernel------->
Hardware
struct tpd_driver_t
{
char *tpd_device_name;
int (*tpd_local_init)(void);
void (*suspend)(struct early_suspend *h);
void (*resume)(struct early_suspend *h);
int tpd_have_button;
};
其中变量int tpd_have_button就是判断是否存在tp按键,如项目有使用tp虚拟按键,这在注册时须将此处的值设置为1,如:
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = NT11004_TS_NAME,
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
#ifdef TPD_HAVE_BUTTON
.tpd_have_button = 1,
#else
.tpd_have_button = 0,
#endif
}
tp 按键相关的驱动代码:
X:\MT6577\alps\mediatek\custom\common\kernel\touchpanel\src\tpd_button.c
//#ifdef TPD_HAVE_BUTTON
//static int tpd_keys[TPD_KEY_COUNT] = TPD_KEYS;
//static int tpd_keys_dim[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
static unsigned int tpd_keycnt = 0;
static int tpd_keys[TPD_VIRTUAL_KEY_MAX]={0};
static int tpd_keys_dim[TPD_VIRTUAL_KEY_MAX][4];// = {0};
static ssize_t mtk_virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) {
int i, j;
for(i=0, j=0;i<tpd_keycnt;i++)
j+=sprintf(buf, "%s%s:%d:%d:%d:%d:%d%s",buf,
__stringify(EV_KEY),tpd_keys,
tpd_keys_dim[0],tpd_keys_dim[1],
tpd_keys_dim[2],tpd_keys_dim[3],
(i==tpd_keycnt-1?"\n":":"));
return j;
static struct kobj_attribute mtk_virtual_keys_attr = {
.attr = {
.name = "virtualkeys.mtk-tpd",
.mode = S_IRUGO,
},
.show = &mtk_virtual_keys_show,
};
static struct attribute *mtk_properties_attrs[] = {
&mtk_virtual_keys_attr.attr,
NULL
};
static struct attribute_group mtk_properties_attr_group = {
.attrs = mtk_properties_attrs,
};
void tpd_button_init(void) {
int ret = 0, i = 0;
// if((tpd->kpd=input_allocate_device())==NULL) return -ENOMEM;
tpd->kpd=input_allocate_device();
/* struct input_dev kpd initialization and registration */
tpd->kpd->name = TPD_DEVICE "-kpd";
set_bit(EV_KEY, tpd->kpd->evbit);
//set_bit(EV_REL, tpd->kpd->evbit);
//set_bit(EV_ABS, tpd->kpd->evbit);
for(i=0;i<tpd_keycnt;i++)
__set_bit(tpd_keys, tpd->kpd->keybit);
tpd->kpd->id.bustype = BUS_HOST;
tpd->kpd->id.vendor = 0x0001;
tpd->kpd->id.product = 0x0001;
tpd->kpd->id.version = 0x0100;
if(input_register_device(tpd->kpd))
TPD_DMESG("input_register_device failed.(kpd)\n");
set_bit(EV_KEY, tpd->dev->evbit);
for(i=0;i<tpd_keycnt;i++)
__set_bit(tpd_keys, tpd->dev->keybit);
properties_kobj = kobject_create_and_add("board_properties", NULL);
if(properties_kobj)
ret = sysfs_create_group(properties_kobj,&mtk_properties_attr_group);
if(!properties_kobj || ret)
printk("failed to create board_properties\n");
}
在sys/board_properties/下创建一个叫virtualkeys.*节点;在frameworks/base/services/input下的文件EventHub.cpp中对这个节点进行访问;
如果采用以上方式注册tp虚拟按键,则需要在X:\MT6577\alps\mediatek\config\basicom_6462k有对应的mtk-kpd.kl中有对应虚拟按键的键值映射表。用adb shell 可以再/system/usr/keyLayout目录下看到此文件。
EventHub.cpp中会检测是否有tp事件发生,如果tp坐标点值落在主次的虚拟按键virtualkeys.*中的话,则转成上次按键消息传递给上层。
四 tp接近的功能
此功能是在中断处理函数中通过调用ret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data),更新psensor的数据,senosr处理模块polling时检测到有数据更新后将消息传递上hal层。代码请参考X:\MT6577\alps\kernel\mediatek\source\kernel\drivers\hwmon\hwmsen\hwmsen_dev.c
故此功能需保证驱动和hal都需要先注册上psensor相关的模块。
当一个模块需调用其他模块的函数或者变量时:可使用EXPORT_SYMBOL和EXPORT_SYMBOL_GPL来定义:
/proc/kallsyms”文件对应着内核符号表,记录了符号以及符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号表:
1 EXPORT_SYMBOL(符号名);
2 EXPORT_SYMBOL_GPL(符号名)
导出的符号可以被其他模块使用,不过使用之前一定要声明一下。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。
五 tp apk升级功能的实现方法
static int nvt_flash_init()
{
int ret=0;
NVT_proc_entry = create_proc_entry(DEVICE_NAME, 0666, NULL);
if(NVT_proc_entry == NULL)
{
TPD_DMESG("Couldn't create proc entry!\n");
ret = -ENOMEM;
return ret ;
}
else
{
TPD_DMESG("Create proc entry success!\n");
NVT_proc_entry->proc_fops = &nvt_flash_fops;
}
flash_priv=kzalloc(sizeof(*flash_priv),GFP_KERNEL);
TPD_DMESG("============================================================\n");
TPD_DMESG("NVT_flash driver loaded\n");
TPD_DMESG("============================================================\n");
return 0;
error:
if(ret != 0)
{
TPD_DMESG("flash_priv error!\n");
}
return -1;
}
通过create_proc_entry(DEVICE_NAME, 0666, NULL)创建一个虚拟的文件, 虚拟文件一方面可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。故用户空间可直对该文件节点进行读写操作来控制内核驱动。我们可以通过
adbshell后再proc目录下看到我们创建的节点DEVICE_NAME。
struct file_operations nvt_flash_fops =
{
.owner = THIS_MODULE,
.open = nvt_flash_open,
.release = nvt_flash_close,
.write = nvt_flash_write,
.read = nvt_flash_read,
};
当我们需要知道tp的一些信息,只需通过去读该虚拟文件,调用nvt_flash_read则可以知道,而tp升级是通过对虚拟文件进行写操作,调用nvt_flash_write将二进制文件fw通过i2c总线写到tp ic的ROM中。
六 经常遇到一些问题
调试中遇到一些问题总结:
tp触摸按键没有功能
1 检查在sys/board_properties/是否有注册上相关节点
2 检查kl文件的键值映射表
tp触摸无效:
1 硬件量int是否有中断产生
2 tp address是否正确,i2c时候有ack
博主,我目前也在调试这块IC,有些问题一直搞不明白,小编能留个联系方式吗
我在网上看了一份代码,在tpd_custom_nt11004.h中有三个地址一直不是很清楚,看datesheet也没搞明白,我只知道其中一个是I2C地址,另外两个不太清楚是什么?博主能给解释一下吗?谢谢了
#ifndef TOUCHPANEL_H__
#define TOUCHPANEL_H__
#define NT11004_TS_ADDR (0x34)
#define NT11004_HW_ADDR (0x7F)
#define NT11004_FW_ADDR (0x01)
写得很好,明白了一些东西
这个要看对应的DATASHEET呢。
学习
嗯,是这样的,我和厂商联系过,他们说TP的I2C地址就是0x01,在这个函数中static struct i2c_board_info __initdata i2c_tpd={ I2C_BOARD_INFO(NT11004_TS_NAME, 0X01)}初始化过了,但我不知道在下边这个函数中为什么还会有三个地址的选择,不知道他们分别代表什么,DATESHEET里说的也不是很清楚,博主能给解释一下吗?谢谢了
int nvt_flash_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
struct i2c_msg msgs[2];
char *str;
int ret = -1;
int retries = 0;
unsigned char tmpaddr;
TPD_DMESG("nvt_flash_write\n");
file->private_data = (uint8_t *)kmalloc(64, GFP_KERNEL);
str = file->private_data;
ret = copy_from_user(str, buff, count);
TPD_DMESG("str[0]=%x, str[1]=%x, str[2]=%x, str[3]=%x\n", str[0], str[1], str[2], str[3]);
tmpaddr = i2c_client->addr;
if((str[0] == 0x7F)||(str[0] == (0x7F<<1)))
{
i2c_client->addr = NT11004_HW_ADDR;
}
else if((str[0] == 0x01)||(str[0] == (0x01<<1)))
{
i2c_client->addr = NT11004_FW_ADDR;
}
else
{
i2c_client->addr = NT11004_TS_ADDR;
}
i2c_smbus_write_i2c_block_data(i2c_client, str[2], str[1]-1, &str[3]);
i2c_client->addr = tmpaddr;
return ret;
}
写的很好很详细!支持支持
支持支持!支持免费
学习!
学习了~
感谢,感谢,非常感谢!