Linux 多点电容触摸驱动框架

linux 下多点电容触摸驱动的编写框架和步骤。需要用到以下知识点和框架。

①、多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。
②、linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。
③、多点电容触摸属于 input 子系统,因此还要用到 input 子系统框架。
④、在中断处理程序中按照 linux 的 MT 协议上报坐标信息。

多点电容触摸驱动编写框架以及步骤如下:

1、I2C 驱动框架

驱动总体采用 I2C 框架,参考框架代码如下所示:

/* 无设备树匹配表 */
static const struct i2c_device_id xxx_ts_id[] = {
    {"xxx",0,},
    {/* sentinel */}
};

/* 设备树匹配表 */
static const struct of_device_id xxx_of_match[] = {
    {.compatible = "xxx",},
    {/* sentinel */}
};

/* i2c 驱动结构体 */
static struct i2c_driver ft5x06_ts_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "edt_ft5x06",
        .of_match_table = of_match_ptr(xxx_of_match),
    },
    .id_table = xxx_ts_id,
    .probe = xxx_ts_probe,
    .remove = xxx_ts_remove,
};

/*
 * @description : 驱动入口函数
 * @param : 无
 * @return : 无
 */
static int __init xxx_init(void)
{
    int ret = 0;

    ret = i2c_add_driver(&xxx_ts_driver);

    return ret;
}

/*
 * @description : 驱动出口函数
 * @param : 无
 * @return : 无
 */
static void __exit xxx_exit(void)
{
    i2c_del_driver(&ft5x06_ts_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");

当设备树中触摸 IC的设备节点和驱动匹配以后,xxx_ts_probe 函数就会执行,我们可以在此函数中初始化触摸 IC,中断和 input 子系统等。

2、在probe函数中初始化触摸 IC、中断和 input 子系统

static int xxx_ts_probe(struct i2c_client *client, const struct
                        i2c_device_id *id)
{
    struct input_dev *input;

    /* 1、初始化 I2C */
    ......

    /* 2,申请中断, */
    devm_request_threaded_irq(&client->dev, client->irq, NULL,
                                  xxx_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                  client->name, &xxx);
    ......

    /* 3,input 设备申请与初始化 */
    input = devm_input_allocate_device(&client->dev);

    input->name = client->name;
    input->id.bustype = BUS_I2C;
    input->dev.parent = &client->dev;
    ......

        /* 4,初始化 input 和 MT */
        __set_bit(EV_ABS, input->evbit);
    __set_bit(BTN_TOUCH, input->keybit);

    input_set_abs_params(input, ABS_X, 0, width, 0, 0);
    input_set_abs_params(input, ABS_Y, 0, height, 0, 0);
    input_set_abs_params(input, ABS_MT_POSITION_X, 0, width, 0, 0);
    input_set_abs_params(input, ABS_MT_POSITION_Y, 0, height, 0, 0);
    input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
    ......

        /* 5,注册 input_dev */
        input_register_device(input);
    ......
}

第一步:初始化I2C。首先肯定是初始化触摸芯片,包括芯片的相关 IO,比如复位、中断等 IO 引脚,然后就是芯片本身的初始化,也就是配置触摸芯片的相关寄存器。

第二步:申请中断。因为一般触摸芯片都是通过中断来向系统上报触摸点坐标信息的,因此我们需要初始化中断。采用了 devm_request_threaded_irqy申请中断的原因如下:
①、用于申请中断,作用和 request_irq 函数类似。
②、可将中断线程化,保证高优先级的任务能先被处理。
③、“devm_”函数最大的作用就是:使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理。

第三步:input 设备申请与初始化,因为多点电容触摸属于 input 子系统。同样使用devm_input_allocate_device 函数来申请 input_dev。申请到 input_dev 以后还需要对其进行初始化操作。

第四步:初始化input和MT。设置 input_dev 需要上报的事件为 EV_ABS 和 BTN_TOUCH,因为多点电容屏的触摸坐标为绝对值,因此需要上报 EV_ABS 事件。触摸屏有按下和抬起之分,因此需要上报 BTN_TOUCH 按键。

调用 input_set_abs_params 函数设置 EV_ABS 事件需要上报 ABS_X、ABS_Y、ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。单点触摸需要上报 ABS_X 和 ABS_Y,对于多点触摸需要上报 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y。

调用 input_mt_init_slots 函数初始化多点电容触摸的 slots。

第五步:注册 input_dev,调用 input_register_device 函数注册前面申请到的 input_dev。

3、上报坐标信息

最后就是在中断服务程序中上报读取到的坐标信息,以 Type B类型为例讲解一下上报过程,参考驱动框架如下所示:

static irqreturn_t xxx_handler(int irq, void *dev_id)
{

    int num;        /* 触摸点数量 */
    int x[n], y[n]; /* 保存坐标值 */

    /* 1、从触摸芯片获取各个触摸点坐标值 */
    ......

        /* 2、上报每一个触摸点坐标 */
        for (i = 0; i < num; i++)
    {
        input_mt_slot(input, id);
        input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
        input_report_abs(input, ABS_MT_POSITION_X, x[i]);
        input_report_abs(input, ABS_MT_POSITION_Y, y[i]);
    }
    ......

        input_sync(input);
    ......

        return IRQ_HANDLED;
}

第一步:从触摸芯片获取各个触摸点坐标值,假设触摸点数量保存到 num 变量,触摸点坐标存放到 x,y 数组里面。

第二步:上报每一个触摸点坐标,循环上报每一个触摸点坐标,一定要按照 Type B 类型的时序进行。
每一轮触摸点坐标上报完毕以后就调用一次 input_sync 函数发送一个SYN_REPORT 事件。

你可能感兴趣的:(arm,linux,vscode,c++,功能测试)