linux 下多点电容触摸驱动的编写框架和步骤。需要用到以下知识点和框架。
①、多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。
②、linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。
③、多点电容触摸属于 input 子系统,因此还要用到 input 子系统框架。
④、在中断处理程序中按照 linux 的 MT 协议上报坐标信息。
多点电容触摸驱动编写框架以及步骤如下:
驱动总体采用 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 子系统等。
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。
最后就是在中断服务程序中上报读取到的坐标信息,以 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 事件。