首页 > 其他分享 >多点触摸屏驱动

多点触摸屏驱动

时间:2024-03-31 23:46:16浏览次数:30  
标签:struct dev 触摸屏 client ret 多点 input 驱动 ft5x06

以下代码为整点原子代码:   多点触摸相关API函数:

int input_mt_init_slots( struct input_dev *dev,         
            unsigned int  num_slots,
            unsigned int  flags)            // 初始化slots
 
// 函数参数和返回值含义如下:
dev: MT 设备对应的 input_dev,因为 MT 设备隶属于 input_dev。
num_slots:设备要使用的 SLOT 数量,也就是触摸点的数量。
flags:其他一些 flags 信息,可设置的 flags 如下所示:


void input_mt_slot(struct input_dev *dev, 
            int slot)                // 告诉核心层当前上报的是哪一个触摸点的坐标
 
// 函数参数和返回值含义如下:
dev: MT 设备对应的 input_dev。
slot:当前发送的是哪个 slot 的坐标信息,也就是哪个触摸点。
返回值:无。    


void input_mt_report_slot_state( struct input_dev *dev,
            unsigned int tool_type, 
            bool active)            // 关联ABS_MT_TRACKING_ID

// 函数参数和返回值含义如下:
dev: MT 设备对应的 input_dev。
tool_type:触摸类型,可以选择 MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)或
MT_TOOL_PALM(手掌),对于多点电容触摸屏来说一般都是手指。
active:true,连续触摸,input 子系统内核会自动分配一个 ABS_MT_TRACKING_ID 给 slot。
false,触摸点抬起,表示某个触摸点无效了,input 子系统内核会分配一个-1 给 slot,表示触摸点溢出。


void input_mt_report_pointer_emulation(struct input_dev *dev, 
            bool use_count)            // 上报上层触摸点多了

// 函数参数和返回值含义如下:
dev: MT 设备对应的 input_dev。
use_count:true,有效的触摸点数量;false,追踪到的触摸点数量多于当前上报的数量。
返回值:无。

 

驱动文件ft5x06.c:
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/input/edt-ft5x06.h>
#include <linux/i2c.h>

#define MAX_SUPPORT_POINTS        5            /* 5点触摸     */
#define TOUCH_EVENT_DOWN        0x00        /* 按下     */
#define TOUCH_EVENT_UP            0x01        /* 抬起     */
#define TOUCH_EVENT_ON            0x02        /* 接触     */
#define TOUCH_EVENT_RESERVED    0x03        /* 保留     */

/* FT5X06寄存器相关宏定义 */
#define FT5X06_TD_STATUS_REG    0X02        /*    状态寄存器地址         */
#define FT5x06_DEVICE_MODE_REG    0X00         /* 模式寄存器             */
#define FT5426_IDG_MODE_REG        0XA4        /* 中断模式                */
#define FT5X06_READLEN            29            /* 要读取的寄存器个数     */

struct ft5x06_dev {
    struct device_node    *nd;                 /* 设备节点         */
    int irq_pin,reset_pin;                    /* 中断和复位IO        */
    int irqnum;                                /* 中断号            */
    void *private_data;                        /* 私有数据         */
    struct input_dev *input;                /* input结构体         */
    struct i2c_client *client;                /* I2C客户端         */
};

static struct ft5x06_dev ft5x06;

/*
 * @description     : 复位FT5X06
 * @param - client     : 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_ts_reset(struct i2c_client *client, struct ft5x06_dev *dev)
{
    int ret = 0;

    if (gpio_is_valid(dev->reset_pin)) {          /* 检查IO是否有效 */
        /* 申请复位IO,并且默认输出低电平 */
        ret = devm_gpio_request_one(&client->dev,    
                    dev->reset_pin, GPIOF_OUT_INIT_LOW,
                    "edt-ft5x06 reset");
        if (ret) {
            return ret;
        }

        msleep(5);
        gpio_set_value(dev->reset_pin, 1);    /* 输出高电平,停止复位 */
        msleep(300);
    }

    return 0;
}

