前言
前面简单聊了一下多点触控协议,接下来找个驱动来看看具体实现。目前市面上多点触控芯片用得比较多的主要是汇顶和敦泰。我们找一款敦泰的芯片来看看。
多点触控驱动分析
linux版本:5.10
芯片:ft5436 (10点触控芯片)
通信接口: i2c
(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 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 > 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) 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 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 input_dev, abs_mt_pressure, events[i].p);#endif if (events[i].area 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 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等 )给上层。
总结
整体分析下来,会发现多点触控驱动并不难,主要就是注册为输入子系统,然后中断触发后读取触控数据,最后通过输入子系统上报数据。所有输入子系统的驱动基本都是这个套路。
大屏时代投屏需求彰显,中兴携手乐播深度定制投屏功能打造新机
全自动生化分析仪有什么作用,可以检测什么指标
利用三维嵌入式电极提升压电陶瓷能量收集器输出电流密度
关于无线通信系统的仿真、分析和测试的结果分享
MS5541/MS5542 高精度数模转换器特点及应用
简单聊一下多点触控协议
两千元左右的投影仪推荐 5款全网最公认的投影仪推荐
戴耳机耳朵不舒服?骨传导耳机才是最佳选择
同步升压DC-DC转换器概述/特征/功能/布局注意事项
C++多继承的二义性问题
“逻辑门”RNA电路 利用生物计算机处理多个复杂信号
磁开关技术参数及规格
FPGA学习经验总结
2018中国创新力企业榜单 阿里巴巴斑马智行公司名列前茅
如何设计用于插值和抽取的IIR滤波器
5G无线通信技术开启通信数据信息传输时代的开始
荣耀加冕!小匠物联斩获2023“光明奖”三项大奖
M1处理器有望支持eGPUt显卡扩展
怎么使用DMA在FPGA中的HDL和嵌入式C之间传输数据?
一种多功能输出开关电源