RK3566(泰山派):3.1寸屏幕D310T9362V1SPEC触摸驱动(竖屏)

RK3566(泰山派):3.1寸屏幕D310T9362V1SPEC触摸驱动(竖屏)


文章目录

  • RK3566(泰山派):3.1寸屏幕D310T9362V1SPEC触摸驱动(竖屏)
  • 电路
  • 配置i2c1设备树
  • 创建驱动
  • 编写Makefile
  • my_touch.c驱动
    • I2C驱动框架
  • 驱动中的结构体
    • probe
      • 竖屏probe函数
      • 横屏probe函数
    • 中断
    • 中断线程服务函数
      • 竖屏中断线程服务函数
      • 横屏中断线程服务函数
    • TD_STATUS
    • TOUCHn_X寄存器
    • TOUCHn_Y寄存器
    • 上报数据
  • 触摸驱动完整代码
  • 编译生效
  • 效果


电路

在这里插入图片描述

配置i2c1设备树

从原理图中可知GP7101和触摸共同挂在道I2C下,所以引用&i2c1并添加一个我们自己定义的myts@38触摸节点。

&i2c1 {
    status = "okay";             // 表示这个i2c1设备是可用的
    clock-frequency = <400000>;  // 设置i2c1的时钟频率为400kHz
    myts@38 {                    // 定义一个i2c设备,设备地址为0x38,设备名称为myts
        compatible = "my,touch"; // 表示这个设备是触摸屏设备,驱动名称为my,touch
        reg = <0x38>;            // i2c设备地址
        tp-size = <89>;          // 触摸屏的大小
        max-x = <480>;           // 触摸屏支持的最大X坐标值
        max-y = <800>;           // 触摸屏支持的最大Y坐标值
        touch-gpio = <&gpio1 RK_PA0 IRQ_TYPE_LEVEL_LOW>; // 触摸屏的触摸中断引脚,连接到gpio1的第0个引脚,触发方式为低电平触发
        reset-gpio = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>;   // 触摸屏的复位引脚,连接到gpio1的第1个引脚,有效电平为高电平
    };
   /****省略****/
};

在这里插入图片描述

创建驱动

一般触摸都放在
/kernel/drivers/input/touchscreen目录下,所以我们在此路径下创建一个my_touch目录用来存放Makefile和my_touch.c文件。

/home/paranoid/tspi/android/kernel/drivers/input/touchscreen
cd kernel/drivers/input/touchscreen
mkdir my_touch
cd my_touch/
touch Makefile
touch my_touch.c

在这里插入图片描述

编写Makefile

touchscreen/Makefile中把my_touch.c编译到内核中,当然也可以选择obj-m编译成模块。

obj-y   += my_touch.o

在这里插入图片描述

要想
my_touch下的Makefile生效还需要在上一层目录的Makefile中添加my_touch目录,所以我们需要在touchscreen目录下Makefile中加入:注释掉官方SDK的触摸驱动

在这里插入图片描述
添加自己写的驱动
在这里插入图片描述

my_touch.c驱动

I2C驱动框架

一个框架结构。

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    return 0;
}

static int my_touch_ts_remove(struct i2c_client *client)
{
    MY_DEBUG("locat");
    return 0;
}

