Linux 驱动 | hy46xx触摸屏驱动

hy46xx Touch IC

hy46xx是HYCON科技一款触摸IC。

上电时序:
在这里插入图片描述
通信接口:
在这里插入图片描述

  • 使用IIC通信

中断方式:
在这里插入图片描述

  • INT引脚产生下降沿的时候,触摸数据就绪,这样就可以在中断中读取
  • 如果使用线程轮询方式获取触摸数据,则可以通过判断INT引脚是否为低电平来判断触摸数据是否就绪

两种实现驱动方式:

  • 按照IIC设备驱动一般框架驱动芯片获取触摸坐标,这种方式的缺点:不兼容旧的或别人的触摸相关的应用程序
  • 按照内核input输入子系统框架来实现触摸驱动

MT(Multi-touch)多点触摸协议

内核中MT协议相关文档:Documentation\input\multi-touch-protocol.rst

老旧版本的linux内核不支持MT协议,如2.x 版本 linux 内核的话可能找不到 MT 协议

MT协议数以input子系统的一部分。

凡是触摸屏驱动若是按照input子系统框架来实现触摸都要按照MT协议来

MT协议被分为两种:

  • Type A,用于触摸点不能被区分或追踪,需上报原始数据
  • Type B,用于具备硬件追踪并能区分触摸点的触摸设备,此类型设备通过 slot (也就是触摸点ID)更新某一个触摸点的信息,既然是多点触摸协议,触摸点也必须具备ID才能区分是哪个触摸点

hy46xx 便是一款支持多点触摸的的Touch IC,从手册上可得知,其最高可支持11点触摸

现今多数Touch IC都支持多点触摸,遇到的Touch IC基本都是使用I2C接口

内核定义的MT相关的事件: include\uapi\linux\input-event-codes.h

#define ABS_MT_SLOT		0x2f	/* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR	0x30	/* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR	0x31	/* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR	0x32	/* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR	0x33	/* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION	0x34	/* Ellipse orientation */
#define ABS_MT_POSITION_X	0x35	/* Center X touch position */
#define ABS_MT_POSITION_Y	0x36	/* Center Y touch position */
#define ABS_MT_TOOL_TYPE	0x37	/* Type of touching device */
#define ABS_MT_BLOB_ID		0x38	/* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID	0x39	/* Unique ID of initiated contact */
#define ABS_MT_PRESSURE		0x3a	/* Pressure on contact area */
#define ABS_MT_DISTANCE		0x3b	/* Contact hover distance */
#define ABS_MT_TOOL_X		0x3c	/* Center X tool position */
#define ABS_MT_TOOL_Y		0x3d	/* Center Y tool position */

常用MT事件:

  • ABS_MT_SLOT, 上报触摸点ID
  • ABS_MT_POSITION_X,上报触摸X坐标
  • ABS_MT_POSITION_Y,上报触摸Y坐标
  • ABS_MT_TRACKING_ID,对于Type B类设备,需要该事件来区分触摸点
  • ABS_MT_TOOL_TYPE 事件用于上报触摸工具类型
    • MT_TOOL_FINGER(手指)
    • MT_TOOL_PEN(笔)
    • MT_TOOL_PALM(手掌)

Type A Touch Device 上报触摸数据流程 :

1、 ABS_MT_POSITION_X x[0] ,上报触摸点0的x坐标
	调用static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)上报
	
2 、ABS_MT_POSITION_Y y[0] ,上报触摸点0的y坐标

3、 SYN_MT_REPORT , 上报该事件,用于隔离区分不同触摸点,因为Type A设备无法区分触摸点,
    调用static inline void input_mt_sync(struct input_dev *dev)来上报该事件
    
4、 ABS_MT_POSITION_X x[1] ,上报触摸点1的x坐标

5 、ABS_MT_POSITION_Y y[1] ,上报触摸点1的y坐标

6、 SYN_MT_REPORT 

......                       以此类推,上报所有触摸点坐标

n、 SYN_REPORT , 最后结束,上报同步事件,
	调用static inline void input_sync(struct input_dev *dev)来上报

