个人总结之触摸屏调式(ILITEK系列)

2024-03-25 00:10

本文主要是介绍个人总结之触摸屏调式(ILITEK系列),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目场景:

需要在安卓系统屏幕上增加一块触摸屏,实现该触摸功能,本项目使用的是ILITEK251系列IC采集触摸屏的点位数据,通过LVDS串行解串芯片传输,基于芯驰X9系列SOC平台实现。

一、软硬件框架

框架示意图
SOC:该芯片是一款多核异构的SOC,R5核跑的是传统的轻量级OS,例如freeRTOS等轻量级系统,A核可以跑像安卓、linux等多任务系统,这种生态的好处是R5核可以满足实时性的要求,同时A核可以实现高处理能力以及多生态环境,多核之间的通信原理是基于寄存器和中断,在共享内存中实现跨系统的数据访问。目前主流的通信实现采用Virtio下的RPMsg。更详细的可以自行去官网查看理解。

串行解串器:用一对差分信号线实现短距离的数据串行传输。一般是成对匹配使用,芯片和SOC之间采用IIC的协议通信,串行器,解串器分别可以看成是两个设备,挂在一条IIC的总线上。串行解串芯片内部自带中断控制INT,还会集成其他外设,例如LVDS、I2S、通用可透传的GPIO、SPI等等。

ILITEK:该触摸IC是负责采集触摸屏的报点数据,然后触发中断,中断通过解串器,由LINK传回串行器,硬件上串行器INT连接SOC的GPIO,配置GPIO为外部中断触发告知SOC。本项目中,由SOC通过IIC配置串行器的寄存器,实现触摸IC在IIC总线上的挂载,然后在SOC通过IIC对该IC芯片的寄存器读写,既一条IIC总线上挂载三个设备,串行器、解串器以及触摸IC。

二、驱动移植

1、首先对设备树进行配置:

