1. 硬件层
触摸屏硬件:触摸屏通常包括电容式或电阻式触摸屏,它们通过感应手指的触摸来产生信号。电容式触摸屏利用人体的电流感应工作,而电阻式触摸屏则通过压力改变电阻值来检测触摸。
传感器与接口:触摸屏包含多个触摸传感器,这些传感器通过总线接口(如I2C、SPI等)与主控芯片通信,将触摸数据(如位置、压力等)传输给软件层。
2. 软件层
输入子体系: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,将供应商提供的驱动驱动资料拷贝到该目录下;
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[i] = cinfo.x[i];
- cinfo.y[i] = cinfo.y[i];
-
- tpd_down(cinfo.x[i], cinfo.y[i], cinfo.id[i]); //上报按下数据
- printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x[i], cinfo.y[i], cinfo.id[i]);
- }
- input_sync(tpd->dev);
- }
- else
- {
- tpd_up(cinfo.x[0], cinfo.y[0]); //上报弹起数据
- //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[0] = 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[i], cinfo.y[i], cinfo.id[i]);
-
- cinfo.x[i] = cinfo.x[i];
- cinfo.y[i] = cinfo.y[i];
-
- tpd_down(cinfo.x[i], cinfo.y[i], cinfo.id[i]); //按下数据处理
- printk(KERN_DEBUG"----calibration----- X:%4d, Y:%4d, P:%4d \n", cinfo.x[i], cinfo.y[i], cinfo.id[i]);
- }
- input_sync(tpd->dev);
- }
- else
- {
- tpd_up(cinfo.x[0], cinfo.y[0]); //弹起数据处理
- //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企服之家,中国第一个企服评测及商务社交产业平台。 |