static inline void input_mt_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}


static inline void input_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
}

内核中的一个参考例子:drivers\input\touchscreen\st1232.c

static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
{
	struct st1232_ts_data *ts = dev_id;
	struct st1232_ts_finger *finger = ts->finger;
	struct input_dev *input_dev = ts->input_dev;
	int count = 0;
	int i, ret;

	ret = st1232_ts_read_data(ts);
	if (ret < 0)
		goto end;

	/* multi touch protocol */
	for (i = 0; i < MAX_FINGERS; i++) {
		if (!finger[i].is_valid)
			continue;

		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
		input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
		input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
		input_mt_sync(input_dev);
		count++;
	}

	/* SYN_MT_REPORT only if no contact */
	if (!count) {
		input_mt_sync(input_dev);
		if (ts->low_latency_req.dev) {
			dev_pm_qos_remove_request(&ts->low_latency_req);
			ts->low_latency_req.dev = NULL;
		}
	} else if (!ts->low_latency_req.dev) {
		/* First contact, request 100 us latency. */
		dev_pm_qos_add_ancestor_request(&ts->client->dev,
						&ts->low_latency_req,
						DEV_PM_QOS_RESUME_LATENCY, 100);
	}

	/* SYN_REPORT */
	input_sync(input_dev);

end:
	return IRQ_HANDLED;
}

linux内核是一个宝库,只要熟悉框架,对于一般驱动基本都可以参考内核已有的驱动去编写驱动。

Type B Touch Device 上报触摸数据流程 :

1、 ABS_MT_SLOT 0 ,上报 ABS_MT_SLOT 事件,就是触摸点0的ID,调用input_mt_slot上报

2、 ABS_MT_TRACKING_ID 45,按照Type B类型设备要求每个slot必须
	关联一个TRACKING_ID完成对触摸点的添加、替换或删除,调用input_mt_report_slot_state实现
	
3、 ABS_MT_POSITION_X x[0],上报触摸点0的x坐标, input_report_abs

4、 ABS_MT_POSITION_Y y[0],上报触摸点0的y坐标, input_report_abs

5、 ABS_MT_SLOT 1 ,上报触摸点1的ABS_MT_SLOT 事件

6、 ABS_MT_TRACKING_ID 46 

7、 ABS_MT_POSITION_X x[1] 

8、 ABS_MT_POSITION_Y y[1] 

.....                      // 以此类推上报所有触摸点

x、 SYN_REPORT,调用input_sync上报

static inline void input_mt_slot(struct input_dev *dev, int slot)
{
	input_event(dev, EV_ABS, ABS_MT_SLOT, slot);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}

Type B Touch Device 触摸数据移除流程 :

1、 ABS_MT_TRACKING_ID -1 ,调用input_mt_report_slot_state实现不需要手动设置值为-1,
   将改函数名为active参数设置为false即可
2、 SYN_REPORT 

bool input_mt_report_slot_state(struct input_dev *dev,
				unsigned int tool_type, bool active)
{
	struct input_mt *mt = dev->mt;
	struct input_mt_slot *slot;
	int id;

	if (!mt)
		return false;

	slot = &mt->slots[mt->slot];
	slot->frame = mt->frame;

	if (!active) {
		input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
		return false;
	}

	id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
	if (id < 0)
		id = input_mt_new_trkid(mt);

	input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
	input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);

	return true;
}

从input_mt_report_slot_state函数实现中可以看到,当active设置为false时,会调用
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1)上报ABS_MT_TRACKING_ID,值为-1

hy46xx 触摸驱动实现

1、设备树节点编写

iomuxc节点增加复位引脚的pinctrl节点:pinctrl_tsc_reset

&iomuxc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_hog_1>;
	......
	pinctrl_tsc_reset: tscresetgrp {
		fsl,pins = <
			/* used for tsc reset */
			MX6UL_PAD_LCD_RESET__GPIO3_IO04		0x05
		>;
	};
	......
}

在IO复用节点 —— iomuxc节点中增加即可,位置放在那里都没关系