static const struct of_device_id my_touch_of_match[] = {
    { .compatible = "my,touch", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_touch_of_match);

static struct i2c_driver my_touch_ts_driver = {
    .probe      = my_touch_ts_probe,
    .remove     = my_touch_ts_remove,
    .driver = {
        .name     = "my-touch",
     .of_match_table = of_match_ptr(my_touch_of_match),
    },
};

static int __init my_ts_init(void)
{
    MY_DEBUG("locat");
    return i2c_add_driver(&my_touch_ts_driver);
}

static void __exit my_ts_exit(void)
{
    MY_DEBUG("locat");
    i2c_del_driver(&my_touch_ts_driver);
}

module_init(my_ts_init);
module_exit(my_ts_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("KuLiT");

驱动中的结构体

因为驱动过程中会有很多参数,我们不可能创建全局变量去保存他们,在linux驱动中一般都是通过创建一个结构体来保存驱动相关的参数,所以这里创建一个my_touch_dev结构体。

// 定义一个表示触摸设备的结构体
struct my_touch_dev {
    struct i2c_client *client; // 指向与触摸设备通信的 I2C 客户端结构体的指针
    struct input_dev *input_dev; // 指向与输入设备关联的 input_dev 结构体的指针,用于处理输入事件
    int rst_pin; // 触摸设备的复位引脚编号
    int irq_pin; // 触摸设备的中断引脚编号
    u32 abs_x_max; // 触摸设备在 X 轴上的最大绝对值
    u32 abs_y_max; // 触摸设备在 Y 轴上的最大绝对值
    int irq; // 触摸设备的中断号
};

probe

当驱动中of_match_table = of_match_ptr(my_touch_of_match)和设备树匹配成功以后会执行探针函数,探针函数中我们会去初始化驱动。

竖屏probe函数

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    int ret; // 定义一个返回值变量
    struct my_touch_dev *ts; // 定义一个结构体指针,用来指向my_touch_dev结构体
    struct device_node *np = client->dev.of_node; // 获取设备节点
    // 打印调试信息
    MY_DEBUG("locat"); // 调用MY_DEBUG函数打印调试信息,此处打印"locat"

    ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); // 使用devm_kzalloc分配内存,减少内存申请操作
    if (ts == NULL){ // 检查内存分配是否成功
        dev_err(&client->dev, "Alloc GFP_KERNEL memory failed."); // 内存分配失败,打印错误信息
        return -ENOMEM; // 返回内存申请错误的码
    }
    ts->client = client; // 触摸屏设备的客户端指针指向i2c_client结构体
    i2c_set_clientdata(client, ts); // 将my_touch_dev结构体的指针设置为i2c客户端的数据

    // 从设备树中读取触摸屏的最大X和Y值
    if (of_property_read_u32(np, "max-x", &ts->abs_x_max)) {
        dev_err(&client->dev, "no max-x defined\n"); // 如果读取最大X值失败,打印错误信息
        return -EINVAL; // 返回参数无效的错误码
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_x_max); // 打印X值

    if (of_property_read_u32(np, "max-y", &ts->abs_y_max)) {
        dev_err(&client->dev, "no max-y defined\n"); // 如果读取最大Y值失败,打印错误信息
        return -EINVAL; // 返回参数无效的错误码
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_y_max); // 打印Y值

    // 获取并请求复位GPIO管脚
    ts->rst_pin = of_get_named_gpio(np, "reset-gpio", 0); // 从设备树中获取复位管脚
    ret = devm_gpio_request(&client->dev,ts->rst_pin,"my touch touch gpio"); // 请求使用复位管脚
    if (ret < 0){ // 如果请求失败
        dev_err(&client->dev, "gpio request failed."); // 打印错误信息
        return -ENOMEM;                                 // 返回内存申请错误的码
    }
    
    ts->irq_pin = of_get_named_gpio(np, "touch-gpio", 0); // 从设备树中获取中断管脚
    ret = devm_gpio_request_one(&client->dev, ts->irq_pin, // 请求使用中断管脚
                GPIOF_IN, "my touch touch gpio");
    if (ret < 0)
        return ret; // 如果请求失败,直接返回错误码

    // 复位触摸屏设备
    gpio_direction_output(ts->rst_pin,0); // 设置复位管脚输出低电平
    msleep(20); // 等待20毫秒
    gpio_direction_output(ts->irq_pin,0); // 设置中断管脚输出低电平
    msleep(2); // 等待2毫秒
    gpio_direction_output(ts->rst_pin,1); // 设置复位管脚输出高电平
    msleep(6); // 等待6毫秒
    gpio_direction_output(ts->irq_pin, 0); // 设置中断管脚输出低电平
    msleep(50); // 等待50毫秒

    // 申请中断服务
    ts->irq = gpio_to_irq(ts->irq_pin); // 将GPIO管脚转换为中断号
    if(ts->irq){ // 检查中断号是否有效
        ret = devm_request_threaded_irq(&(client->dev), ts->irq, // 请求线程化中断
                NULL, my_touch_irq_handler,                      // 中断服务函数
                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,             // 中断触发方式为下降沿触发,且只触发一次
                client->name, ts);
        if (ret != 0) {
            MY_DEBUG("Cannot allocate ts INT!ERRNO:%d\n", ret); // 如果中断请求失败,打印错误信息
            return ret; // 返回错误码
        }
    }

    // 使用devm_input_allocate_device分配输入设备对象
    ts->input_dev = devm_input_allocate_device(&client->dev); 
    if (!ts->input_dev) { // 检查输入设备对象是否分配成功
        dev_err(&client->dev, "Failed to allocate input device.\n"); // 打印错误信息
        return -ENOMEM; // 返回内存申请错误的码
    }

    // 设置输入设备的名称
    ts->input_dev->name = "my touch screen"; 
    // 设置输入设备的总线类型为I2C
    ts->input_dev->id.bustype = BUS_I2C; 
    
    // 设置X轴的最大值为480
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0); 
    // 设置Y轴的最大值为800
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0); 

    // 初始化5个多点触摸槽位,直接模式
    ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT); 
    if (ret) {
        dev_err(&client->dev, "Input mt init error\n"); // 打印错误信息
        return ret; // 返回错误码
    }

    // 注册输入设备
    ret = input_register_device(ts->input_dev); // 注册输入设备
    if (ret)
        return ret; // 返回错误码

    // 读取版本号
    gt9271_read_version(client); 

    return 0; // 如果一切顺利,返回0
}