/*
 * @description    : 从FT5X06读取多个寄存器数据
 * @param - dev:  ft5x06设备
 * @param - reg:  要读取的寄存器首地址
 * @param - val:  读取到的数据
 * @param - len:  要读取的数据长度
 * @return         : 操作结果
 */
static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len)
{
    int ret;
    struct i2c_msg msg[2];
    struct i2c_client *client = (struct i2c_client *)dev->client;

    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = client->addr;            /* ft5x06地址 */
    msg[0].flags = 0;                    /* 标记为发送数据 */
    msg[0].buf = &reg;                    /* 读取的首地址 */
    msg[0].len = 1;                        /* reg长度*/

    /* msg[1]读取数据 */
    msg[1].addr = client->addr;            /* ft5x06地址 */
    msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/
    msg[1].buf = val;                    /* 读取数据缓冲区 */
    msg[1].len = len;                    /* 要读取的数据长度*/

    ret = i2c_transfer(client->adapter, msg, 2);
    if(ret == 2) {
        ret = 0;
    } else {
        ret = -EREMOTEIO;
    }
    return ret;
}

/*
 * @description    : 向ft5x06多个寄存器写入数据
 * @param - dev:  ft5x06设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return       :   操作结果
 */
static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg, u8 *buf, u8 len)
{
    u8 b[256];
    struct i2c_msg msg;
    struct i2c_client *client = (struct i2c_client *)dev->client;
    
    b[0] = reg;                    /* 寄存器首地址 */
    memcpy(&b[1],buf,len);        /* 将要写入的数据拷贝到数组b里面 */
        
    msg.addr = client->addr;    /* ft5x06地址 */
    msg.flags = 0;                /* 标记为写数据 */

    msg.buf = b;                /* 要写入的数据缓冲区 */
    msg.len = len + 1;            /* 要写入的数据长度 */

    return i2c_transfer(client->adapter, &msg, 1);
}

/*
 * @description    : 向ft5x06指定寄存器写入指定的值,写一个寄存器
 * @param - dev:  ft5x06设备
 * @param - reg:  要写的寄存器
 * @param - data: 要写入的值
 * @return   :    无
 */
static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data)
{
    u8 buf = 0;
    buf = data;
    ft5x06_write_regs(dev, reg, &buf, 1);
}

/*
 * @description     : FT5X06中断服务函数
 * @param - irq     : 中断号 
 * @param - dev_id    : 设备结构。
 * @return             : 中断执行结果
 */
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
    struct ft5x06_dev *multidata = dev_id;

    u8 rdbuf[29];
    int i, type, x, y, id;
    int offset, tplen;
    int ret;
    bool down;

    offset = 1;     /* 偏移1,也就是0X02+1=0x03,从0X03开始是触摸值 */
    tplen = 6;        /* 一个触摸点有6个寄存器来保存触摸值 */

    memset(rdbuf, 0, sizeof(rdbuf));        /* 清除 */

    /* 读取FT5X06触摸点坐标从0X02寄存器开始,连续读取29个寄存器 */
    ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
    if (ret) {
        goto fail;
    }

    /* 上报每一个触摸点坐标 */
    for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
        u8 *buf = &rdbuf[i * tplen + offset];

        /* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
         * bit7:6  Event flag  0:按下 1:释放 2:接触 3:没有事件
         * bit5:4  保留
         * bit3:0  X轴触摸点的11~8位。
         */
        type = buf[0] >> 6;     /* 获取触摸类型 */
        if (type == TOUCH_EVENT_RESERVED)
            continue;
 
        /* 我们所使用的触摸屏和FT5X06是反过来的 */
        x = ((buf[2] << 8) | buf[3]) & 0x0fff;
        y = ((buf[0] << 8) | buf[1]) & 0x0fff;
        
        /* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
         * bit7:4  Touch ID  触摸ID,表示是哪个触摸点
         * bit3:0  Y轴触摸点的11~8位。
         */
        id = (buf[2] >> 4) & 0x0f;
        down = type != TOUCH_EVENT_UP;

        input_mt_slot(multidata->input, id);
        input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);

        if (!down)
            continue;

        input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
        input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
    }

    input_mt_report_pointer_emulation(multidata->input, true);
    input_sync(multidata->input);