iomuxc_snvs节点增加中断引脚的pinctrl节点:pinctrl_tsc_irq

&iomuxc_snvs {
	pinctrl-names = "default_snvs";
	......
	pinctrl_tsc_irq: tsc_irq {
		fsl,pins = <
			MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09        0x4001b8b0
		>;
	};
};

最重要是在根节点下增加hy46xx触摸IC的节点:

hy46xx@0x38 {
    compatible = "hy46xx,ts";     /* 设备树和驱动匹配属性 */
    pinctrl-0 = <&pinctrl_tsc_reset>;
    pinctrl-1 = <&pinctrl_tsc_irq>;
    reg = <0x38>;                 /* i2c设备地址 */
    status = "okay";
    /*gpio*/
    reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;      
    irq-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>;
    /*interrupt­*/
    interrupt-parent = <&gpio5>;
    interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
    irq-flags = <2>;		/* 1:rising  2: falling */	
};

设备树编译:

ares@ubuntu:~/work/ebf_linux_kernel-ebf_4.19.35_imx6ul$ cat make_dtb.sh
#!/bin/sh

make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

将设备树拷贝系统内核目录:

debian@npi:~/nfs_root/driver$ cat cp_dtb_to_linux.sh
#!/bin/sh

sudo cp imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-carp-imx6/
  • /usr/lib/linux-image-4.19.35-carp-imx6/ ,系统存放设备树的目录

重启系统设备树生效:

sudo reboot

查看设备树是否生效 :

debian@npi:~$ ls /sys/bus/i2c/devices/
0-001e  0-0038  0-005d  0-0068  1-001a  1-0039  i2c-0  i2c-1

可以看到出现了I2C设备地址为0x38的设备。

2、驱动编写

头文件:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/poll.h>
#include <linux/atomic.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>

宏定义和数据结构:

#define HY_CHIP_ID_REG          0xA9
#define HY_TP_RUN_MODE_REG        0x00        /* 0x00 工作模式  0xc0 测试模式 */
#define HY_FITLTER_REG          0x8A        /* 滤波器  0-5 */
#define HY_PWR_NOISE_REG        0x89        /* 电源噪声 0 off  1 on */
#define HY_FW_VERSION_REG       0xA6       
#define HY_LIB_VERSION_REG      0xA7    
#define HY_PWR_MODE_REG         0xA5        /* 0x03 tp enter sleep  需要 reset pin 拉 low 喚醒 */
#define HY_REPORT_SPEED_REG     0x88        /* 报点率设置 0x64 */

#define HY_GSTID_REG 		    0x02   	    /* 当前检测到的触摸情况 */
#define HY_TP1_XH_REG 		    0X03  	    /* 第一个触摸点数据地址 */
#define HY_TP1_XL_REG 		    0X04  	    /* 第一个触摸点数据地址 */
#define HY_TP1_YH_REG 		    0X05  	    /* 第一个触摸点数据地址 */
#define HY_TP1_YL_REG 		    0X06  	    /* 第一个触摸点数据地址 */

#define HY_TP2_REG 		        0X09		/* 第二个触摸点数据地址 */
#define HY_TP3_REG 		        0X0F		/* 第三个触摸点数据地址 */
#define HY_TP4_REG 		        0X15		/* 第四个触摸点数据地址 */
#define HY_TP5_REG 		        0X1B		/* 第五个触摸点数据地址 */  

#define HY_MAX_SUPPORT_POINTS           5

#define HY46XX_HOR_RES    1024
#define HY46XX_VER_RES    600

#ifndef HY_COUNTOF
#define  HY_COUNTOF(a)           (sizeof(a)/sizeof(a[0]))
#endif

#define  DEV_NAME                   "hy46xx,ts"
#define  HY46XX_DTS_COMPATIBLE       "hy46xx,ts"

#define  HY46XX_DTS_IRQ_GPIO_NAME     "irq-gpios"
#define  HY46XX_DTS_RST_GPIO_NAME     "reset-gpios"