&i2c8 {status = "okay";ilitek:ilitek@41 {compatible = "tchip,ilitek";//设备和驱动根据compatible属性配对reg = <0x41>;				//设备的I2C地址ilitek,irq-gpio = <&port4d 12 0x0>;//外部中断号108ilitek,vbus = "vcc_i2c";	//一些设备信息的描述ilitek,vdd = "vdd";ilitek,name = "ilitek_i2c";};
};

在具体的dts文件中,引用&i2c8,然后status使能okay,证明开启该节点与驱动进行配对,如果为disabled则不启动配对

i2c8: i2c@30b00000 {/*compatible = "snps,designware-i2c";*/compatible = "sd,virtual-i2c";//使用虚拟i2c连接R5核phy-num = /bits/ 8 <8>;reg = <0x0 0x30b00000 0x0 0x1000>;interrupts = <0 40 4>;#address-cells = <1>;#size-cells = <0>;clocks=<&I2C_SEC0_9>;clock-names = "i2c-clk";#clock-frequency = <100000>;timeout = <10>; /* ms */status = "disabled";};

修改原厂i2c8的设备树描述信息,因为实际的i2c8总线控制已经挂到R5核上,所以我们这里不使用实际的IIC总线,而是注册一个虚拟的IIC总线控制模拟器,会匹配另外一个虚拟化的IIC驱动,由这个控制器传递IIC通信数据给到R5核上实际对应的IIC驱动,然后发往设备端.

2、移植驱动文件到指定的内核目录下,对于触摸屏驱动,linux内核下有专门的输入子系统,该路径为/drivers/input/touchscreen,,修改input目录下的makefile
注释: #obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
增加:obj-y += touchscreen/
我这里为了方便调试,直接使用obj-y来使能编译 touchscreen/下的文件,正常的话应该make menuconfig来配置CONFIG_INPUT_TOUCHSCREEN = y
然后在 touchscreen/创建一个文件夹ilitek,修改touchscreen/下的makefile
增加:obj-y += ilitek/ 使能编译该目录下的文件,然后将驱动文件放入该目录下,清除内核输出信息,重新编译即可

三、驱动代码部分解析

static int __init ilitek_touch_driver_init(void)
{tp_msg("add ILITEK touch device driver\n");return i2c_add_driver(&ilitek_touch_device_driver);  //加入I2C总线匹配
}static void __exit ilitek_touch_driver_exit(void)
{tp_msg("remove touch device driver i2c driver.\n");#ifdef ILITEK_SPI_INTERFACEspi_unregister_driver(&ilitek_touch_device_driver);
#elsei2c_del_driver(&ilitek_touch_device_driver);
#endif
}
#endifmodule_init(ilitek_touch_driver_init);		//平台模块注册入口函数
module_exit(ilitek_touch_driver_exit);
MODULE_AUTHOR("ILITEK");
MODULE_LICENSE("GPL");``static struct i2c_driver ilitek_touch_device_driver = {
#endif.driver = {.name = ILITEK_TS_NAME,.owner = THIS_MODULE,
#ifdef CONFIG_OF.of_match_table = ilitek_touch_match_table,  //设备树的匹配表
#endif},.probe = ilitek_touch_driver_probe,								//如果驱动和设备配对成功则调用该函数.remove = ilitek_touch_driver_remove,};#ifdef CONFIG_OF
static struct of_device_id ilitek_touch_match_table[] = {{.compatible = "tchip,ilitek",},	//该信息要与设备树描述的compatible属性一致{},
};
#endifstatic int ilitek_touch_driver_probe(struct i2c_client *client,const struct i2c_device_id *id)
{if (!client) {tp_err("i2c client is NULL\n");return -ENODEV;}return ilitek_main_probe(client, &client->dev); //匹配成功以后执行主线函数
}

则选ilitek_main_probe函数中部分功能分析

int ilitek_main_probe(void *client, struct device *device)
{.......if (!(ts = kzalloc(sizeof(*ts), GFP_KERNEL))) {  //分配一块内存tp_err("allocate ts failed\n");return -ENOMEM;}ilitek_request_gpio();//注册一个外部的中断iots->dev = ilitek_dev_init(interface_i2c, &dev_cb, ts);//对ts结构注册一个回调函数集if (!ts->dev)goto err_free_gpio;ts->process_and_report = ilitek_read_data_and_report_3XX; //中断下半部执行函数ts->irq_trigger_type = IRQF_TRIGGER_FALLING;			  //下降沿触发if (ilitek_request_irq())//注册中断,申请一个软件中断号goto err_dev_exit;......ilitek_create_sysfsnode(); //在/sys/下创建信息节点ilitek_create_tool_node();//在/proc/下创建信息节点ilitek_init_netlink();//创建一个netlink,用于应用和驱动之间的通信......return 0;err_free_irq:free_irq(ts->irq, ts);err_dev_exit:ilitek_dev_exit(ts->dev);err_free_gpio:ilitek_free_gpio();kfree(ts);return -ENODEV;
}

看下中断函数的注册则选

static int ilitek_request_irq(void)
{int error;ts->irq = gpio_to_irq(ts->irq_gpio);//将GPIO号转换......tp_msg("ts->irq: %d\n", ts->irq);if (ts->irq <= 0)return -EINVAL;//方法一、中断注册函数,内核线程注册方法/*error = request_threaded_irq(ts->irq, NULL, ilitek_i2c_isr,ts->irq_trigger_type | IRQF_ONESHOT,"ilitek_touch_irq", ts);*///方法二、这是中断注册函数方法,配合工作队列error = request_irq(ts->irq, ilitek_i2c_isr, ts->irq_trigger_type, "ilitek_touch_irq", ts);	 if (error) {tp_err("request threaded irq failed, err: %d\n", error);return error;}//由于之前调试出点问题,怀疑过是不是方法一有问题,所以注册了一个工作队列代替//初始化一个工作队列INIT_WORK(&ts->my_workqueue,autofure_work);ts->irq_registerred = true;atomic_set(&ts->irq_enabled, 1);return 0;
}

看下中断下半部则选

int ilitek_read_data_and_report_3XX(void)
{......ts->buf[0] = 0x10;ret = ilitek_write_and_read(ts->buf, 1, 1, ts->buf, 32);//接收IIC过来的数据包if (ret < 0) {tp_err("get touch information err\n");if (ts->is_touched) {ilitek_touch_release_all_point();ilitek_check_key_release(x, y, 0);}return ret;}......//取出数据包中x y的坐标x = ((ts->buf[i * 5 + 1] & 0x3F) << 8) + ts->buf[i * 5 + 2];y = (ts->buf[i * 5 + 3] << 8) + ts->buf[i * 5 + 4];if (!(ts->touch_key_hold_press)) {if (ILITEK_REVERT_X)x = ts->dev->screen_info.x_max - x + ts->dev->screen_info.x_min;if (ILITEK_REVERT_Y)y = ts->dev->screen_info.y_max - y + ts->dev->screen_info.y_min;//根据分辨率将x y坐标转换#ifdef ILITEK_USE_LCM_RESOLUTION  x = (x - ts->dev->screen_info.x_min) * TOUCH_SCREEN_X_MAX /(ts->dev->screen_info.x_max - ts->dev->screen_info.x_min);y = (y - ts->dev->screen_info.y_min) * TOUCH_SCREEN_Y_MAX /(ts->dev->screen_info.y_max - ts->dev->screen_info.y_min);#endifts->is_touched = true;tp_msg("Touch id=%02X, x: %04d, y: %04d\n", i, x, y);ilitek_touch_down(i, x, y, 10, 128, 1);//触摸过程的上报}......input_sync(input); //每次上报应用都要sync一下return 0;
}

四、调试异常和解决

1、IIC通信不成功:
刚开始的硬件连接是soc-串行器-解串器-触摸屏,发现IIC只能访问串行器-解串器,却读取不到触摸屏的设备地址,然后屏幕和触摸IC都换了一个发现还是不行,后面尝试跳线SOC直连到触摸IC,发现读取成功,那问题就是串行解串转换出了问题,然后排查到解串器的SDA 、SCL是上拉到1.8V,然后通过电平转换到3.3V到触摸IC(触摸IC是3.3V的上拉),看似没问题,将电平转换芯片去掉然后解串器的SDA 、SCL上拉到3.3V,通信成功!!后面排查可能原因是电平转换过程中电平不稳定导致的通信失败。

2、IIC通信过程中,读写长字节数据会出现错误的异常:
SOC在发送和接收触摸IC的数据过程中,读写一些几个字节的数据通信都是正常,例如获取版本号、设置工作模式等等,但是当触发中断接收长字节数据时,后面十几个字节开始收到的数据都是不正常的,一开始觉得是触摸IC厂发过来的数据包有问题(战略上拉扯了很久…),然后实在没办法,我叫硬件给我改回来直连SOC试试(之前硬件工程师觉得都可以访问到设备就证明是没问题),然后我坚定要改回来试试,一改发现直连触摸IC一点问题都没有…还是配对芯片的问题,后面排查发现解串器的SDA、SCL是上拉来10K电阻,然后根据数据手册和他们原厂支持改成2.2K,完美通信成功。

3、有部分触摸区域没反应:
触摸过程中边缘的一块区域没反应,触摸也不会产生下降沿中断,然后他们IC技术支持一直说是SOC驱动的问题,我就说你中断信号都过不来,我这边只是做数据处理,后面使用他们提供的画线工具,接上USB到windows下画线,发现同样没有反应,最后是他们提供一版新的触摸IC固件,烧录后成功解决。

五、总结

驱动的调试过程中,都会遇到各种各样意想不到的问题,有时候要发散思维,从多个点切入,将某个疑难问题分部解析来看待,调试与硬件相关的问题都是很考验人的,所以遇到这些问题的时候要冷静多角度去考虑。

最后,在安卓系统中完成触摸的功能,验证来点击区域的正确性。

这篇关于个人总结之触摸屏调式(ILITEK系列)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/843253

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0