0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

简单聊一下多点触控协议

嵌入式软件开发交流 来源:嵌入式软件开发交流 2023-04-17 09:20 次阅读

前言

前面简单聊了一下多点触控协议,接下来找个驱动来看看具体实现。目前市面上多点触控芯片用得比较多的主要是汇顶和敦泰。我们找一款敦泰的芯片来看看。

多点触控驱动分析

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 < 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

    文章

    311

    浏览量

    21388
  • I2C接口
    +关注

    关注

    1

    文章

    119

    浏览量

    24849
  • 触控芯片
    +关注

    关注

    2

    文章

    62

    浏览量

    21244
  • FFTs
    +关注

    关注

    0

    文章

    2

    浏览量

    5328

原文标题:Linux驱动分析之多点触控驱动

文章出处:【微信号:嵌入式软件开发交流,微信公众号:嵌入式软件开发交流】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    单层ITO多点控技术

    本帖最后由 eehome 于 2013-1-5 10:04 编辑 是否有人研究过单层ITO多点控技术,发现这方面专利很少,有价值的paper也不多,若有人研究过,求交流,谢谢!
    发表于 11-14 10:45

    大尺寸显示器多点控精度更上层楼

      要在更大的显示器上与更严苛的应用环境中,实现多点控并维持良好灵敏度与精准度,是电容式控设计的大挑战。透过提高抗电磁干扰能力与系统整合度,来提升
    发表于 01-13 09:45

    多点控技术被广泛应用于段码屏领域

    不断拓宽,很多场所比如:电视演播室的大屏幕、机场广告互动平台、购物中心的互动广告屏等都有大量用到大屏幕控屏。控大屏幕与传统的大屏幕显示系统有很大的不同,它操作简单,并且用户在使用的时候具有
    发表于 10-08 16:32

    Zytronic为小屏幕带来多点

    支持。  新的 Zytronic ZXY150 触摸控制器为22 英寸及更小的显示屏进行优化,能够在信息亭、自动柜员机和自动售货机等公用界面小屏幕上提供像平板电脑样的功能。ZXY150 为支持多点
    发表于 11-07 11:00

    Zytronic为小屏幕带来多点

      Zytronic 是先进、强健的投射电容式(PCTTM 和 MPCTTM)触摸传感器领域的市场领导者,近日发布了款新的多点触摸控制器,可为更小的自助式触摸屏应用程序提供更快的响应时间和手势识别
    发表于 11-07 10:54

    FPGA板和多点控屏之间的连接?

    大家好!!我是FPGA(套件)及其接口的新手。我将参与个项目。这个项目的部分是连接/驱动至少10+“对角线的多点控电容式触摸屏。任何人都可以帮助我指导我需要开始的东西。就像有几种
    发表于 07-13 16:53

    什么是多点控技术?多点控是怎么实现的?

    什么是多点控技术?多点控是怎么实现的?多点控技术的用途有哪些?
    发表于 06-17 07:47

    stm32的低功耗调试

    前言:物联网的大部分设备都是电池供电的,设备本身低功耗对延长设备使用至关重要,今天就实际调试总结stm32的低功耗调试。1、stm32在运行状态的功耗上图截图自stm32l15
    发表于 08-11 08:18

    一下GS的波形

    对于咱们电源工程师来讲,我们很多时候都在看波形,看输入波形,MOS开关波形,电流波形,输出二极管波形,芯片波形,MOS管的GS波形,我们拿开关GS波形为例来一下GS的波形。我们测试MOS管GS波形
    发表于 11-16 09:15

    一下OK1012A-C的网络性能

    ,各擅胜场。下面小编就各产品的网络性能为您简单介绍一下。先来一下OK1012A-C。OK1012A-C开发板采用的FET1012A-C核心板基于NXP公司ARM Cortex-A53
    发表于 12-20 08:32

    i.MX6UL基于嵌入式QT实现电容屏多点控的方法

    i.MX6UL基于嵌入式QT实现电容屏多点控 基于i.MX6UL平台,使用嵌入式QT实现电容屏的多点控,前提是开发板的电容触摸屏驱动已经支持多点
    发表于 12-21 06:19

    平衡小车代码的实现

    输出脉冲,单片机通过编码器模式检测脉冲值,并把该值代入速度环计算出个角度值送给角度环,角度环在输出PWM给电机,以此循环。其实就是上节的框图,再贴遍。程序程序部分为了减少篇幅,之前讲解的编码器模式及pwm等代码都不在讲解,原理都
    发表于 01-14 08:29

    一下涂鸦智能植物生长机的嵌入式部分

    篇文章我们分享了DIY个涂鸦智能植物生长机需要的硬件以及结构件的物料,接下来我们这篇文章我们主要来一下嵌入式部分。1、产品创建进入智能涂鸦IoT平台,点击创建产品。选择小家电-
    发表于 02-17 06:56

    请问一下RK3288 ubuntu系统支持多点控吗

    请问一下RK3288 ubuntu支持多点控吗?求大神解答一下
    发表于 09-21 17:33

    简单实用的多点控制开关

    简单实用的多点控制开关
    发表于 12-16 01:16 911次阅读
    <b class='flag-5'>简单</b>实用的<b class='flag-5'>多点</b>控制开关