enum hy46xx_ts_state {
	HY46XX_TS_DOWN    = 0x00,      /* 按下 */
	HY46XX_TS_UP      = 0x01,        /* 弹起 */
	HY46XX_TS_CONTACT = 0x02,       /* 持续性按下 */
    HY46XX_TS_RESERVED = 0x03,  
};

struct touch_data {
    short x;
    short y;
    uint8_t state;
    uint8_t id;
};

struct hy46xx_device {
    int irq; /* 中断号 */
    int irq_gpio;
    int rst_gpio;
    dev_t dev_no; /* 设备号 */
    struct i2c_client *i2c;
    struct input_dev *inputdev;    /* input 结构体 */ 
    struct touch_data ts_data[HY_MAX_SUPPORT_POINTS];
};

static struct hy46xx_device *hy46xx_dev;

熟悉的I2C子系统操作:

static int hy46xx_write_regs(struct i2c_client *i2c, uint8_t reg, uint8_t *buf, uint8_t len)
{
    int ret = 0;
    uint8_t w_buf[300] = {0};

    struct i2c_msg msg;

    w_buf[0] = reg;
    memcpy(&w_buf[1], buf, len);

    msg.addr = i2c->addr;
    msg.buf = w_buf;
    msg.flags = 0;               /* I2C direction : write */
    msg.len = len + 1;

    ret = i2c_transfer(i2c->adapter, &msg, 1);
    if (ret < 0)
        return ret;
    else if (ret != 1)
        return -EIO;

    return 0;
}

static int hy46xx_write_reg(struct i2c_client *client, uint8_t reg, uint8_t data)
{
    return hy46xx_write_regs(client, reg, &data, 1);
}

static int hy46xx_read_regs(struct i2c_client *client, uint8_t reg, uint8_t *buf, uint8_t len)
{
    struct i2c_msg msgs[2];
	int ret;

	msgs[0].flags = 0;
	msgs[0].addr  = client->addr;
	msgs[0].len   = 1;
	msgs[0].buf   = &reg;

	msgs[1].flags = I2C_M_RD;
	msgs[1].addr  = client->addr;
	msgs[1].len   = len;
	msgs[1].buf   = buf;

	ret = i2c_transfer(client->adapter, msgs, 2);
    if (ret < 0)
        return ret;
    else if (ret != 2)
        return -EIO;
    return 0;
}

static int hy46xx_i2c_read_reg(struct i2c_client *client, uint8_t reg, uint8_t *data)
{
    return hy46xx_read_regs(client, reg, data, 1);
}

static int hy46xx_read_touch_data(struct hy46xx_device *dev)
{
    uint8_t buf[29];
    uint8_t *ts;
    int ret;
    int i;

    ret =  hy46xx_read_regs(dev->i2c, HY_TP1_XH_REG, buf, 29);
    if (ret != 0) return ret;

    for (i = 0; i < HY_MAX_SUPPORT_POINTS; i++)
    {
        ts = &buf[i * 6];
        dev->ts_data[i].state = (ts[0] & 0xC0) >> 6;
        dev->ts_data[i].x = ((ts[0] & 0x0f) << 8) | ts[1];
        dev->ts_data[i].y = ((ts[2] & 0x0f) << 8) | ts[3];
        dev->ts_data[i].id = (ts[2] & 0xf0) >> 4;            
    }

    return 0;
}

/* 设置为坐标模式, 默认也是坐标模式 */
static int hy46xx_set_tp_run_mode(struct hy46xx_device *dev)
{
    return hy46xx_write_reg(dev->i2c, HY_TP_RUN_MODE_REG, 0x00);
}

static int hy46xx_ts_reset(struct hy46xx_device *dev)
{
    if (gpio_is_valid(dev->rst_gpio))   /* 检查 IO 是否有效 */ 
    {
        gpio_set_value(dev->rst_gpio, 0);
        msleep(5);
        gpio_set_value(dev->rst_gpio, 1);
        msleep(1000);

        return 0;
    }
    return -1;
}

probe 和 remove函数实现:

