【Android驱动04】触摸屏touchscreen驱动移植以及调试方法
1. 硬件层触摸屏硬件:触摸屏通常包括电容式或电阻式触摸屏,它们通过感应手指的触摸来产生信号。电容式触摸屏利用人体的电流感应工作,而电阻式触摸屏则通过压力改变电阻值来检测触摸。
传感器与接口:触摸屏包含多个触摸传感器,这些传感器通过总线接口(如I2C、SPI等)与主控芯片通信,将触摸数据(如位置、压力等)传输给软件层。
https://i-blog.csdnimg.cn/direct/144c735ec9d5455cb150213bdf2f252b.png2. 软件层
输入子体系:Android内核中的输入子体系是一个关键组件,负责处理来自各种输入装备的数据,包括触摸屏。输入子体系从触摸屏驱动程序吸取触摸数据,并将其转换为标准的输入事故,然后通报给上层应用程序。
触摸屏驱动程序:触摸屏驱动程序是Android内核中实现触摸屏功能的具体实现。它负责与硬件层通信,读取触摸传感器的数据,并通过输入子体系将其上报给体系。
3 . 触摸屏驱动流程
[*] 初始化与配置
装备树(DTS)配置:在装备树文件中配置触摸屏相关的硬件参数,如GPIO、I2C地址等。
驱动程序加载:在体系启动时,根据装备树配置加载触摸屏驱动程序。驱动程序会进行须要的初始化工作,如设置GPIO引脚、配置I2C接口等。
1.1 设置GPIO引脚:
打开 vendor\mediatek\proprietary\scripts\DrvGen.exe
选择 cendor\mediatek\proprietary\bootable\bootloader\lk\target\ $ (project) \dct\dct\codegen.dws 配置文件
1.2 创建ft5x16,将供应商提供的驱动驱动资料拷贝到该目录下;
https://i-blog.csdnimg.cn/direct/af228a2787724e16b6ba7eb0b1f9bc2e.jpeg
1.3 修改配置文件:CUSTOM_KERNEL_TOUCHPANEL其值由改为ft5x16,表明对应ft5x16子目录;
打开ft5x16.c文件,修改一下:
static struct i2c_board_info __initdata ft5x16_i2c_tpd={ I2C_BOARD_INFO("ft5x16", (0x70>>1))}; //"ft5x16"为设备名 ,设备地址为高7位
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = "FT5x16",
.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
};
/* called when loaded into kernel */
static int __init tpd_driver_init(void) {
printk("MediaTek FT5x16 touch panel driver init\n");
/* 注册板级设备信息 */
i2c_register_board_info(IIC_PORT, &ft5x16_i2c_tpd, 1);//IIC_PORT表示i2c控制器号,由电路原理图可知TP设备连接到i2c控制器0,ft5x16_i2c_tpd为i2c设备结构,1表示该i2c_board_info个数
if(tpd_driver_add(&tpd_device_driver) < 0)
printk("add FT5x16 driver failed\n");
return 0;
}
1.4 I2C通信
新驱动编译进内核,启动内核后,我们怎样验证i2c接口能够正常通信呢?
体系启动后通过串口或adb shell进入体系命令行窗口,查询/sys/bus/i2c/devices目录下是否有0-0038信息,查询/sys/bus/i2c/drivers目录下是否存在‘ft5x16’装备名;先包管i2c能够正常通信;
1.5 中断触发
中断注册函数:
mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 1);
//tpd_eint_interrupt_handler函数为中断回调函数
1.6 数据上报
当触摸屏产生中断的时间就会调用到该接口;然后在中断处理函数中唤醒运行在子线程中的等待队列,再通过子线程获取TP数据并上报到体系;
static DECLARE_WAIT_QUEUE_HEAD(waiter);//初始化等待队列
thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);//新建线程
static int touch_event_handler(void *unused)
{
......
do
{
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
set_current_state(TASK_INTERRUPTIBLE);
wait_event_interruptible(waiter,tpd_flag!=0);//等待队列进入休眠,等待唤醒
tpd_flag = 0;
set_current_state(TASK_RUNNING);
......
if (tpd_touchinfo(&cinfo, &pinfo)) //获取TP数据
{
//TPD_DEBUG("point_num = %d\n",point_num);
TPD_DEBUG_SET_TIME;
if(point_num >0)
{
for(i =0; i<point_num; i++)//only support 3 point
{
cinfo.x = cinfo.x;
cinfo.y = cinfo.y;
tpd_down(cinfo.x, cinfo.y, cinfo.id); //上报按下数据
printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x, cinfo.y, cinfo.id);
}
input_sync(tpd->dev);
}
else
{
tpd_up(cinfo.x, cinfo.y); //上报弹起数据
//TPD_DEBUG("release --->\n");
//input_mt_sync(tpd->dev);
input_sync(tpd->dev);
}
}
......
}while(!kthread_should_stop());
return 0;
}
注:如果TP获取到的数据比力乱的时间发起通过打开‘指针位置’功能进行检察,清除TP固件分辨与LCD没对应等标题;
4. TP驱动扼要分析
MTK自己编写了一套TP框架,通过该框架管理TP装备,tpd_driver_add为框架的接口之一;体系通过tpd_driver_add添加驱动后会回调tpd_local_init函数;
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = FT5x16,
.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
};
/* called when loaded into kernel */
static int __init tpd_driver_init(void) {
printk("MediaTek FT5x16 touch panel driver init\n");
i2c_register_board_info(0, &ft5x16_i2c_tpd, 1);//注册板级设备信息
if(tpd_driver_add(&tpd_device_driver) < 0)//添加驱动
printk("add FT5x16 driver failed\n");
return 0;
}
向体系注册i2c驱动后,如果找到对应的装备就会调用tpd_probe函数;
static int tpd_local_init(void)
{
TPD_DMESG("FTS I2C Touchscreen Driver (Built %s @ %s)\n", __DATE__, __TIME__);
if(i2c_add_driver(&tpd_i2c_driver)!=0)//注册i2c驱动
{
TPD_DMESG("FTS unable to add i2c driver.\n");
return -1;
}
TPD_DMESG("end %s, %d\n", __FUNCTION__, __LINE__);
tpd_type_cap = 1;
return 0;
}
static const struct i2c_device_id ft5x16_tpd_id[] = {{TPD_NAME,0},{}};
static struct i2c_driver tpd_i2c_driver = {
.driver = {
.name = TPD_NAME,
},
.probe = tpd_prob,
.remove = __devexit_p(tpd_remove),
.id_table = ft5x16_tpd_id,
.detect = tpd_detect,
};
static int __devinit tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int retval = TPD_OK;
char data;
u8 report_rate=0;
int err=0;
int reset_count = 0;
u8 chip_id,i;
reset_proc:
i2c_client = client;
#ifdef MAIERXUN_TP_COM
if(touchpanel_flag){
return 0;
}
#endif
//复位
//power on, need confirm with SA
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
msleep(5);
TPD_DMESG(" fts ic reset\n");
//打开TP电源
#ifdef TPD_POWER_SOURCE_CUSTOM
hwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_3300, "TP");
#else
hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_3300, "TP");
#endif
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
#ifdef TPD_CLOSE_POWER_IN_SLEEP
hwPowerDown(TPD_POWER_SOURCE,"TP");
hwPowerOn(TPD_POWER_SOURCE,VOL_3300,"TP");
msleep(100);
#else/* 结束复位 */
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
msleep(5);
TPD_DMESG(" fts ic reset\n");
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
#endif
/* 初始化中断引脚 */
mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN, GPIO_PULL_UP);
/* 中断配置和注册 */
mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 1);//注册中断处理函数,TP产生中断时就会回调tpd_eint_interrupt函数
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
msleep(400);
err=i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &data);
TPD_DMESG("gao_i2c:err %d,data:%d\n", err,data);
if(err< 0 || data!=0)// reg0 data running state is 0; other state is not 0
{
TPD_DMESG("I2C transfer error, line: %d\n", __LINE__);
#ifdef TPD_RESET_ISSUE_WORKAROUND
if ( reset_count < TPD_MAX_RESET_COUNT )
{
reset_count++;
goto reset_proc;
}
#endif
//add at 20150330 by zhu
#ifdef MAIERXUN_TP_COM
touchpanel_flag=false;
#endif
return -1;
}
......
#ifdef VELOCITY_CUSTOM_FT5206
if((err = misc_register(&tpd_misc_device)))//注册混杂设备驱动
{
printk("mtk_tpd: tpd_misc_device register failed\n");
}
#endif
#ifdef TPD_AUTO_UPGRADE
printk("********************Enter CTP Auto Upgrade********************\n");
fts_ctpm_auto_upgrade(i2c_client);
#endif
thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);//创建子线程,通过该子线程获取和上报数据
if (IS_ERR(thread))
{
retval = PTR_ERR(thread);
TPD_DMESG(TPD_DEVICE " failed to create kernel thread: %d\n", retval);
}
TPD_DMESG("FTS Touch Panel Device Probe %s\n", (retval < TPD_OK) ? "FAIL" : "PASS");
/* 初始化TP的P-sensor功能,暂不分析 */
#ifdef TPD_PROXIMITY
struct hwmsen_object obj_ps;
obj_ps.polling = 0;//interrupt mode
obj_ps.sensor_operate = tpd_ps_operate;
if((err = hwmsen_attach(ID_PROXIMITY, &obj_ps)))
{
APS_ERR("proxi_fts attach fail = %d\n", err);
}
else
{
APS_ERR("proxi_fts attach ok = %d\n", err);
}
#endif
#ifdef MAIERXUN_TP_COM
touchpanel_flag=true;
#endif
return 0;
}
中断处理函数
中断处理遵照中断上下文的筹划原则,使得中断子程序只是简朴唤醒等待队列就可以了,没有多余的操作;
static void tpd_eint_interrupt_handler(void)
{
//TPD_DEBUG("TPD interrupt has been triggered\n");
TPD_DEBUG_PRINT_INT;
tpd_flag = 1;
wake_up_interruptible(&waiter);//唤醒等待队列
}
子线程处理函数
中断与轮询:触摸屏驱动程序可以通过中断或轮询的方式从硬件层读取触摸数据。当触摸屏被触摸时,硬件会触发一个中断,驱动程序响应中断并读取数据;或者驱动程序定期轮询硬件以获取数据。
数据处理:驱动程序将读取到的原始触摸数据进行处理,转换为标准的输入事故格式(如ABS_X、ABS_Y等),并准备将其上报给输入子体系。
static int touch_event_handler(void *unused)
{
struct touch_info cinfo, pinfo;
int i=0;
struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };
sched_setscheduler(current, SCHED_RR, ¶m);
#ifdef TPD_PROXIMITY
int err;
hwm_sensor_data sensor_data;
u8 proximity_status;
#endif
u8 state;
do//进入while循环进行睡眠-等待唤醒的操作
{
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);//中断使能(解除屏蔽)
set_current_state(TASK_INTERRUPTIBLE);
wait_event_interruptible(waiter,tpd_flag!=0);//进入睡眠等待唤醒
tpd_flag = 0;
set_current_state(TASK_RUNNING);
......
#ifdef TPD_PROXIMITY//TP的P-sensor功能,暂不分析
if (tpd_proximity_flag == 1)
{
i2c_smbus_read_i2c_block_data(i2c_client, 0xB0, 1, &state);
TPD_PROXIMITY_DEBUG("proxi_5206 0xB0 state value is 1131 0x%02X\n", state);
if(!(state&0x01))
{
tpd_enable_ps(1);
}
i2c_smbus_read_i2c_block_data(i2c_client, 0x01, 1, &proximity_status);
TPD_PROXIMITY_DEBUG("proxi_5206 0x01 value is 1139 0x%02X\n", proximity_status);
if (proximity_status == 0xC0)
{
tpd_proximity_detect = 0;
}
else if(proximity_status == 0xE0)
{
tpd_proximity_detect = 1;
}
TPD_PROXIMITY_DEBUG("tpd_proximity_detect 1149 = %d\n", tpd_proximity_detect);
if ((err = tpd_read_ps()))
{
TPD_PROXIMITY_DMESG("proxi_5206 read ps data 1156: %d\n", err);
}
sensor_data.values = tpd_get_ps_value();
sensor_data.value_divide = 1;
sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
if ((err = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data)))
{
TPD_PROXIMITY_DMESG(" proxi_5206 call hwmsen_get_interrupt_data failed= %d\n", err);
}
}
#endif
if (tpd_touchinfo(&cinfo, &pinfo)) //获取TP设备数据,并把数据保存在cinfob buf中
{
//TPD_DEBUG("point_num = %d\n",point_num);
TPD_DEBUG_SET_TIME;
if(point_num >0)
{
for(i =0; i<point_num; i++)//only support 3 point
{
printk(KERN_DEBUG"X:%4d, Y:%4d, P:%4d \n", cinfo.x, cinfo.y, cinfo.id);
cinfo.x = cinfo.x;
cinfo.y = cinfo.y;
tpd_down(cinfo.x, cinfo.y, cinfo.id);//按下数据处理
printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x, cinfo.y, cinfo.id);
}
input_sync(tpd->dev);
}
else
{
tpd_up(cinfo.x, cinfo.y); //弹起数据处理
//TPD_DEBUG("release --->\n");
//input_mt_sync(tpd->dev);
input_sync(tpd->dev);
}
}
......
}while(!kthread_should_stop());
return 0;
}
事故上报与响应
事故上报:触摸屏驱动程序通过输入子体系将处理后的触摸事故上报给体系。这些事故包括触摸点的位置、压力等信息。
事故响应:上层应用程序通过监听输入事故来响应用户的触摸操作。例如,当用户用手指在屏幕上滑动时,应用程序会吸取到相应的滑动事故,并据此执行相应的操作(如页面滚动)。
5. 触摸屏调试本领
1,利用体系自带的调试工具:
开辟者选项:Android装备通常有一个“开辟者选项”,此中包含了许多用于调试和性能优化的工具。通过开启“显示触摸操作”等选项,可以在屏幕上看到触摸的轨迹,帮助直观相识触摸操作的效果。
ADB(Android Debug Bridge):ADB是一个多功能的命令行工具,答应用户与连接的Android装备进行通信。通过ADB,可以执行各种调试命令,如检察日志、安装和调试应用等。
编写和运行测试程序:
开辟者可以编写专门的测试程序来检测触摸屏的响应情况,如绘制触摸轨迹、检测触摸点的坐标和压力等。这些测试程序可以帮助辨认触摸屏的硬件或软件标题。
检察和修改触摸屏配置文件:
在某些情况下,触摸屏的性能标题可能与配置文件有关。开辟者可以检察和修改这些配置文件,以调解触摸屏的灵敏度、响应速度等参数。
2,getevent调试方法
getevent是Android体系中用于获取输入装备事故信息的工具,它可以捕获触摸屏、键盘等输入装备的原始事故数据。以下是利用getevent进行触摸屏调试的根本方法:
打开终端或命令提示符:
在PC上,可以通过ADB连接到Android装备,并在终端或命令提示符中执行getevent命令。
检察所有输入装备:
利用getevent -p命令列出所有可用的输入装备及其支持的事故类型。找到触摸屏对应的装备文件(如/dev/input/eventX)。
监督触摸屏事故:
利用getevent -lt /dev/input/eventX命令(此中X是触摸屏装备文件的编号)来及时监督触摸屏事故。这个命令会显示触摸屏的触摸、滑动、抬起等事故,以及相应的时间戳和事故代码。
分析事故数据:
通过分析getevent输出的数据,可以相识触摸屏的响应情况,包括触摸点的坐标、压力值、事故类型等。这有助于辨认触摸屏的硬件或软件标题。
发送模拟事故:
固然getevent主要用于获取事故信息,但可以利用sendevent命令向装备发送模拟的触摸、按键等事故。这在进行主动化测试或模拟用户操作时非常有用。
3,ADB 提供了一个更高级的 input 命令,用于模拟键盘和触摸屏输入,而不需要知道底层的装备文件和事故代码。例如:
模拟触摸按下(在屏幕的500,500位置):
adb shell input tap 500 500
模拟滑动(从屏幕的500,500位置滑动到1000,1000位置):
adb shell input swipe 500 500 1000 1000
input 命令是模拟触摸事故的一种更简朴、更直观的方法,通常对于大多数开辟者来说已经富足了。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]