横屏probe函数

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    int ret;
    struct my_touch_dev *ts;
    struct device_node *np = client->dev.of_node;
    u8 addr[1] = {0x00};
    u8 point_data[1]={00};//1个状态位置+5个触摸点,一个点是6个数据组成
    // 打印调试信息
    MY_DEBUG("locat");

    // ts = kzalloc(sizeof(*ts), GFP_KERNEL);
    ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
    if (ts == NULL){
        dev_err(&client->dev, "Alloc GFP_KERNEL memory failed.");
        return -ENOMEM;
    }
    ts->client = client;
    i2c_set_clientdata(client, ts);

    if (of_property_read_u32(np, "max-x", &ts->abs_x_max)) {
        dev_err(&client->dev, "no max-x defined\n");
        return -EINVAL;
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_x_max);

    if (of_property_read_u32(np, "max-y", &ts->abs_y_max)) {
        dev_err(&client->dev, "no max-y defined\n");
        return -EINVAL;
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_y_max);

    //找复位gpio
    ts->rst_pin = of_get_named_gpio(np, "reset-gpio", 0);
    //申请复位gpio
    ret = devm_gpio_request(&client->dev,ts->rst_pin,"my touch touch gpio");
    if (ret < 0){
        dev_err(&client->dev, "gpio request failed.");
        return -ENOMEM;
    }

    //找中断引进
    ts->irq_pin = of_get_named_gpio(np, "touch-gpio", 0);
    /* 申请使用管脚 */
    ret = devm_gpio_request_one(&client->dev, ts->irq_pin,
                GPIOF_IN, "my touch touch gpio");
    if (ret < 0)
        return ret;

    /*reset 复位屏幕,如果屏幕没有成功被复位触摸是无法产生中断的*/
    gpio_direction_output(ts->rst_pin,0);
    msleep(20); 
    gpio_direction_output(ts->irq_pin,0);
    msleep(2); 
    gpio_direction_output(ts->rst_pin,1);
    msleep(6); 
    gpio_direction_output(ts->irq_pin, 0);
    gpio_direction_output(ts->irq_pin, 0);
    msleep(50);

    //申请中断
    ts->irq = gpio_to_irq(ts->irq_pin); 
    if(ts->irq){
        ret = devm_request_threaded_irq(&(client->dev), ts->irq, NULL, 
            my_touch_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT , 
            client->name, ts);
        if (ret != 0) {
            MY_DEBUG("Cannot allocate ts INT!ERRNO:%d\n", ret);
            return ret;
        }
    }

    // 分配输入设备对象
    ts->input_dev = devm_input_allocate_device(&client->dev);
    if (!ts->input_dev) {
        dev_err(&client->dev, "Failed to allocate input device.\n");
        return -ENOMEM;
    }

    // 设置输入设备的名称和总线类型
    ts->input_dev->name = "my touch screen";
    ts->input_dev->id.bustype = BUS_I2C;

    /*设置触摸 x 和 y 的最大值*/
    // 设置输入设备的绝对位置参数,假如你的触摸出现范围很小的情况就是这值不对
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0);
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0);

    // 初始化多点触摸设备的槽位
    ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT);
    if (ret) {
        dev_err(&client->dev, "Input mt init error\n");
        return ret;
    }

    // 注册输入设备
    ret = input_register_device(ts->input_dev);
    if (ret)
        return ret;

    my_touch_i2c_write(ts->client, addr,sizeof(addr), point_data, sizeof(point_data));
    gt9271_read_version(client);

    
    return 0;
}

中断

读取触摸数据有很多方法比如轮询但是这样效率太低了,所以我们这里通过中断方式实现触摸数据读取,当屏幕被触控时,屏幕会通过irq引脚输出中断信号。

devm_request_threaded_irq(&(client->dev), ts->irq,   // 请求线程化中断
                NULL, my_touch_irq_handler,          // 中断服务函数
                IRQF_TRIGGER_FALLING | IRQF_ONESHOT, // 中断触发方式为下降沿触发,且只触发一次
                client->name, ts);

中断线程服务函数

上面中断以后当屏幕被触摸会触发中断线程服务函数my_touch_irq_handler,在这个函数里面我们通过i2c去读取屏幕相关的参数。

竖屏中断线程服务函数

