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
效果
触摸打印信息,表示生效