static int hy46xx_driver_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int err = 0;
    struct device_node *dev_node;

    hy46xx_dev = (struct hy46xx_device *)kzalloc(sizeof(struct hy46xx_device), GFP_KERNEL);
    if (!hy46xx_dev)
    {
        printk("can't kzalloc hy46xx device\n");
        return -ENOMEM;
    }

    dev_node = client->dev.of_node;
    if (!dev_node)
    {
        printk("hy46xx ts dts node err\r\n");
        err = -EINVAL;
        goto exit_free_dev;
    }

    hy46xx_dev->i2c = client;
    printk("hy46xx dts irq %d !\r\n", client->irq);

    hy46xx_dev->irq_gpio = of_get_named_gpio(dev_node, HY46XX_DTS_IRQ_GPIO_NAME, 0);   /* 获取irq-gpios */
    if (!gpio_is_valid( hy46xx_dev->irq_gpio)) {
        printk("don't get %s %s!\n", DEV_NAME, HY46XX_DTS_IRQ_GPIO_NAME);
    	err = -EINVAL;
        goto exit_free_dev;
    }
    hy46xx_dev->irq = gpio_to_irq( hy46xx_dev->irq_gpio);                              /* 通过gpio得到irq */
    
    hy46xx_dev->rst_gpio = of_get_named_gpio(dev_node, HY46XX_DTS_RST_GPIO_NAME, 0);   /* 获取rst-gpios */
    if (!gpio_is_valid( hy46xx_dev->rst_gpio)) {
        printk("don't get %s %s!\n", DEV_NAME, HY46XX_DTS_RST_GPIO_NAME);
    	err = -EINVAL;
        goto exit_free_dev;
    }

    printk("%s %d, %s %d", HY46XX_DTS_IRQ_GPIO_NAME, hy46xx_dev->irq_gpio, HY46XX_DTS_RST_GPIO_NAME, hy46xx_dev->rst_gpio);

#if 0
    err = request_irq(hy46xx_dev->irq, hy46xx_ts_isr, RQF_TRIGGER_FALLING, DEV_NAME, NULL);    
#else
   /* 中断线程化 */
    err = devm_request_threaded_irq(&client->dev, client->irq, NULL, 
                                    hy46xx_ts_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 
                                    client->name, hy46xx_dev);
#endif
    if (err)
    {
        printk(KERN_INFO "failed to request irq %d\r\n", hy46xx_dev->irq);
        goto exit_free_dev;
    }

    hy46xx_dev->inputdev =  devm_input_allocate_device(&client->dev);;    /* 申请输入设备结构 */
    if (!hy46xx_dev->inputdev) 
    {
        printk(KERN_ERR "%s: Failed to allocate input device.\n", __func__);
        err = -ENOMEM;
        goto exit_free_irq;
    }
    hy46xx_dev->inputdev->name = DEV_NAME;
    hy46xx_dev->inputdev->id.bustype = BUS_I2C;      /* 触摸屏通信总线类型 */
    hy46xx_dev->inputdev->dev.parent = &client->dev;

    /* Single touch */
	input_set_abs_params(hy46xx_dev->inputdev, ABS_X, 0, HY46XX_HOR_RES, 0, 0);
	input_set_abs_params(hy46xx_dev->inputdev, ABS_Y, 0, HY46XX_VER_RES, 0, 0);

    input_set_abs_params(hy46xx_dev->inputdev, ABS_MT_POSITION_X, 0, HY46XX_HOR_RES, 0, 0);
	input_set_abs_params(hy46xx_dev->inputdev, ABS_MT_POSITION_Y, 0, HY46XX_VER_RES, 0, 0);

    err = input_mt_init_slots(hy46xx_dev->inputdev, HY_MAX_SUPPORT_POINTS, 0);        /* 初始化触摸屏,5点触摸 */
	if (err) {
		dev_err(&client->dev,
			"Failed to initialize MT slots: %d", err);
		goto exit_free_irq;
	}

    err = input_register_device(hy46xx_dev->inputdev);           /* 注册input_device */                
    if (err) 
    {
        printk(KERN_ERR "%s: Failed to regist key device.\n", __func__);
        goto exit_free_irq;
    }

    gpio_direction_input(hy46xx_dev->irq_gpio);
    gpio_direction_output(hy46xx_dev->rst_gpio, 0); 

    hy46xx_ts_reset(hy46xx_dev);     /* 复位 */

    hy46xx_set_tp_run_mode(hy46xx_dev);

    goto exit;