static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
{
    s32 ret = -1;                        // 定义一个返回值,初始化为-1
    struct my_touch_dev *ts = dev_id;    // 获取指向设备数据的指针
    u8 addr[1] = {0x02};                 // 定义一个寄存器地址数组对应数据手册TD_STATUS 
    u8 point_data[1+6*5]={0};            // 定义一个点数据数组,预留足够空间 for 5 touch points
    u8 touch_num = 0;                    // 定义一个变量来存储触摸点的数量
    u8 *touch_data;                       // 定义一个指针用于指向点数据
    int i = 0;                           // 定义一个循环变量
    int event_flag, touch_id, input_x, input_y; // 定义一些用于存储事件信息的变量

    MY_DEBUG("irq");                    // 打印中断信息

    ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data)); // 尝试读取触摸屏设备的数据
    if (ret < 0){                        // 如果读取失败
        MY_DEBUG("I2C write end_cmd error!"); // 打印错误信息
    }
    touch_num = point_data[0]&0x0f;     // 获取触摸点的数量
    MY_DEBUG("touch_num:%d",touch_num); // 打印触摸点数量

    // 遍历触摸点数据
    for(i=0; i<5; i++){
        // 获取触摸点数据
        touch_data = &point_data[1+6*i];
        /*
        解析触摸点的事件标志位
        00b: 按下
        01b: 抬起
        10b: 接触
        11b: 保留
        */
        event_flag = touch_data[0] >> 6;
        if(event_flag == 0x03)continue; // 如果事件标志位不是按下或抬起,则跳过此循环
        touch_id = touch_data[2] >> 4;    // 获取触摸点ID

        MY_DEBUG("i:%d touch_id:%d event_flag:%d",i,touch_id,event_flag); // 打印调试信息
        input_x  = ((touch_data[0]&0x0f)<<8) | touch_data[1]; // 计算X坐标
        input_y  = ((touch_data[2]&0x0f)<<8) | touch_data[3]; // 计算Y坐标

        // MY_SWAP(input_x,input_y); // 如果需要交换X和Y坐标,可以取消注释此行
        MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y); // 打印调试信息
        // 设置输入设备的触摸槽位
        input_mt_slot(ts->input_dev, touch_id);

        if(event_flag == 0){ // 如果是按下
            // 上报按下事件和坐标
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); // 设置为按下状态
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
        }else if (event_flag == 2){ // 如果是长按
            // 直接上报数据
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
        else if(event_flag == 1){ // 如果是触摸抬起
            // 上报事件
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); // 设置为抬起状态
        }
    }
    // 报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(ts->input_dev, true);
    // 同步输入事件
    input_sync(ts->input_dev);
    // 返回IRQ_HANDLED,表示中断已经被处理
    return IRQ_HANDLED;
}

横屏中断线程服务函数

/*
1.中断来了,进入这里面,既然来中断证明屏幕被触摸了。
2.读取屏幕里面的触摸数据,每个屏幕不一样但是方法是一样
    怎么读my_touch_i2c_read
    读哪里
3.解析数据
4.上报数据

*/
static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
{
    s32 ret = -1;
    struct my_touch_dev *ts = dev_id;
    u8 addr[1] = {0x02};
    u8 point_data[1+6*5]={0};//1个状态位置+5个触摸点,一个点是6个数据组成
    u8 touch_num = 0;
    u8 *touch_data;
    int i = 0;
    int event_flag, touch_id, input_x, input_y;

    MY_DEBUG("irq");

    ret = my_touch_i2c_read(ts->client, addr,sizeof(addr), point_data, sizeof(point_data));
    if (ret < 0){
        MY_DEBUG("I2C write end_cmd error!");
    }
    touch_num = point_data[0]&0x0f;
    MY_DEBUG("touch_num:%d",touch_num);

    //获取

    for(i=0; i<5; i++){
        //获取点
        touch_data = &point_data[1+6*i];
        /*
        00b: Put Down 
        01b: Put Up 
        10b: Contact 
        11b: Reserved
        */
        //TOUCHn_XH[7:6]
        event_flag = touch_data[0] >> 6;

        MY_DEBUG("event_flag:%d",event_flag);
        if(event_flag == 0x03)continue; 

        touch_id = touch_data[2] >> 4;

        MY_DEBUG("i:%d touch_id:%d event_flag:%d",i,touch_id,event_flag);
        input_x  = ((touch_data[0]&0x0f)<<8) | touch_data[1];
        input_y  = ((touch_data[2]&0x0f)<<8) | touch_data[3];

        // MY_SWAP(input_x,input_y);
        MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y);
        // 设定输入设备的触摸槽位
        input_mt_slot(ts->input_dev, touch_id);

        if(event_flag == 0){
            // 如果是按下上报按下和坐标
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        }else if (event_flag == 2){
            // 如果是长按直接上报数据
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        }else if(event_flag == 1){
            // 触摸抬起,上报事件
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
        }
    }

    // 报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(ts->input_dev, true);

    // 同步输入事件
    input_sync(ts->input_dev);

    return IRQ_HANDLED;
}

TD_STATUS

读取数据从TD_STATUS开始,一个触摸点包含6个数据分别是TOUCH1_XH、TOUCH1_XL、TOUCH1_YH、TOUCH1_YL、TOUCH1_WEIGHT,共计支持5点触控,所以连续读取的长度为1(TD_STATUS)+6(数据)*5。

ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data)); 

在这里插入图片描述

TD_STATUS寄存器中的[3:0]位存储的是触摸点数据,所以我们通过下面语句读取到当前有多少点被按下。

touch_num = point_data[0]&0x0f;     // 获取触摸点的数量

TOUCHn_X寄存器

TOUCHn_XH寄存器的[7:6]位是事件标志位数,所以我们通过touch_data右移6位来获取[7:6]位值。通过判定这个标志位我们可以知道当前触摸状态。

值类型:

  • 触摸:00b
  • 抬起:01b
  • 长按:10b
  • 保留:11b