fail:
    return IRQ_HANDLED;

}

/*
 * @description     : FT5x06中断初始化
 * @param - client     : 要操作的i2c
 * @param - multidev: 自定义的multitouch设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_ts_irq(struct i2c_client *client, struct ft5x06_dev *dev)
{
    int ret = 0;

    /* 1,申请中断GPIO */
    if (gpio_is_valid(dev->irq_pin)) {
        ret = devm_gpio_request_one(&client->dev, dev->irq_pin,
                    GPIOF_IN, "edt-ft5x06 irq");
        if (ret) {
            dev_err(&client->dev,
                "Failed to request GPIO %d, error %d\n",
                dev->irq_pin, ret);
            return ret;
        }
    }

    /* 2,申请中断,client->irq就是IO中断, */
    ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
                    ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                    client->name, &ft5x06);
    if (ret) {
        dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
        return ret;
    }

    return 0;
}

 /*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;

    ft5x06.client = client;

    /* 1,获取设备树中的中断和复位引脚 */
    ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
    ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);

    /* 2,复位FT5x06 */
    ret = ft5x06_ts_reset(client, &ft5x06);
    if(ret < 0) {
        goto fail;
    }

    /* 3,初始化中断 */
    ret = ft5x06_ts_irq(client, &ft5x06);
    if(ret < 0) {
        goto fail;
    }

    /* 4,初始化FT5X06 */
    ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0);     /* 进入正常模式     */
    ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1);         /* FT5426中断模式    */

    /* 5,input设备注册 */
    ft5x06.input = devm_input_allocate_device(&client->dev);
    if (!ft5x06.input) {
        ret = -ENOMEM;
        goto fail;
    }
    ft5x06.input->name = client->name;
    ft5x06.input->id.bustype = BUS_I2C;
    ft5x06.input->dev.parent = &client->dev;

    __set_bit(EV_KEY, ft5x06.input->evbit);
    __set_bit(EV_ABS, ft5x06.input->evbit);
    __set_bit(BTN_TOUCH, ft5x06.input->keybit);

    input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
    input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
    input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
    input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0);         
    ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
    if (ret) {
        goto fail;
    }

    ret = input_register_device(ft5x06.input);
    if (ret)
        goto fail;

    return 0;

fail:
    return ret;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client     : i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int ft5x06_ts_remove(struct i2c_client *client)
{    
    /* 释放input_dev */
    input_unregister_device(ft5x06.input);
    return 0;
}


/*
 *  传统驱动匹配表
 */ 
static const struct i2c_device_id ft5x06_ts_id[] = {
    { "edt-ft5206", 0, },
    { "edt-ft5426", 0, },
    { /* sentinel */ }
};

/*
 * 设备树匹配表 
 */
static const struct of_device_id ft5x06_of_match[] = {
    { .compatible = "edt,edt-ft5206", },
    { .compatible = "edt,edt-ft5426", },
    { /* sentinel */ }
};

/* i2c驱动结构体 */    
static struct i2c_driver ft5x06_ts_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "edt_ft5x06",
        .of_match_table = of_match_ptr(ft5x06_of_match),
    },
    .id_table = ft5x06_ts_id,
    .probe    = ft5x06_ts_probe,
    .remove   = ft5x06_ts_remove,
};

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

    ret = i2c_add_driver(&ft5x06_ts_driver);

    return ret;
}

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