exit_free_irq:                                   
    free_irq(hy46xx_dev->irq, NULL);             /* 释放中断*/
exit_free_dev:
    kfree(hy46xx_dev);
    hy46xx_dev = NULL;
exit:
    return err;
}

static int hy46xx_driver_remove(struct i2c_client *i2c)
{
    /* 释放中断*/
    free_irq(hy46xx_dev->irq, NULL);    
        
    /* 释放内存 */    
    kfree(hy46xx_dev);

    printk(KERN_INFO "%s success\n", DEV_NAME);

    return 0;
}

出口入口函数实现:

/* 设备树的匹配列表 */
static struct of_device_id dts_match_table[] = {
    {.compatible = HY46XX_DTS_COMPATIBLE}, /* 通过设备树来匹配 */
    {},
};

/* 传统匹配方式 ID 列表 ,即使不使用也要添加,不然probe匹配不成功 */
static const struct i2c_device_id id_table[] = {
    {.name = HY46XX_DTS_COMPATIBLE, 0},
    {},
};

static struct i2c_driver hy46xx_driver = {
    .probe = hy46xx_driver_probe,
    .remove = hy46xx_driver_remove,
    .driver = {
        .name = HY46XX_DTS_COMPATIBLE,
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table, /* 通过设备树匹配 */
    },
    .id_table = id_table,
};

module_i2c_driver(hy46xx_driver);

上报触摸数据的关键操作:

static void hy46xx_report_events(struct hy46xx_device *dev)
{
	int i;
	bool touch;

	for (i = 0; i < HY_MAX_SUPPORT_POINTS; i++) 
    {
		input_mt_slot(dev->inputdev, dev->ts_data[i].id);                  /* ABS_MT_SLOT事件  上报触摸ID  */

        if (dev->ts_data[i].state == HY46XX_TS_RESERVED) continue;         

		touch = dev->ts_data[i].state != HY46XX_TS_UP;                     /* 当触摸按下touch为true,弹起时touch为false */

		input_mt_report_slot_state(dev->inputdev, MT_TOOL_FINGER, touch);    /* ABS_MT_TRACKING_ID 事件 */

		if (touch)                                                        
        {
			input_report_abs(dev->inputdev, ABS_MT_POSITION_X, dev->ts_data[i].x);    /* 上报触摸点的x坐标 */
			input_report_abs(dev->inputdev, ABS_MT_POSITION_Y, dev->ts_data[i].y);    /* 上报触摸点的y坐标 */
		}
	}

	input_mt_report_pointer_emulation(dev->inputdev, true);
	input_sync(dev->inputdev);                                            
}

static irqreturn_t hy46xx_ts_isr(int irq, void *dev)
{
    struct hy46xx_device *hydev = (struct hy46xx_device *)dev;

    /* 上报触摸点数据 */
    hy46xx_read_touch_data(hydev);
    hy46xx_report_events(hydev);

    return IRQ_RETVAL(IRQ_HANDLED);
}

hy46xx符合Type B类型的触摸设备时序,故而使用的是Type B类型时序。
在中断处理函数hy46xx_ts_isr中调用hy46xx_read_touch_data读取5点坐标,调用hy46xx_report_events上报事件

3、驱动测试

安装驱动出现错误:

debian@npi:~/nfs_root/driver$ sudo insmod hy46xx_ts_drv.ko
[   69.990212] hy46xx_ts_drv: loading out-of-tree module taints kernel.
[   70.003587] hy46xx dts irq 210 !
[   70.006999] irq-gpios 137, reset-gpios 68
[   70.016025] input: hy46xx,ts as /devices/soc0/soc/2100000.aips-bus/21a0000.i2c/i2c-0/0-0038/input/input1