event_flag = touch_data[0] >> 6;

TOUCHn_XH寄存器的[3:0]位是x坐标的高[11:8]位数,要想获取完整的x坐标值需要把TOUCHn_XH的[3:0]位和TOUCHn_XL的[7:0]位进行组合。

input_x  = ((touch_data[0]&0x0f)<<8) | touch_data[1];

在这里插入图片描述

TOUCHn_Y寄存器

TOUCHn_YH寄存器的[7:4]位是触摸ID,通过右移4位获取到触摸id。

touch_id = touch_data[2] >> 4;    // 获取触摸点ID

TOUCHn_YH寄存器的[3:0]位是y坐标的高[11:8]位数,要想获取完整的x坐标值需要把TOUCHn_YH的[3:0]位和TOUCHn_YL的[7:0]位进行组合。

input_y  = ((touch_data[2]&0x0f)<<8) | touch_data[3]; // 计算Y坐标

在这里插入图片描述

上报数据

数据到以后调用input接口函数进行数据上报,详细的函数使用方法大家看代码里面的注释。

触摸驱动完整代码

#include "linux/stddef.h"
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/input/mt.h>
#include <linux/random.h>

#define MY_SWAP(x, y)                 do{\
                                         typeof(x) z = x;\
                                         x = y;\
                                         y = z;\
                                       }while (0)

#if 1
#define MY_DEBUG(fmt,arg...)  printk("MY_TOUCH:%s %d "fmt"",__FUNCTION__,__LINE__,##arg);
#else
#define MY_DEBUG(fmt,arg...)
#endif

struct my_touch_dev {
    struct i2c_client *client;
    struct input_dev *input_dev;
    int rst_pin;
    int irq_pin;
    u32 abs_x_max;
    u32 abs_y_max;
    int irq;
};

s32 my_touch_i2c_read(struct i2c_client *client,u8 *addr,u8 addr_len, u8 *buf, s32 len)
{
    struct i2c_msg msgs[2];
    s32 ret=-1;
    msgs[0].flags = !I2C_M_RD;
    msgs[0].addr  = client->addr;
    msgs[0].len   = addr_len;
    msgs[0].buf   = &addr[0];
    msgs[1].flags = I2C_M_RD;
    msgs[1].addr  = client->addr;
    msgs[1].len   = len;
    msgs[1].buf   = &buf[0];

    ret = i2c_transfer(client->adapter, msgs, 2);
    if(ret == 2)return 0;

    if(addr_len == 2){
        MY_DEBUG("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(addr[0] << 8)) | addr[1]), len, ret);
    }else {
        MY_DEBUG("I2C Read: 0x%02X, %d bytes failed, errcode: %d! Process reset.", addr[0], len, ret);
    }
    
    return -1;
}

s32 my_touch_i2c_write(struct i2c_client *client, u8 *addr, u8 addr_len, u8 *buf,s32 len)
{
    struct i2c_msg msg;
    s32 ret = -1;
    u8 *temp_buf;

    msg.flags = !I2C_M_RD;
    msg.addr  = client->addr;
    msg.len   = len+addr_len;

    temp_buf= kzalloc(msg.len, GFP_KERNEL);
    if (!temp_buf){
        goto error;
    }
    
    // 装填地址
    memcpy(temp_buf, addr, addr_len);
    // 装填数据
    memcpy(temp_buf + addr_len, buf, len);
    msg.buf = temp_buf;

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret == 1) {
        kfree(temp_buf);
        return 0;
    }

error:
    if(addr_len == 2){
        MY_DEBUG("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(addr[0] << 8)) | addr[1]), len, ret);
    }else {
        MY_DEBUG("I2C Read: 0x%02X, %d bytes failed, errcode: %d! Process reset.", addr[0], len, ret);
    }
    if (temp_buf)
        kfree(temp_buf);
    return -1;
}

/*
1.中断来了,进入这里面,既然来中断证明屏幕被触摸了。
2.读取屏幕里面的触摸数据,每个屏幕不一样但是方法是一样
    怎么读my_touch_i2c_read
    读哪里
3.解析数据
4.上报数据

*/
static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
{
    s32 ret = -1;
    struct my_touch_dev *ts = dev_id;
    u8 addr[1] = {0x02};
    u8 point_data[1+6*5]={0};//1个状态位置+5个触摸点,一个点是6个数据组成
    u8 touch_num = 0;
    u8 *touch_data;
    int i = 0;
    int event_flag, touch_id, input_x, input_y;

    MY_DEBUG("irq");

    ret = my_touch_i2c_read(ts->client, addr,sizeof(addr), point_data, sizeof(point_data));
    if (ret < 0){
        MY_DEBUG("I2C write end_cmd error!");
    }
    touch_num = point_data[0]&0x0f;
    MY_DEBUG("touch_num:%d",touch_num);

    //获取

    for(i=0; i<5; i++){
        //获取点
        touch_data = &point_data[1+6*i];
        /*
        00b: Put Down 
        01b: Put Up 
        10b: Contact 
        11b: Reserved
        */
        //TOUCHn_XH[7:6]
        event_flag = touch_data[0] >> 6;

        MY_DEBUG("event_flag:%d",event_flag);
        if(event_flag == 0x03)continue; 

        touch_id = touch_data[2] >> 4;

        MY_DEBUG("i:%d touch_id:%d event_flag:%d",i,touch_id,event_flag);
        input_x  = ((touch_data[0]&0x0f)<<8) | touch_data[1];
        input_y  = ((touch_data[2]&0x0f)<<8) | touch_data[3];

        // MY_SWAP(input_x,input_y);
        MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y);
        // 设定输入设备的触摸槽位
        input_mt_slot(ts->input_dev, touch_id);

        if(event_flag == 0){
            // 如果是按下上报按下和坐标
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        }else if (event_flag == 2){
            // 如果是长按直接上报数据
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x);
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
        }else if(event_flag == 1){
            // 触摸抬起,上报事件
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
        }
    }

    // 报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(ts->input_dev, true);

    // 同步输入事件
    input_sync(ts->input_dev);

    return IRQ_HANDLED;
}