module_init(ft5x06_init);
module_exit(ft5x06_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lethe1203");

 

 

 

 

 

 

标签:struct,dev,触摸屏,client,ret,多点,input,驱动,ft5x06
From: https://www.cnblogs.com/lethe1203/p/18107494

相关文章

  • 领域驱动设计战略设计
    ​领域驱动设计(Domain-DrivenDesign,简称DDD)是一种软件设计方法,它强调的是基于领域模型来进行软件设计,以确保软件结构能够贴切地反映业务领域的复杂性和细微差别。战略设计关注的是高层次的设计决策,特别是关于如何将大型系统划分为较小、管理得来的部分,每一部分都聚焦于特定的业务......
  • 领域驱动设计战略设计
    领域驱动设计(Domain-DrivenDesign,简称DDD)是一种软件设计方法,它强调的是基于领域模型来进行软件设计,以确保软件结构能够贴切地反映业务领域的复杂性和细微差别。战略设计关注的是高层次的设计决策,特别是关于如何将大型系统划分为较小、管理得来的部分,每一部分都聚焦于特定的业......
  • 提问的力量:驱动问题解决、核心发现与创新启示
    目录问题解决的驱动力:提问的力量提问的力量:催生解决方案的火花提问的力量:启发新思想的源泉核心发现的推动力:提问的力量提问的力量:推动科学探索与发现提问的力量:揭示历史真相创新启示:提问的力量提问的力量:激发创新思维提问的力量:推动社会进步探索篇:提问与问题解决—......
  • 领域驱动设计核心概念
    ​ 领域驱动设计(Domain-DrivenDesign,DDD)是一种软件设计哲学,旨在通过深入理解业务领域的复杂性来指导软件项目的设计和开发,以确保软件能够准确地解决业务问题。DDD提倡通过深入业务领域的知识合作,以及业务领域专家的紧密合作,来寻找和实现软件解决方案。这种方法论主要关注的......
  • 毕业设计:深度学习驱动的电影推荐系统
    目录前言项目背景设计思路更多帮助前言  ......
  • 大电流LED驱动/数码管驱动芯片VK16K33A SOP28,适用于电饭煲,数字电表等
    产品品牌:永嘉微电/VINKA产品型号:VK16K33A封装形式:SOP28概述:VK16K33A是一种带按键扫描接口的数码管或点阵LED驱动控制专用芯片,内部集成有数据锁存器、键盘扫描、LED驱动模块等电路。数据通过I2C通讯接口与MCU通信。SEG脚接LED阳极,GRID脚接LED阴极,可支持16SEGx8GRID的点阵LED......
  • Linux内核驱动编程的一道陷阱题(转载)
    本篇转载于:https://blog.csdn.net/yhb1047818384/article/details/84073838 原文如下:------看过一道linux内核驱动编程的题目,我觉得有点价值。题目很简单,凭记忆整理了下,代码如下:#include<linux/init.h>#include<linux/module.h>#include<linux/delay.h>#include<l......
  • openEuler或基于openEuler的麒麟服务器系统安装nvidia驱动
    一、openEuler安装步骤尝试安装了openEuler20.03和22.03两个版本,在摸索的过程中总结了一下步骤,以及相关问题的解决方案,进行简单记录。便于后续使用。1、openEuler20.03安装步骤,网络配置以及可视化操作界面安装。下载openEuler系统(官网):https://www.openeuler.org/zh/安装步骤(官网......
  • cnc数据采集 ,机床数据采集,设备联网,多品牌多线程采集驱动,融合马扎克、西门子、海德汉、
    +cnccaiji采集驱动可以对不同品牌的机床信息进行管理(ip、端口、采集点位、车间、工厂、设备名称、编号)等,将采集到的数据通过mqtt或者http推送到指定地址,实现和业务系统的完全解耦。对于不了解cnc采集相关业务的公司来讲非常的友好。驱动支持系统 马扎克马扎克MAZAKCNC数据采......
  • VKL144A/B TSSOP48/QFP48L-点阵式液晶驱动芯片/低电流LCD驱动,36×4段技术支持
    产品品牌:永嘉微电/VINKA产品型号:VKL144A/B封装形式:TSSOP48/QFN48L概述:VKL144A/BTSSOP48/QFN48L是一个点阵式存储映射的LCD驱动器,可支持最大144点(36SEGx4COM)的LCD屏。单片机可通过I2C接口配置显示参数和读写显示数据,可配置4种功耗模式,也可通过关显示和关振荡器进入省电模式......