这是因为系统还有个gt9xx的触摸使用了设备树节点定义的资源,所以匹配失败,去掉gt9xx相关的设备节点即可。
我使用的系统触摸设备树节点是使用设备树插件实现的,只要禁用关于gt9xx触摸屏插件即可

debian@npi:~/nfs_root/driver$ sudo nano /boot/uEnv.txt
  GNU nano 3.2                     /boot/uEnv.txt

dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-hdmi.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-cam.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-btwifi.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-can1.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-can2.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-dht11.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-ecspi3.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-key.dtbo
dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-lcd.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-touch-capac$
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-led.dtbo
dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-sound.dtbo
dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-uart2.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-uart3.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-mpu6050.dtbo
#dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-ds18b20.dtbo
#overlay_end

通过#号注释掉dtoverlay=/usr/lib/linux-image-4.19.35-carp-imx6/overlays/imx-fire-touch-capac该项目接着保存重启系统使修改生效。

重新安装驱动模块:

debian@npi:~/nfs_root/driver$ sudo insmod hy46xx_ts_drv.ko
[   69.990212] hy46xx_ts_drv: loading out-of-tree module taints kernel.
[   70.003587] hy46xx dts irq 210 !
[   70.006999] irq-gpios 137, reset-gpios 68
[   70.016025] input: hy46xx,ts as /devices/soc0/soc/2100000.aips-bus/21a0000.i2c/i2c-0/0-0038/input/input1

可以看到已经probe成功了

查看输入子系统生成设备节点:

debian@npi:~/nfs_root/driver$ ls /dev/input/ev*
/dev/input/event0  /dev/input/event1

/dev/input/event1 就是hy46xx触摸的设备节点

用命令方式测试驱动:

debian@npi:~/nfs_root/driver$ hexdump /dev/input/event1
hexdump: /dev/input/event1: Permission denied
debian@npi:~/nfs_root/driver$ sudo hexdump /dev/input/event1
0000000 c6f9 6249 62ff 000e 0003 0039 000c 0000
0000010 c6f9 6249 62ff 000e 0003 0035 024b 0000
0000020 c6f9 6249 62ff 000e 0003 0036 0108 0000
0000030 c6f9 6249 62ff 000e 0003 0000 024b 0000
0000040 c6f9 6249 62ff 000e 0003 0001 0108 0000
0000050 c6f9 6249 62ff 000e 0000 0000 0000 0000
0000060 c6f9 6249 0498 000f 0003 0039 ffff ffff

这样测试不太直观,编写测试程序:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>

#define DEV_NAME "/dev/input/event1"

static struct input_event in_event; 

int main(int argc, char *argv[])
{
    int fd;
    int ret = 0;

    fd = open(DEV_NAME, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", DEV_NAME);
        return -1;
    }

    while (1) 
    {
        ret = read(fd, &in_event, sizeof(in_event));
        if (ret == sizeof(in_event))      /* 读取数据成功 */
        {
            switch (in_event.type) 
            {
            case EV_KEY:     /* 按键事件类型 */
                break;

            /* 其他类型的事件,自行处理 */ 
            case EV_REL: 

                break; 
            case EV_ABS: 

                if (in_event.code == ABS_X)
                {
                    printf("x %d ", in_event.value);
                }

                if (in_event.code == ABS_Y)
                {
                    printf("y %d\r\n", in_event.value);
                }
               
                break; 
            case EV_MSC: 

                break; 
            case EV_SW: 

                break; 
            } 
        }
        else
        { 
            printf("读取数据失败\r\n"); 
        } 
    }  
    return 0; 
}

执行sudo ./hy46xx_ts_test进行测试

debian@npi:~/nfs_root/driver$ sudo ./hy46xx_ts_test
x 606 y 303
x 621 y 464
x 744 y 255
x 734 y 276
x 826 y 428
x 972 y 573
x 982 y 529
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲盖弥彰1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值