s32 gt9271_read_version(struct i2c_client *client)
{
    s32 ret = -1;
    u8 addr[1] = {0xA1};
    u8 buf[3] = {0};


    ret = my_touch_i2c_read(client, addr,sizeof(addr), buf, sizeof(buf));
    if (ret < 0){
        MY_DEBUG("GTP read version failed");
        return ret;
    }

    if (buf[5] == 0x00){
        MY_DEBUG("IC Version: %0x %0x_%02x", buf[0], buf[1], buf[2]);
    }
    else{
        MY_DEBUG("IC Version: %0x %0x_%02x", buf[0], buf[1], buf[2]);
    }
    return ret;
}

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    int ret;
    struct my_touch_dev *ts;
    struct device_node *np = client->dev.of_node;
    u8 addr[1] = {0x00};
    u8 point_data[1]={00};//1个状态位置+5个触摸点,一个点是6个数据组成
    // 打印调试信息
    MY_DEBUG("locat");

    // ts = kzalloc(sizeof(*ts), GFP_KERNEL);
    ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
    if (ts == NULL){
        dev_err(&client->dev, "Alloc GFP_KERNEL memory failed.");
        return -ENOMEM;
    }
    ts->client = client;
    i2c_set_clientdata(client, ts);

    if (of_property_read_u32(np, "max-x", &ts->abs_x_max)) {
        dev_err(&client->dev, "no max-x defined\n");
        return -EINVAL;
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_x_max);

    if (of_property_read_u32(np, "max-y", &ts->abs_y_max)) {
        dev_err(&client->dev, "no max-y defined\n");
        return -EINVAL;
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_y_max);

    //找复位gpio
    ts->rst_pin = of_get_named_gpio(np, "reset-gpio", 0);
    //申请复位gpio
    ret = devm_gpio_request(&client->dev,ts->rst_pin,"my touch touch gpio");
    if (ret < 0){
        dev_err(&client->dev, "gpio request failed.");
        return -ENOMEM;
    }

    //找中断引进
    ts->irq_pin = of_get_named_gpio(np, "touch-gpio", 0);
    /* 申请使用管脚 */
    ret = devm_gpio_request_one(&client->dev, ts->irq_pin,
                GPIOF_IN, "my touch touch gpio");
    if (ret < 0)
        return ret;

    /*reset 复位屏幕,如果屏幕没有成功被复位触摸是无法产生中断的*/
    gpio_direction_output(ts->rst_pin,0);
    msleep(20); 
    gpio_direction_output(ts->irq_pin,0);
    msleep(2); 
    gpio_direction_output(ts->rst_pin,1);
    msleep(6); 
    gpio_direction_output(ts->irq_pin, 0);
    gpio_direction_output(ts->irq_pin, 0);
    msleep(50);

    //申请中断
    ts->irq = gpio_to_irq(ts->irq_pin); 
    if(ts->irq){
        ret = devm_request_threaded_irq(&(client->dev), ts->irq, NULL, 
            my_touch_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT , 
            client->name, ts);
        if (ret != 0) {
            MY_DEBUG("Cannot allocate ts INT!ERRNO:%d\n", ret);
            return ret;
        }
    }

    // 分配输入设备对象
    ts->input_dev = devm_input_allocate_device(&client->dev);
    if (!ts->input_dev) {
        dev_err(&client->dev, "Failed to allocate input device.\n");
        return -ENOMEM;
    }

    // 设置输入设备的名称和总线类型
    ts->input_dev->name = "my touch screen";
    ts->input_dev->id.bustype = BUS_I2C;

    /*设置触摸 x 和 y 的最大值*/
    // 设置输入设备的绝对位置参数,假如你的触摸出现范围很小的情况就是这值不对
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0);
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0);

    // 初始化多点触摸设备的槽位
    ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT);
    if (ret) {
        dev_err(&client->dev, "Input mt init error\n");
        return ret;
    }

    // 注册输入设备
    ret = input_register_device(ts->input_dev);
    if (ret)
        return ret;

    my_touch_i2c_write(ts->client, addr,sizeof(addr), point_data, sizeof(point_data));
    gt9271_read_version(client);

    
    return 0;
}

