前言
前面简单聊了一下多点触控协议,接下来找个驱动来看看具体实现。目前市面上多点触控芯片用得比较多的主要是汇顶和敦泰。我们找一款敦泰的芯片来看看。
多点触控驱动分析
Linux版本:5.10
芯片:FT5436 (10点触控芯片)
(1)加载和卸载函数
static const struct i2c_device_id fts_ts_id[] = { {FTS_DRIVER_NAME, 0}, {}, }; //设备树匹配 static const struct of_device_id fts_dt_match[] = { {.compatible = "focaltech,ft5436", }, {}, }; MODULE_DEVICE_TABLE(of, fts_dt_match); static struct i2c_driver fts_ts_driver = { .probe = fts_ts_probe, .remove = fts_ts_remove, .driver = { .name = FTS_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(fts_dt_match), }, .id_table = fts_ts_id, }; static int __init fts_ts_init(void) { int ret = 0; FTS_FUNC_ENTER(); //添加i2c设备驱动 ret = i2c_add_driver(&fts_ts_driver); if ( ret != 0 ) { FTS_ERROR("Focaltech touch screen driver init failed!"); } FTS_FUNC_EXIT(); return ret; } static void __exit fts_ts_exit(void) { i2c_del_driver(&fts_ts_driver); } module_init(fts_ts_init); module_exit(fts_ts_exit);
FT5436使用I2C接口进行通信,所以注册i2c_driver。
(2)probe()函数
static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; struct fts_ts_data *ts_data = NULL; FTS_INFO("Touch Screen(I2C BUS) driver prboe..."); //检查I2C功能 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { FTS_ERROR("I2C not supported"); return -ENODEV; } //为结构体分配内存 ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL); if (!ts_data) { FTS_ERROR("allocate memory for fts_data fail"); return -ENOMEM; } //保存数据,方便后面使用 fts_data = ts_data; ts_data->client = client; //i2c设备 ts_data->dev = &client->dev; ts_data->log_level = 1; ts_data->fw_is_running = 0; i2c_set_clientdata(client, ts_data); //进入真正的probe流程 ret = fts_ts_probe_entry(ts_data); //...... FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully"); return 0; }
初始化结构体,保存i2c设备,然后调用fts_ts_probe_entry进入设备的初始化。
static int fts_ts_probe_entry(struct fts_ts_data *ts_data) { int ret = 0; int pdata_size = sizeof(struct fts_ts_platform_data); struct device_node *np = ts_data->dev->of_node; FTS_FUNC_ENTER(); FTS_INFO("%s", FTS_DRIVER_VERSION); ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL); if (!ts_data->pdata) { FTS_ERROR("allocate memory for platform_data fail"); return -ENOMEM; } //获取平台数据 if (ts_data->dev->of_node) { //dts解析 ret = fts_parse_dt(ts_data->dev, ts_data->pdata); if (ret) FTS_ERROR("device-tree parse fail"); } else { if (ts_data->dev->platform_data) { memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size); } else { FTS_ERROR("platform_data is null"); return -ENODEV; } } //创建工作队列 ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); //..... //在输入子系统中注册设备 ret = fts_input_init(ts_data); if (ret) { FTS_ERROR("input initialize fail"); goto err_input_init; } //...... //gpio配置(中断,复位) ret = fts_gpio_configure(ts_data); if (ret) { FTS_ERROR("configure the gpios fail"); goto err_gpio_config; } //..... //创建proc调试接口 ret = fts_create_apk_debug_channel(ts_data); if (ret) { FTS_ERROR("create apk debug node fail"); } //创建sys调试接口 ret = fts_create_sysfs(ts_data); if (ret) { FTS_ERROR("create sysfs node fail"); } #if FTS_POINT_REPORT_CHECK_EN //初始化work(用于处理中断数据) ret = fts_point_report_check_init(ts_data); if (ret) { FTS_ERROR("init point report check fail"); } #endif //..... //注册中断 ret = fts_irq_registration(ts_data); //..... //初始化固件更新功能(用于为Touch模组更新固件) ret = fts_fwupg_init(ts_data); if (ret) { FTS_ERROR("init fw upgrade fail"); } //配置休眠唤醒 #if defined(CONFIG_FB) if (ts_data->ts_workqueue) { INIT_WORK(&ts_data->resume_work, fts_resume_work); } ts_data->tp.tp_resume = fts_ts_late_resume; ts_data->tp.tp_suspend = fts_ts_early_suspend; //通过LCD屏亮灭来决定唤醒 tp_register_fb(&ts_data->tp); #elif defined(CONFIG_HAS_EARLYSUSPEND) ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; ts_data->early_suspend.suspend = fts_ts_early_suspend; ts_data->early_suspend.resume = fts_ts_late_resume; register_early_suspend(&ts_data->early_suspend); #endif //使能中断唤醒 if (of_property_read_bool(np, "wakeup-source")) { device_init_wakeup(&ts_data->client->dev, 1); enable_irq_wake(ts_data->irq); } FTS_FUNC_EXIT(); return 0; //...... }
上面主要完成如下工作:
1.解析dts
2.创建工作队列(处理中断下半部)
3.注册输入子系统设备
4.初始化GPIO(中断和复位)
5.创建proc和sys调试接口
6.注册中断
7.初始化固件更新功能(用于升级Touch芯片上的固件)
8.配置休眠唤醒
(3)注册输入设备
static int fts_input_init(struct fts_ts_data *ts_data) { int ret = 0; int key_num = 0; struct fts_ts_platform_data *pdata = ts_data->pdata; struct input_dev *input_dev; FTS_FUNC_ENTER(); //分配输入设备(input_dev) input_dev = input_allocate_device(); if (!input_dev) { FTS_ERROR("Failed to allocate memory for input device"); return -ENOMEM; } /* Init and register Input device */ input_dev->name = FTS_DRIVER_NAME; input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = ts_data->dev; input_set_drvdata(input_dev, ts_data); __set_bit(EV_SYN, input_dev->evbit); //同步事件 __set_bit(EV_ABS, input_dev->evbit); //绝对坐标事件(比如X,Y坐标信息) __set_bit(EV_KEY, input_dev->evbit); //按键事件 __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); if (pdata->have_key) { FTS_INFO("set key capabilities"); for (key_num = 0; key_num < pdata->key_number; key_num++) input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); } #if FTS_MT_PROTOCOL_B_EN //Type B协议 //初始化SLOT(最大触点数) input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); #else input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0); #endif //设置X,Y,接触轴 input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); #if FTS_REPORT_PRESSURE_EN //设置压力值 input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); #endif //注册输入设备 ret = input_register_device(input_dev); //..... ts_data->input_dev = input_dev; FTS_FUNC_EXIT(); return 0; }
设置支持的输入事件,然后注册到输入子系统中。
(4)中断处理
static irqreturn_t fts_irq_handler(int irq, void *data) { struct fts_ts_data *ts_data = (struct fts_ts_data *)data; if (!ts_data) { FTS_ERROR("[INTR]: Invalid fts_ts_data"); return IRQ_HANDLED; } if (device_can_wakeup(&ts_data->client->dev)) pm_stay_awake(&ts_data->client->dev); //读取数据 fts_irq_read_report(); if (device_can_wakeup(&ts_data->client->dev)) pm_relax(&ts_data->client->dev); return IRQ_HANDLED; }
static void fts_irq_read_report(void) { int ret = 0; struct fts_ts_data *ts_data = fts_data; //..... #if FTS_POINT_REPORT_CHECK_EN //添加work到工作队列中(延迟) fts_prc_queue_work(ts_data); #endif //读取touch数据 ret = fts_read_parse_touchdata(ts_data); if (ret == 0) { mutex_lock(&ts_data->report_mutex); //上报数据 #if FTS_MT_PROTOCOL_B_EN fts_input_report_b(ts_data); //Type B #else fts_input_report_a(ts_data); //Type A #endif mutex_unlock(&ts_data->report_mutex); } //...... }
读取touch数据并解析,然后进行事件上报。
(5)读取和解析数据
static int fts_read_parse_touchdata(struct fts_ts_data *data) { int ret = 0; int i = 0; u8 pointid = 0; int base = 0; struct ts_event *events = data->events; int max_touch_num = data->pdata->max_touch_number; u8 *buf = data->point_buf; //通过I2C读取芯片数据 ret = fts_read_touchdata(data); if (ret) { return ret; } //对读取到的数据进行解析 data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; data->touch_point = 0; if (data->ic_info.is_incell) { if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF) && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { FTS_DEBUG("touch buff is 0xff, need recovery state"); fts_release_all_finger(); fts_tp_state_recovery(data); return -EIO; } } //..... for (i = 0; i < max_touch_num; i++) { base = FTS_ONE_TCH_LEN * i; pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4; if (pointid >= FTS_MAX_ID) break; else if (pointid >= max_touch_num) { FTS_ERROR("ID(%d) beyond max_touch_number", pointid); return -EINVAL; } data->touch_point++; events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4; events[i].p = buf[FTS_TOUCH_PRE_POS + base]; if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { FTS_INFO("abnormal touch data from fw"); return -EIO; } } if (data->touch_point == 0) { FTS_INFO("no touch point information"); return -EIO; } return 0; }
fts_read_touchdata()从芯片中读出数据,然后对数据进行解析。
(6)事件上报
static int fts_input_report_b(struct fts_ts_data *data) { int i = 0; int uppoint = 0; int touchs = 0; bool va_reported = false; u32 max_touch_num = data->pdata->max_touch_number; struct ts_event *events = data->events; for (i = 0; i < data->touch_point; i++) { if (fts_input_report_key(data, i) == 0) { continue; } va_reported = true; //上报ABS_MT_SLOT事件 input_mt_slot(data->input_dev, events[i].id); //按下/抬起手指 if (EVENT_DOWN(events[i].flag)) { //上报手指按下和坐标等信息 //使用MT_TOOL_FINGER来确定按下和抬起, //就不需要使用ABS_MT_TRACKING_ID来控制触点的生命周期了 input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); #if FTS_REPORT_PRESSURE_EN if (events[i].p <= 0) { events[i].p = 0x3f; } //上报压力值 input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); #endif if (events[i].area <= 0) { events[i].area = 0x09; } //上报触点的主轴长度 input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); //上报X,Y坐标 input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); touchs |= BIT(events[i].id); data->touchs |= BIT(events[i].id); //...... } else { uppoint++; //上报手指抬起 input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); data->touchs &= ~BIT(events[i].id); if (data->log_level >= 1) { FTS_DEBUG("[B]P%d UP!", events[i].id); } } } if (unlikely(data->touchs ^ touchs)) { for (i = 0; i < max_touch_num; i++) { if (BIT(i) & (data->touchs ^ touchs)) { if (data->log_level >= 1) { FTS_DEBUG("[B]P%d UP!", i); } va_reported = true; input_mt_slot(data->input_dev, i); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); } } } data->touchs = touchs; //上报Touch按键事件 if (va_reported) { /* touchs==0, there's no point but key */ if (EVENT_NO_DOWN(data) || (!touchs)) { //所有触点都抬起了 if (data->log_level >= 1) { FTS_DEBUG("[B]Points All Up!"); } input_report_key(data->input_dev, BTN_TOUCH, 0); } else { input_report_key(data->input_dev, BTN_TOUCH, 1); } } //同步(告诉上层本次上报结束) input_sync(data->input_dev); return 0; }
上报各种事件(MT_TOOL_FINGER/ABS_MT_POSITION_X/ABS_MT_POSITION_Y等 )给上层。
总结
整体分析下来,会发现多点触控驱动并不难,主要就是注册为输入子系统,然后中断触发后读取触控数据,最后通过输入子系统上报数据。所有输入子系统的驱动基本都是这个套路。
审核编辑:刘清
-
LINUX内核
+关注
关注
1文章
317浏览量
22121 -
I2C接口
+关注
关注
1文章
133浏览量
25814 -
触控芯片
+关注
关注
2文章
76浏览量
21883 -
FFTs
+关注
关注
0文章
2浏览量
5440
原文标题:Linux驱动分析之多点触控驱动
文章出处:【微信号:嵌入式软件开发交流,微信公众号:嵌入式软件开发交流】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
聊一下IGBT驱动中的参考电位问题

FPGA板和多点触控屏之间的连接?
多点触控技术的全解析
单点触控和多点触控区别是什么?原理分析
多点悬浮触控技术将掀起一场新革命
多点触控应用的模拟多点触控模拟环境搭建

简单聊一聊DPT技术-double pattern technology
多点触控和单点触控区别

ElfBoard技术贴|如何调整eMMC存储分区
ELF 2开发板基于瑞芯微RK3588高性能处理器设计,拥有四核ARM Cortex-A76与四核ARM Cortex-A55的CPU架构,主频高达2.4GHz,内置6TOPS算力的NPU,这一设计让它能够轻松驾驭多种深度学习框架,高效处理各类复杂的AI任务。

米尔基于MYD-YG2LX系统启动时间优化应用笔记
1.概述MYD-YG2LX采用瑞萨RZ/G2L作为核心处理器,该处理器搭载双核Cortex-A55@1.2GHz+Cortex-M33@200MHz处理器,其内部集成高性能3D加速引擎Mail-G31GPU(500MHz)和视频处理单元(支持H.264硬件编解码),16位的DDR4-1600/DDR3L-1333内存控制器、千兆以太网控制器、USB、CAN、

运放技术——基本电路分析
虚短和虚断的概念由于运放的电压放大倍数很大,一般通用型运算放大器的开环电压放大倍数都在80dB以上。而运放的输出电压是有限的,一般在10V~14V。因此运放的差模输入电压不足1mV,两输入端近似等电位,相当于“短路”。开环电压放大倍数越大,两输入端的电位越接近相等。“虚短”是指在分析运算放大器处于线性状态时,可把两输入端视为等电位,这一特性称为虚假短路,简称

飞凌嵌入式携手中移物联,谱写全国产化方案新生态
4月22日,飞凌嵌入式“2025嵌入式及边缘AI技术论坛”在深圳成功举办。中移物联网有限公司(以下简称“中移物联”)携OneOS操作系统与飞凌嵌入式共同推出的工业级核心板亮相会议展区,操作系统产品部高级专家严镭受邀作《OneOS工业操作系统——助力国产化智能制造》主题演讲。

ATA-2022B高压放大器在螺栓松动检测中的应用
实验名称:ATA-2022B高压放大器在螺栓松动检测中的应用实验方向:超声检测实验设备:ATA-2022B高压放大器、函数信号发生器,压电陶瓷片,数据采集卡,示波器,PC等实验内容:本研究基于振动声调制的螺栓松动检测方法,其中低频泵浦波采用单频信号,而高频探测波采用扫频信号,利用泵浦波和探测波在接触面的振动声调制响应对螺栓的松动程度进行检测。通过螺栓松动检测

MOS管驱动电路——电机干扰与防护处理
此电路分主电路(完成功能)和保护功能电路。MOS管驱动相关知识:1、跟双极性晶体管相比,一般认为使MOS管导通不需要电流,只要GS电压(Vbe类似)高于一定的值,就可以了。MOS管和晶体管向比较c,b,e—–>d(漏),g(栅),s(源)。2、NMOS的特性,Vgs大于一定的值就会导通,适合用于源极接地时的情况(低端驱动),只要栅极电压达到4V或10V就可以

压敏(MOV)在电机上的应用剖析
一前言有刷直流电机是一种较为常见的直流电机。它的主要特点包括:1.结构相对简单,由定子、转子、电刷和换向器等组成;2.通过电刷与换向器的接触来实现电流的换向,从而使电枢绕组中的电流方向周期性改变,保证电机持续运转;3.具有调速性能较好等优点,可以通过改变电压等方式较为方便地调节转速。有刷直流电机在许多领域都有应用,比如一些电动工具、玩具、小型机械等。但它也存

硬件原理图学习笔记
这一个星期认真学习了硬件原理图的知识,做了一些笔记,方便以后查找。硬件原理图分为三类1.管脚类(gpio)和门电路类输入输出引脚,上拉电阻,三极管与门,或门,非门上拉电阻:正向标志作用,给悬空的引脚一个确定的状态三极管:反向三极管(gpio输出高电平,NP两端导通,被控制端导通,电压为0)->NPN正向三极管(gpio输出低电平,PN两端导通,被控制端导通,

TurMass™ vs LoRa:无线通讯模块的革命性突破
TurMass™凭借其高传输速率、强大并发能力、双向传输、超强抗干扰能力、超远传输距离、全国产技术、灵活组网方案以及便捷开发等八大优势,在无线通讯领域展现出强大的竞争力。

RZT2H CR52双核BOOT流程和例程代码分析
RZT2H是多核处理器,启动时,需要一个“主核”先启动,然后主核根据规则,加载和启动其他内核。本文以T2H内部的CR52双核为例,说明T2H多核启动流程。

干簧继电器在RF信号衰减中的应用与优势
在电子测试领域,RF(射频)评估是不可或缺的一部分。无论是研发阶段的性能测试,还是生产环节的质量检测,RF测试设备都扮演着关键角色。然而,要实现精准的RF评估,测试设备需要一种特殊的电路——衰减电路。这些电路的作用是调整RF信号的强度,以便测试设备能够准确地评估RF组件和RF电路的各个方面。衰减器的挑战衰减器的核心功能是校准RF信号的强度。为了实现这一点,衰

ElfBoard嵌入式教育科普|ADC接口全面解析
当代信息技术体系中,嵌入式系统接口作为数据交互的核心基础设施,构成了设备互联的神经中枢。基于标准化通信协议与接口规范的技术架构,实现了异构设备间的高效数据交换与智能化协同作业。本文选取模数转换接口ADC作为技术解析切入点,通过系统阐释其工作机理、性能特征及重要参数,为嵌入式学习者爱好者构建全维度接口技术认知框架。

深入理解C语言:C语言循环控制
在C语言编程中,循环结构是至关重要的,它可以让程序重复执行特定的代码块,从而提高编程效率。然而,为了避免程序进入无限循环,C语言提供了多种循环控制语句,如break、continue和goto,用于改变程序的执行流程,使代码更加灵活和可控。本文将详细介绍这些语句的作用及其应用场景,并通过示例代码进行说明。Part.1break语句C语言中break语句有两种

第 21 届(顺德)家电电源与智能控制技术研讨会圆满落幕--其利天下斩获颇丰
2025年4月25日,其利天下应大比特之邀出席第21届(顺德)家电电源与智能控制技术研讨会,已圆满落幕。一、演讲回顾我司研发总监冯建武先生在研讨会上发表了主题为《重新定义风扇驱动:一套算法兼容百种电机的有效磁链观测器方案》的演讲,介绍了我司研发自适应技术算法(简称),该方案搭载有效磁链观测器,适配百种电机类型,结合FOC算法可实现免调参稳定启动、低速静音控制

来自资深工程师对ELF 2开发板的产品测评
来自资深工程师对ELF 2开发板的使用测评
评论