行业知识
基于“xxx” Androidx平台的驱动及系统开发 之 触摸板篇
目录
- 一、基于全志 A133 Android10平台,适配1366x768 - ilitek2511触摸
- 1、原理图分析
- 2、驱动移植与适配
- 3、补丁和资源文件
- 二、基于瑞芯微 RK3566 Android11平台,适配GT9XX触摸
- 1、原理图分析
- 2、补丁及资源文件
- 三、遇到的问题与解决
- 1、基于amlogic Android9平台,使用(V-By-One接口LCD屏,USB接口触摸板)的一体式触摸屏
- 2、基于RK3566 Android11平台,使用(LVDS接口LCD屏,I2C接口触摸板)的分离式触摸屏
- 3、基于GT9XX触摸IC 触摸出现 -> "点偏位" 或 "划线异常不连续"
- ◕对该触摸驱动如何通过“CTP_CFG_GROUPx”这个配置组表更新到触摸IC寄存器内部的
- 有些人可能是通过外部去对触摸进行矫正的,那么就需要把自动写入对应的配置信息组表功能给关了
- 4、在屏幕���屏情况下做触摸唤醒屏幕功能,但是内核版本原因,无法使用原厂提供的手势识别功能
一、基于全志 A133 Android10平台,适配1366x768 - ilitek2511触摸
1、原理图分析
ilitek2511触摸使用的时I2C通信,因此重点关注SDA(数据)、SCK(时钟)、INT(中断)、RST(复位)这四个引脚即可。
2、驱动移植与适配
首先我们需要弄清除,触摸驱动是干嘛的,其实它只不过是通过I2C通信方式获取触摸板上触发的每个坐标点信息,通过事件方式上报,像这种通用的事件上报方式(触摸屏),上层的HAL、Framework等,原生Android或者原厂都是已经给做好了,已经只需要移植驱动层即可,一般别的层无需做修改的。
通过触摸IC厂商提供的驱动移植资料修改设备树,如下图原厂提供的设备树和我修改好的设备树。
设备树配置好后,直接将相关的驱动文件拷贝到 a\longan\kernel\linux-4.9\drivers\input\touchscreen 目录下创建的ilitek文件夹下即可,如下图所示。
通过以上操作,基本是已经将触摸驱动移植好了。
3、补丁和资源文件
下载链接
二、基于瑞芯微 RK3566 Android11平台,适配GT9XX触摸
1、原理图分析
2、补丁及资源文件
这里就不多分析了,直接给上补丁文件。
下载链接
三、遇到的问题与解决
1、基于amlogic Android9平台,使用(V-By-One接口LCD屏,USB接口触摸板)的一体式触摸屏
出现问题1:明明获取的USB设备,但是一直处于disconnected断开,一直打印不支持该USB设备,日志如下图所示
解决方法1:其实这个是控制板卡上的液晶屏的参数设置不对引起的,使得它们与触摸控制器不匹配,从而使触摸屏功能无法正常工作。因此只需要更具屏幕规格书修改屏幕参数即可。
2、基于RK3566 Android11平台,使用(LVDS接口LCD屏,I2C接口触摸板)的分离式触摸屏
出现问题1:因为触摸板和LCD屏是分离的,因此有可能出现整合者将触摸板和LCD屏摆放的起始方向不一致 或者 触摸板默认竖屏,而屏幕默认横屏, 这两种原因都会导致触摸和显示UI对不上。
解决方法1:在device/rockchip/3566x/目录下的build.prop文件中添加 persist.sys.touch.orientation=90, 如我设置90就是说将触摸板旋转方向为90度,这样就可以使UI和触摸板对上了。
3、基于GT9XX触摸IC 触摸出现 -> “点偏位” 或 “划线异常不连续”
出现的问题1:在原厂提供的文档可以可知,一般每一种触摸IC都有对应的“CTP_CFG_GROUP”这个触摸配置组的,它作用就是优化矫正触摸的。
解决方法1:在drivers\input\touchscreen\gt9xxnew\gt9xx.h文件中将原厂提供的.cfg峰位文件内从内容替换对应CTP_CFG_GROUPx中的内容,如果不确定CTP_CFG_GROUPx是哪个,那可以选择最无脑的方法,将CTP_CFG_GROUP[1~n]中的内容都给替换掉即可。
◕对该触摸驱动如何通过“CTP_CFG_GROUPx”这个配置组表更新到触摸IC寄存器内部的
找到drivers\input\touchscreen\gt9xxnew\gt9xx.c这个文件,该文件是触摸主功能的驱动文件。
在goodix_ts_probe函数下找到如下图的函数调用:
通过该函数索引到函数实现的位置,这里我把源码贴出来,已注释方式解释了。
/*******************************************************Function: Initialize gtp.Input: ts: goodix private dataOutput: Executive outcomes. 0: succeed, otherwise: failed*******************************************************/static s32 gtp_init_panel(struct goodix_ts_data *ts){ s32 ret = -1;#if GTP_DRIVER_SEND_CFG s32 i = 0; u8 check_sum = 0; u8 opr_buf[16] = {0}; u8 sensor_id = 0; /***************************(1)*********************************//* 这一部分是获取gt9xx.h文件定义的CTP_CFG_GROUPx配置信息组的信息到cfg_info_groupx[]中 */ u8 cfg_info_group1[] = CTP_CFG_GROUP1; u8 cfg_info_group2[] = CTP_CFG_GROUP2; u8 cfg_info_group3[] = CTP_CFG_GROUP3; u8 cfg_info_group4[] = CTP_CFG_GROUP4; u8 cfg_info_group5[] = CTP_CFG_GROUP5; u8 cfg_info_group6[] = CTP_CFG_GROUP6; u8 cfg_info_group7[] = CTP_CFG_GROUP7;u8 cfg_info_group8[] = CTP_CFG_GROUP8;/***************************(1)*********************************//***************************(2)*********************************//* 这个是创建send_cfg_buf数组,将上面活动的cfg_info_groupx信息保以元素存到该数组里 */ u8 *send_cfg_buf[] = {cfg_info_group1, cfg_info_group2, cfg_info_group3, cfg_info_group4, cfg_info_group5, cfg_info_group6,cfg_info_group7, cfg_info_group8};/***************************(2)*********************************//***************************(3)*********************************//* CFG_GROUP_LEN这个方法在gt9xx.h文件中有定义,原型是#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0])),它是计算数组的长度的 */ u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group1), CFG_GROUP_LEN(cfg_info_group2), CFG_GROUP_LEN(cfg_info_group3), CFG_GROUP_LEN(cfg_info_group4), CFG_GROUP_LEN(cfg_info_group5), CFG_GROUP_LEN(cfg_info_group6), CFG_GROUP_LEN(cfg_info_group7), CFG_GROUP_LEN(cfg_info_group8)};/***************************(3)*********************************/ dprintk(DEBUG_INIT,"Config Groups Lengths: %d, %d, %d, %d, %d, %d, %d, %d\n", cfg_info_len[0], cfg_info_len[1], cfg_info_len[2], cfg_info_len[3], cfg_info_len[4], cfg_info_len[5], cfg_info_len[6], cfg_info_len[7]);#if GTP_COMPATIBLE_MODE if (CHIP_TYPE_GT9F == ts->chip_type) { ts->fw_error = 0; } else#endif { /***************************(4)*********************************/ /* 检测固件是否正常 */ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1); if (SUCCESS == ret) { if (opr_buf[0] != 0xBE) { ts->fw_error = 1; printk("Firmware error, no config sent!\n"); return -1; } } /***************************(4)*********************************/ } /***************************(5)*********************************/ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) && (!cfg_info_len[3]) && (!cfg_info_len[4]) && (!cfg_info_len[5]) && (!cfg_info_len[6]) &&(!cfg_info_len[7])) { /* 如果所有配置信息组内容都为空,sensor_id值设置为0 */ sensor_id = 0; } else { #if GTP_COMPATIBLE_MODE msleep(50); #endif // ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID, &sensor_id, 1); /* 下面是判断配置信息的名称是对应哪个触摸IC的名称,从而再设置其对应的sensor_id值 */dprintk(DEBUG_INIT,"CTP name : %s\n",config_info.name); if (!strcmp(config_info.name,"gt9271_mb976a9")){ = 0; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name,"gt9110_wt097")){ sensor_id = 1; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name,"gt9271_wt097")){ sensor_id = 2; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name,"gt9110_g200")){ sensor_id = 3; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name,"gt9271_noah")){ sensor_id = 4; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name,"gt9271_p2")){ sensor_id = 5; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name, "gt911_1060")) {sensor_id = 6;dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else if (!strcmp(config_info.name, "gt911_784")) {sensor_id = 7;dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);} else { sensor_id = 0; dprintk(DEBUG_INIT,"gt9xx:sensor_id = %d\n",sensor_id);}ret = SUCCESS; if (SUCCESS == ret) { if (sensor_id >= 0x08) { printk("Invalid sensor_id(0x%02X), No Config Sent!\n", sensor_id); ts->pnl_init_error = 1; return -1; } } else { printk("Failed to get sensor_id, No config sent!\n"); ts->pnl_init_error = 1; return -1; } dprintk(DEBUG_INIT,"Sensor_ID: %d", sensor_id); }/***************************(5)*********************************//***************************(6)*********************************//* 上面设置的sensor_id值就是用来选择send_cfg_buf数组对应的触配置信息组元素的,这里获取对应配置信息组表的长度 */ ts->gtp_cfg_len = cfg_info_len[sensor_id]; /***************************(6)*********************************/ dprintk(DEBUG_INIT,"CTP_CONFIG_GROUP%d used, config length: %d\n", sensor_id + 1, ts->gtp_cfg_len);/***************************(7)*********************************//* 用获取对应配置信息组表的长度与定义的配置信息组表最小长度作对比,确保配置信息组表的数据在长度上是符合的 */ if (ts->gtp_cfg_len gtp_cfg_len); ts->pnl_init_error = 1; return -1; } /***************************(7)*********************************/#if GTP_COMPATIBLE_MODE if (CHIP_TYPE_GT9F == ts->chip_type) { ts->fixed_cfg = 0; } else#endif { /***************************(8)*********************************/ /* 获取IC寄存器对应的配置信息版本 */ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA, &opr_buf[0], 1); if (ret == SUCCESS) { dprintk(DEBUG_INIT,"CFG_GROUP%d Config Version: %d, 0x%02X; IC Config Version: %d, 0x%02X\n", sensor_id+1, send_cfg_buf[sensor_id][0], send_cfg_buf[sensor_id][0], opr_buf[0], opr_buf[0]); if (opr_buf[0] fixed_cfg = 0;/* 如果是opr_buf[0] fixed_cfg = 1;/* 配置信息组设置为固定不变标志 */ gtp_get_info(ts); return 0; } } else { printk("Failed to get ic config version!No config sent!\n"); return -1; } /***************************(8)*********************************/ }/***************************(9)*********************************//* 将send_cfg_buf[sensor_id]这个对应的配置信息组表信息拷贝到 config[GTP_ADDR_LENGTH]这个数组指定的开始位置中 */ memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); memcpy(&config[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id], ts->gtp_cfg_len); /***************************(9)*********************************/#if GTP_CUSTOM_CFG config[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH; config[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8); config[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT; config[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8); if (GTP_INT_TRIGGER == 0) //RISING { config[TRIGGER_LOC] &= 0xfe; } else if (GTP_INT_TRIGGER == 1) //FALLING { config[TRIGGER_LOC] |= 0x01; }#endif // GTP_CUSTOM_CFG/***************************(10)*********************************//* 计算校验和,并将校验和设置到config数组中 */ check_sum = 0; for (i = GTP_ADDR_LENGTH; i gtp_cfg_len; i++) { check_sum += config[i]; } config[ts->gtp_cfg_len] = (~check_sum) + 1; /***************************(10)*********************************/#else // driver not send config ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH; ret = gtp_i2c_read(ts->client, config, ts->gtp_cfg_len + GTP_ADDR_LENGTH); if (ret abs_x_max = GTP_MAX_WIDTH; ts->abs_y_max = GTP_MAX_HEIGHT; ts->int_trigger_type = GTP_INT_TRIGGER; }#endif // GTP_DRIVER_SEND_CFG/***************************(11)*********************************//* 修正abs_x_max、abs_y_max、int_trigger_type的值 */ if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) { ts->abs_x_max = (config[RESOLUTION_LOC + 1] abs_y_max = (config[RESOLUTION_LOC + 3] int_trigger_type = (config[TRIGGER_LOC]) & 0x03; }/***************************(11)*********************************/#if GTP_COMPATIBLE_MODE if (CHIP_TYPE_GT9F == ts->chip_type) { u8 sensor_num = 0; u8 driver_num = 0; u8 have_key = 0; have_key = (config[GTP_REG_HAVE_KEY - GTP_REG_CONFIG_DATA + 2] & 0x01); if (1 == ts->is_950) { driver_num = config[GTP_REG_MATRIX_DRVNUM - GTP_REG_CONFIG_DATA + 2]; sensor_num = config[GTP_REG_MATRIX_SENNUM - GTP_REG_CONFIG_DATA + 2]; if (have_key) { driver_num--; } ts->bak_ref_len = (driver_num * (sensor_num - 1) + 2) * 2 * 6; } else { driver_num = (config[CFG_LOC_DRVA_NUM] & 0x1F) + (config[CFG_LOC_DRVB_NUM]&0x1F); if (have_key) { driver_num--; } sensor_num = (config[CFG_LOC_SENS_NUM] & 0x0F) + ((config[CFG_LOC_SENS_NUM] >> 4) & 0x0F); ts->bak_ref_len = (driver_num * (sensor_num - 2) + 2) * 2; } GTP_INFO("Drv * Sen: %d * %d(key: %d), X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", driver_num, sensor_num, have_key, ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type); return 0; } else#endif { #if GTP_DRIVER_SEND_CFG /***************************(12)*********************************/ /* 通过调用gtp_send_cfg将已经整好的config配置信息组表数据写入到触摸IC对应的寄存器中 */ ret = gtp_send_cfg(ts->client); if (ret gtp_cfg_len; i++) { check_sum += config[i]; } config[ts->gtp_cfg_len] = (~check_sum) + 1; /***************************(12)*********************************/ #endif GTP_INFO("X_MAX: %d, Y_MAX: %d, TRIGGER: 0x%02x", ts->abs_x_max,ts->abs_y_max,ts->int_trigger_type); } msleep(10); return 0;}gtp_send_cfg函数原型,如下所示:
/*******************************************************Function: Send config.Input: client: i2c device.Output: result of i2c write operation. 1: succeed, otherwise: failed*********************************************************/s32 gtp_send_cfg(struct i2c_client *client){ s32 ret = 2;#if GTP_DRIVER_SEND_CFG s32 retry = 0; struct goodix_ts_data *ts = i2c_get_clientdata(client); if (ts->fixed_cfg) { GTP_INFO("Ic fixed config, no config sent!"); return 0; } else if (ts->pnl_init_error) { GTP_INFO("Error occured in init_panel, no config sent"); return 0; } GTP_INFO("Driver send config."); for (retry = 0; retry 0) { break; } }#endif return ret;}有些人可能是通过外部去对触摸进行矫正的,那么就需要把自动写入对应的配置信息组表功能给关了
在gt9xx.h文件中将#define GTP_DRIVER_SEND_CFG 1设置成0即可。
4、在屏幕熄屏情况下做触摸唤醒屏幕功能,但是内核版本原因,无法使用原厂提供的手势识别功能
出现问题1:由于我使用的内核版本与移植的触摸驱动使用的内核版本相差较大,导致无法使用原厂提供的手势识别功能。
须知:一般来说,触摸芯片厂商提供的触摸驱动内部有自带手势识别处理程序,也就是说一般是带有触摸唤醒的功能的。但是,一般默认是不使能的,也就是需要通过宏定义取打开的,我这里以Goodix Touchscreen驱动举例:
static void goodix_ts_work_func(struct work_struct *work)
这个函数就是处理手势识别的处理工作函数,那么这个就需要先启用“GTP_GESTURE_WAKEUP”这个宏定义才能开启手势功能的程序,而手势功能的程序需要启用“CONFIG_HAS_EARLYSUSPEND”这个挂起功能宏定义,才能让手势功能正常使用,但遗憾的是开启“CONFIG_HAS_EARLYSUSPEND”这个挂机功能的宏定义,有几个使用的内核接口函数都不存在,也就说明该驱动程序的手势功能在我这个Linux内核版本不支持。
解决方法1:我的Linux内核版本不支持,因此只能自己手动获取屏幕熄屏和亮屏状态,然后在上报触摸事件是上报“KEY_POWER”事件来开启屏幕。所以我需要先获取屏幕是开启还是休眠状态,通过这个状态去控制是否上报“KEY_POWER”事件来开启屏幕,从而避免在开屏状态下上报了“KEY_POWER”事件导致关屏发生。
这里我将我修改的补丁贴出,如下所示:
commit cdc3304684b7d6ef469a2e8ef401e5882ef07748Author: dengjiawen Date: Fri Oct 20 14:32:46 2023 +0800 屏幕息屏或者睡眠模式下,可触摸唤醒屏幕(基于gt9xx触摸系列添加该功能)diff --git a/longan/kernel/linux-4.9/drivers/input/touchscreen/gt9xxnew/gt9xx.c b/longan/kernel/linux-4.9/drivers/input/touchscreen/gt9xxnew/gt9xx.cindex 77dc9bc566..af1bc4be38 100644--- a/longan/kernel/linux-4.9/drivers/input/touchscreen/gt9xxnew/gt9xx.c+++ b/longan/kernel/linux-4.9/drivers/input/touchscreen/gt9xxnew/gt9xx.c@@ -50,7 +50,9 @@ #include #include "gt9xx.h" #include +#include "../../../video/fbdev/sunxi/disp2/disp/dev_disp.h" +extern int get_screen_status(void); /* 声明引入获取屏幕状态函数 */ #ifdef CONFIG_PM #include #endif@@ -664,6 +666,22 @@ static void gtp_touch_down(struct goodix_ts_data* ts,s32 id,s32 x,s32 y,s32 w) input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);++/************************* 获取屏幕状态,上报KEY_POWER事件来打开屏幕 *********************************/+ printk("get_screen_status = %d\n",get_screen_status());+ if(get_screen_status()) {+ /* 亮屏状态,不发出KEY_POWER事件唤醒屏幕 */+ printk("screen is open\n");+ } else {+ /* 熄屏状态,发出KEY_POWER事件唤醒屏幕 */+ input_event(ts->input_dev,EV_KEY,KEY_POWER,1);+ input_sync(ts->input_dev);+ input_event(ts->input_dev,EV_KEY,KEY_POWER,0);+ input_sync(ts->input_dev);+ printk("screen is close,in open the screen......\n");+ }+/*****************************************************************************************************/+ input_mt_sync(ts->input_dev); #endif @@ -2150,7 +2168,7 @@ static s8 gtp_request_input_dev(struct goodix_ts_data *ts) #if GTP_ICS_SLOT_REPORT input_mt_init_slots(ts->input_dev, 16); // in case of "out of memory" #else- ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);+ ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) | BIT_MASK(EV_KEY); /* 增加外部按键事件掩码,用于KEY_POWER事件 */ #endif __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); @@ -2169,7 +2187,7 @@ if(gtp_gesture_wakeup) input_set_capability(ts->input_dev, EV_KEY, ctp_key_list[index]); } //#endif-+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER); /* 将按键功能 KEY_POWER 添加到输入设备中的按键功能列表中。这意味着输入设备可以检测到并报告关于电源键的按下和释放事件 */ if(1 == exchange_x_y_flag) swap(ts->abs_x_max, ts->abs_y_max); diff --git a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/dev_disp.c b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/dev_disp.cindex 16c45a6529..8c3006fd25 100644--- a/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/dev_disp.c+++ b/longan/kernel/linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/dev_disp.c@@ -97,6 +97,8 @@ int composer_init(struct disp_drv_info *p_disp_drv); int hwc_dump(char *buf); #endif +static int screen_status = 0;/* 定义一个静态的屏幕状态变量 */+ static void disp_shutdown(struct platform_device *pdev); static ssize_t disp_sys_show(struct device *dev, struct device_attribute *attr, char *buf)@@ -3009,6 +3011,7 @@ static int disp_runtime_suspend(struct device *dev) if (ret解决方法2:修改原厂驱动源码,使其手势功能程序兼容本地使用的内核版本。
在gt9xx.c文件中找到static void goodix_ts_work_func(struct work_struct *work)这个函数,这个函数就是处理事件上报功能的,如下图是我截取的原厂驱动双击屏幕开屏的程序段。
这图程序可以看出,它是通过读取记录触摸手势状态的寄存器值来判断触发双击开屏功能的,如果触发了那么就会上报"KEY_POWER"事件来达到开屏目的,而它的屏幕状态通过doze_status这个静态变量取设置与判断的,最后会清除记录触摸手势状态的寄存器的值。
至于具体修改方法就自行取尝试啦。
上一篇:道客巴巴下载指南