static int my_touch_ts_remove(struct i2c_client *client)
{
    struct my_touch_dev *ts = i2c_get_clientdata(client);
    MY_DEBUG("locat");
    input_unregister_device(ts->input_dev);
    return 0;
}

static const struct of_device_id my_touch_of_match[] = {
    { .compatible = "my,touch", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_touch_of_match);


static struct i2c_driver my_touch_ts_driver = {
    .probe      = my_touch_ts_probe,
    .remove     = my_touch_ts_remove,
    .driver = {
        .name     = "my-touch",
     .of_match_table = of_match_ptr(my_touch_of_match),
    },
};

static int __init my_ts_init(void)
{
    MY_DEBUG("locat");
    return i2c_add_driver(&my_touch_ts_driver);
}

static void __exit my_ts_exit(void)
{
    MY_DEBUG("locat");
    i2c_del_driver(&my_touch_ts_driver);
}

module_init(my_ts_init);
module_exit(my_ts_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("KuLiT");

编译生效

cd u-boot && ./make.sh rk3566 && cd ../kernel && make distclean && make ARCH=arm64 tspi_defconfig rk356x_evb.config android-11.config && make ARCH=arm64 tspi-rk3566-user-v10.img -j16 && cd .. && source build/envsetup.sh && lunch rk3566_tspi-userdebug && make installclean -j16 && make -j16 && ./mkimage.sh

在这里插入图片描述

./build.sh -u

在这里插入图片描述

效果

触摸打印信息,表示生效

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/626248.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是50etf期权?期权的三级交易权限是什么?

今天期权懂带你了解什么是50etf期权&#xff1f;期权的三级交易权限是什么&#xff1f;期权三级交易权限&#xff0c;作为股票期权交易中的最高级别权限&#xff0c;赋予了投资者更广泛、更灵活的交易选择和策略。 什么是50etf期权&#xff1f; ETF期权是指在支付一定额度的权…

如何让机器理解人类语言?Embedding技术详解

如何让机器理解人类语言&#xff1f;Embedding技术详解 文章目录 如何让机器理解人类语言&#xff1f;Embedding技术详解介绍什么是词嵌入&#xff1f;什么是句子嵌入&#xff1f;句子嵌入模型实现句子嵌入的方法值得尝试的句子嵌入模型 句子嵌入库实践Step 1Step 2Step 3 Doc2…

SwiftUI中三大渐变色的介绍

在SwiftUI中&#xff0c;渐变色是一种常用的视觉效果&#xff0c;用于创建平滑过渡的颜色变化。通过使用渐变色&#xff0c;我们可以实现丰富多彩的界面设计&#xff0c;增强用户体验。 1. 渐变色的种类和用途 种类&#xff1a; 线性渐变&#xff08;Linear Gradient&#x…

oracle多条重复数据,取最新的

1、原理讲解-可直接看2 筛选出最新的 SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER (PARTITION BY LOCALAUTHID ORDER BY LASTUPDATETIME DESC) AS rn FROM USER_LOCALAUTH_STATE t ) t WHERE t.rn 1; 解释&#xff1a; 这个序号是基于[LOCALAUTHID]字段进行分…

计算机vcruntime140.dll找不到如何修复,分享5种靠谱的修复教程

当您在运行某个应用程序或游戏时遇到提示“找不到vcruntime140.dll”&#xff0c;这通常意味着系统中缺少了Visual C Redistributable for Visual Studio 2015或更高版本的一个重要组件。这个错误通常发生在运行某些程序时&#xff0c;系统无法找到所需的动态链接库文件。小编将…

ASP.NET医药进销存系统

摘 要 目前&#xff0c;大中型城市的多数药品店已经实现了商品管理、客户管理、销售管理及销售管理等的信息化和网络化&#xff0c;提高了管理效率。但是&#xff0c;在大多数小药品店&#xff0c;药品店管理仍然以传统人工管理为主&#xff0c;特别是在药品的采购、销售、库…

电商核心技术揭秘56:客户关系管理与忠诚度提升

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘相关系列文章合集&#xff08;3&#xff09; 文章目录 引言客户关系管理&#xff08;CRM&#xff09;的重要性提升顾客体验数据驱…

Springboot打包jar如何后台启动和查看日志?

如何后台启动Spring Boot的fat jar 使用nohup命令启动&#xff1a; 在Linux或Unix系统中&#xff0c;你可以使用nohup命令来启动jar包&#xff0c;以确保即使你关闭了终端或断开了SSH连接&#xff0c;程序仍然可以在后台运行。命令格式如下&#xff1a;nohup java -jar yourapp…

PCie协议之-TLP Header详解(一)

✨前言&#xff1a; 在PCIe通信过程中&#xff0c;事务层数据包&#xff08;Transaction Layer Packets&#xff0c;简称TLP&#xff09;扮演着非常重要的角色。TLP用于在设备之间传递数据和控制信息&#xff0c;它们是PCIe的基本信息传输单元。 TLP可分为几个部分&#xff0c…

硬盘格式化后能恢复数据吗?这个恢复方法电脑小白也能用!

硬盘格式化后能恢复数据吗&#xff1f;对于这个问题需要先了解清楚硬盘格式化和数据恢复的原理了。 目前我们所说的硬盘格式化通常都是指“快速格式化”&#xff0c;一般来说当我们在清理磁盘空间或者新建磁盘分区时会使用到这个功能&#xff0c;最终的结果就是清除掉磁盘上的…

“打工搬砖记”中吃什么的轮盘功能实现(二)

文章目录 打工搬砖记转盘主要的逻辑实现转盘的素材小结 打工搬砖记 先来一个吃什么轮盘的预览图&#xff0c;这轮盘文案加字呈圆形铺出来&#xff0c;开始后旋转到指定的选项处停下来。 已上线小程序“打工人搬砖记”&#xff0c;可以扫码进行预览观看。 转盘主要的逻辑实现…

【Unity Shader入门精要 第7章】基础纹理补充内容:MipMap原理

1.纹理采样 我们对纹理采样进行显示的过程&#xff0c;可以理解为将屏幕上的一个像素&#xff08;下文用像素表示&#xff09;映射到纹理上的一个像素&#xff08;下文用纹素表示&#xff09;&#xff0c;然后用纹理上的这个像素的颜色进行显示。 理想情况下&#xff0c;屏幕…

AcqKnowledge 5.0使用方法

Biopac 数据导入 matlab 处理方法 第一步&#xff1a;在 AcqKnowledge 软件中&#xff0c;将数据通道的 mark 信息导入到 Graph&#xff0c;并将数据存储为 acq3 的格式 第二步&#xff1a;MATLAB中读取acq3文件脚本 clc clear %%%所有被试这一层路径 pathsub fullfile(file…

【JavaEE】HTTP 协议

文章目录 一、HTTP 协议1、HTTP 是什么2、理解 "应用层协议"3、理解 HTTP 协议的工作过程4、HTTP 协议格式5、HTTP 请求 (Request)5.1 认识 URL 6、 二、HTTPS1、HTTPS是什么2、"加密" 是什么3、HTTPS 的工作过程3.1 对称加密3.2 非对称加密3.3 证书3.4 完…

iOS——消息传递和消息转发

消息传递&#xff08;Message Passing&#xff09;&#xff1a; 在 iOS 中&#xff0c;消息传递机制是基于 Objective-C 语言的动态性质的一种编程方式。这种机制主要涉及到两个概念&#xff1a;发送者&#xff08;即消息的发送对象&#xff09;和接收者&#xff08;即消息的接…

RS422一主多从MAX3490

RS422一主多从MAX3490 最近项目用到了RS422一主多从&#xff0c;一个主机4个从机。芯片用的MAX3490&#xff0c;几经折腾&#xff0c;最终只能从一拖4改为一拖2。 主机发送端&#xff0c;从机4个接收端都是正常的&#xff0c;没有问题。波形非常完美&#xff0c;没有太大变形 …

matlab使用2-基础绘图

matlab使用2-基础绘图 文章目录 matlab使用2-基础绘图1. 二维平面绘图2. 三维立体绘图3. 图形窗口的分割 1. 二维平面绘图 % 创建一些二维数据 x 0:0.01:10; % x轴的数据点&#xff0c;从0到10&#xff0c;间隔为0.01 y sin(x); % y轴的数据点&#xff0c;是x的正弦…

版本控制:软件开发的基石(一文读懂版本控制)

未经允许&#xff0c;禁止转载&#xff01; 在现代软件开发中&#xff0c;版本控制是不可或缺的工具。它帮助开发者跟踪和管理代码的变化&#xff0c;协作完成项目&#xff0c;并确保代码的完整性和安全性。本文将基于Git官网的视频“什么是版本控制”来深入探讨版本控制的基本…

pyqt QComboBox下拉列表框控件

pyqt QComboBox下拉列表框控件 QComboBox效果代码 QComboBox QComboBox 是 PyQt&#xff08;中的一个控件&#xff0c;它允许用户从下拉列表中选择一个选项。这个控件在需要用户从预定义选项中进行选择时非常有用。 效果 代码 import sys from PyQt5.QtWidgets import QAppl…

转发_重定向

1.Servlet/JSP单独使用的弊端 当我们用Servlet或者JSP单独处理请求的时候 Servlet&#xff1a;拼接大量的html字符串 造成可读性差、难以维护JSP&#xff1a;使得html和Java代码互相交织 也造成了可读性差、难以维护的后果 最合适的做法就是两者结合使用 2.ServletJSP处理请…