• 触摸屏驱动


    驱动框架

    • 多点电容触摸芯片的接口,一般都为 I2C 接口,因此驱动主框架肯定是 I2C。

    设备树中触摸 IC的设备节点和驱动匹配以后 。进入probe入口函数。在此函数中初始化触摸 IC,中断和 input 子系统等

    • linux 里面一般都是通过中断来上报触摸点坐标信息,因此需要用到中断框架。

    在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序

    • 触摸屏的坐标信息、屏幕按下和抬起信息都属于 linux 的 input 子系统,因此向 linux 内
      核上报触摸屏坐标信息就得使用 input 子系统 。
    • 在中断处理程序中按照 linux 的 MT 协议上报坐标信息。

    input 子系统下的多点电容触摸协议

    触摸屏坐标信息时,需要按照MT协议来上报

    MT 协议被分为两种类型, TypeA 和 TypeB,这两种类型的区别如下:
    Type A:适用于触摸点不能被区分或者追踪,此类型的设备上报原始数据(此类型在实际使用中非常少! 。
    Type B:适用于有硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot 更新某一个触摸点的信息, FT5426 就属于此类型,一般的多点电容触摸屏 IC 都有此能力

    触摸点的信息通过一系列的 ABS_MT 事件(有的资料也叫消息)上报给 linux 内核,只有ABS_MT 事件是用于多点触摸的, 常用的是一下几类

    触摸点数据上报时序

    • 第 1 行,上报 ABS_MT_SLOT 事件,也就是触摸点对应的 SLOT。每次上报一个触摸点坐
      标之前要先使用input_mt_slot函数上报当前触摸点SLOT,触摸点的SLOT其实就是触摸点ID,需要由触摸 IC 提供。
    • 第 2 行,根据 Type B 的要求,每个 SLOT 必须关联一个 ABS_MT_TRACKING_ID,通过修改 SLOT 关联的 ABS_MT_TRACKING_ID 来完成对触摸点的添加、替换或删除。具体用到的函数就是 input_mt_report_slot_state,如果是添加一个新的触摸点,那么此函数的第三个参数active 要设置为 true, linux 内核会自动分配一个ABS_MT_TRACKING_ID 值,不需要用户去指定具体的 ABS_MT_TRACKING_ID 值。
    • 第 3 行,上报触摸点 0 的 X 轴坐标,使用函数 input_report_abs 来完成。
    • 第 4 行,上报触摸点 0 的 Y 轴坐标,使用函数 input_report_abs 来完成

    驱动编写

    I2C 驱动框架

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

    1. static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
    2. {
    3. }
    4. /*
    5. * @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
    6. * @param - client : i2c设备
    7. * @return : 0,成功;其他负值,失败
    8. */
    9. static int ft5x06_ts_remove(struct i2c_client *client)
    10. {
    11. /* 释放input_dev */
    12. input_unregister_device(ft5x06.input);
    13. return 0;
    14. }
    15. /*
    16. * 传统驱动匹配表
    17. */
    18. static const struct i2c_device_id ft5x06_ts_id[] = {
    19. { "edt-ft5206", 0, },
    20. { "edt-ft5426", 0, },
    21. { /* sentinel */ }
    22. };
    23. /*
    24. * 设备树匹配表
    25. */
    26. static const struct of_device_id ft5x06_of_match[] = {
    27. { .compatible = "edt,edt-ft5206", },
    28. { .compatible = "edt,edt-ft5426", },
    29. { /* sentinel */ }
    30. };
    31. /* i2c驱动结构体 */
    32. static struct i2c_driver ft5x06_ts_driver = {
    33. .driver = {
    34. .owner = THIS_MODULE,
    35. .name = "edt_ft5x06",
    36. .of_match_table = of_match_ptr(ft5x06_of_match),
    37. },
    38. .id_table = ft5x06_ts_id,
    39. .probe = ft5x06_ts_probe,
    40. .remove = ft5x06_ts_remove,
    41. };
    42. /*
    43. * @description : 驱动入口函数
    44. * @param : 无
    45. * @return : 无
    46. */
    47. static int __init ft5x06_init(void)
    48. {
    49. int ret = 0;
    50. ret = i2c_add_driver(&ft5x06_ts_driver);
    51. return ret;
    52. }
    53. /*
    54. * @description : 驱动出口函数
    55. * @param : 无
    56. * @return : 无
    57. */
    58. static void __exit ft5x06_exit(void)
    59. {
    60. i2c_del_driver(&ft5x06_ts_driver);
    61. }
    62. module_init(ft5x06_init);
    63. module_exit(ft5x06_exit);
    64. MODULE_LICENSE("GPL");
    65. MODULE_AUTHOR("zuozhongkai");

    初始化触摸 IC、中断和 input 子系统

    1. struct ft5x06_dev {
    2. struct device_node *nd; /* 设备节点 */
    3. int irq_pin,reset_pin; /* 中断和复位IO */
    4. int irqnum; /* 中断号 */
    5. void *private_data; /* 私有数据 */
    6. struct input_dev *input; /* input结构体 */
    7. struct i2c_client *client; /* I2C客户端 */
    8. };
    9. static struct ft5x06_dev ft5x06;
    10. /*
    11. * @description : 根据i2c协议从FT5X06读取多个寄存器数据
    12. * @param - dev: ft5x06设备
    13. * @param - reg: 要读取的寄存器首地址
    14. * @param - val: 读取到的数据
    15. * @param - len: 要读取的数据长度
    16. * @return : 操作结果
    17. */
    18. static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len)
    19. {
    20. int ret;
    21. struct i2c_msg msg[2];
    22. struct i2c_client *client = (struct i2c_client *)dev->client;
    23. /* msg[0],第一条写消息,发送要读取的寄存器首地址 */
    24. msg[0].addr = client->addr; /* ft5x06地址 */
    25. msg[0].flags = 0; /* 标记为发送数据 */
    26. msg[0].buf = ® /* 读取的首地址 */
    27. msg[0].len = 1; /* reg长度*/
    28. /* msg[1],第二条读消息,读取寄存器数据 */
    29. msg[1].addr = client->addr; /* ft5x06地址 */
    30. msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
    31. msg[1].buf = val; /* 读取数据缓冲区 */
    32. msg[1].len = len; /* 要读取的数据长度*/
    33. ret = i2c_transfer(client->adapter, msg, 2);
    34. if(ret == 2) {
    35. ret = 0;
    36. } else {
    37. ret = -EREMOTEIO;
    38. }
    39. return ret;
    40. }
    41. /*
    42. * @description : i2c驱动的probe函数,当驱动与
    43. * 设备匹配以后此函数就会执行
    44. * @param - client : i2c设备
    45. * @param - id : i2c设备ID
    46. * @return : 0,成功;其他负值,失败
    47. */
    48. static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
    49. {
    50. int ret = 0;
    51. ft5x06.client = client;
    52. /* 1,获取设备树中的中断和复位引脚 */
    53. ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
    54. ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
    55. /* 2,复位FT5x06 */
    56. ret = ft5x06_ts_reset(client, &ft5x06);
    57. if(ret < 0) {
    58. goto fail;
    59. }
    60. /* 3,初始化中断 */
    61. ret = ft5x06_ts_irq(client, &ft5x06);
    62. if(ret < 0) {
    63. goto fail;
    64. }
    65. /* 4,初始化FT5X06 */
    66. ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 进入正常模式 */
    67. ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426中断模式 */
    68. /* 5,input设备注册
    69. 使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理
    70. */
    71. ft5x06.input = devm_input_allocate_device(&client->dev);
    72. if (!ft5x06.input) {
    73. ret = -ENOMEM;
    74. goto fail;
    75. }
    76. /* 6,初始化input设备 */
    77. ft5x06.input->name = client->name;
    78. ft5x06.input->id.bustype = BUS_I2C;
    79. ft5x06.input->dev.parent = &client->dev;
    80. /* 7,设置事件类型 */
    81. __set_bit(EV_KEY, ft5x06.input->evbit);
    82. __set_bit(EV_ABS, ft5x06.input->evbit);
    83. __set_bit(BTN_TOUCH, ft5x06.input->keybit);
    84. /* 7,设置EV_ABS事件上报信息 */
    85. input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
    86. input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
    87. input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
    88. input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0);
    89. /*8, 初始化多点电容触摸的 slots (触摸点)*/
    90. ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
    91. if (ret) {
    92. goto fail;
    93. }
    94. /*8, 注册input设备*/
    95. ret = input_register_device(ft5x06.input);
    96. if (ret)
    97. goto fail;
    98. return 0;
    99. fail:
    100. return ret;
    101. }

    上报坐标信息

    在中断服务程序中上报读取到的坐标信息,根据所使用的多点电容触摸设备类型选择使用 Type A 还是 Type B 时序。

    1. /*
    2. * @description : FT5X06中断服务函数
    3. * @param - irq : 中断号
    4. * @param - dev_id : 设备结构。
    5. * @return : 中断执行结果
    6. */
    7. static irqreturn_t ft5x06_handler(int irq, void *dev_id)
    8. {
    9. struct ft5x06_dev *multidata = dev_id;
    10. u8 rdbuf[29];
    11. int i, type, x, y, id;
    12. int offset, tplen;
    13. int ret;
    14. bool down;
    15. offset = 1; /* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */
    16. tplen = 6; /* 一个触摸点有6个寄存器来保存触摸值 */
    17. memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */
    18. /* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
    19. ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
    20. if (ret) {
    21. goto fail;
    22. }
    23. /* 上报每一个触摸点坐标 */
    24. for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
    25. u8 *buf = &rdbuf[i * tplen + offset];
    26. /* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
    27. * bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
    28. * bit5:4 保留
    29. * bit3:0 X轴触摸点的11~8位。
    30. */
    31. type = buf[0] >> 6; /* 获取触摸类型 */
    32. if (type == TOUCH_EVENT_RESERVED)
    33. continue;
    34. /* 我们所使用的触摸屏和FT5X06是反过来的 */
    35. x = ((buf[2] << 8) | buf[3]) & 0x0fff;
    36. y = ((buf[0] << 8) | buf[1]) & 0x0fff;
    37. /* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
    38. * bit7:4 Touch ID 触摸ID,表示是哪个触摸点
    39. * bit3:0 Y轴触摸点的11~8位。
    40. */
    41. id = (buf[2] >> 4) & 0x0f;
    42. down = type != TOUCH_EVENT_UP;
    43. //产生 ABS_MT_SLOT 事件,告诉内核当前上报的是哪个触摸点的坐标数据
    44. input_mt_slot(multidata->input, id);
    45. /* 给 slot 关 联 一 个 ABS_MT_TRACKING_ID ,
    46. ABS_MT_TOOL_TYPE 事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )
    47. */
    48. input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);
    49. if (!down)
    50. continue;
    51. //上报触摸点坐标信息
    52. input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
    53. input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
    54. }
    55. input_mt_report_pointer_emulation(multidata->input, true);
    56. //上报一个同步事件,告诉内核上报结束
    57. input_sync(multidata->input);
    58. fail:
    59. return IRQ_HANDLED;
    60. }


     

  • 相关阅读:
    (九)MyBatis查询语句的返回值类型
    TypeScript学习01--安装和基本数据类型
    python中,reshape 用法
    MySQL日志管理、备份与恢复
    python astra相机驱动问题
    数理逻辑:1、预备知识
    通过Dockerfile build mysql镜像 初始化mysql数据库
    和至少为K的最短子数组
    防火墙是什么?F5保护Web应用的思路解读
    解密Prompt系列15. LLM Agent之数据库应用设计:DIN & C3 & SQL-Palm & BIRD
  • 原文地址:https://blog.csdn.net/qq_52353238/article/details/133561989