10.2多点触摸屏驱动

多点电容触摸(MT)协议

多点电容触摸(MT)协议是 input 子系统的一部分, MT 协议被分为两种类型,分别是 Type A 和 Type B :

  • Type A :适用于触摸点不能被区分或者追踪的设备(即硬件上没有触摸 ID)
  • Type B :适用于有硬件追踪并能区分触摸点的触摸设备
    触摸点的信息通过一系列的 ABS_MT_* 事件上报给 linux 内核,常用的 ABS_MT 事件有:
  • ABS_MT_SLOT :表示后面上报的是相应 slot 的触摸信息,用于分割 Type B 类型触摸点
  • ABS_MT_TRACKING_ID :上报触摸 ID 事件, slot 从松开到按下时系统自动分配 TRACKING_ID
  • ABS_MT_POSITION_X :上报绝对坐标X
  • ABS_MT_POSITION_Y :上报绝对坐标Y
  • SYN_MT_REPORT:表示前面已经上报完一个触摸点数据,用于分割 Type A 类型触摸点
    对于 Type B 类型采用 ABS_MT_SLOT 分割触摸点,并用 ABS_MT_TRACKING_ID 上报触摸点 ID,对于 Type A 类型则采用 SYN_MT_REPORT 事件来分割触摸点。

多点触摸所常用 API

/* 初始化 slots* dev :MT 设备对应的 input_dev* num_slots :设备要使用的 SLOT 数量,也就是触摸点的数量* flags :其他一些 flags 信息,如 INPUT_MT_POINTER 表示触摸板设备,INPUT_MT_DIRECT 表示触摸屏设备**/
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, unsigned int flags)
/* 通过 slot 上报 ABS_MT_SLOT 事件* dev :MT 设备对应的 input_dev* slot :指定产生 ABS_MT_SLOT 事件 slot**/
void input_mt_slot(struct input_dev *dev, int slot)
/* 上报触摸点的状态,用于产生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE 事件* dev :MT 设备对应的 input_dev* tool_type :触摸类型, MT_TOOL_FINGER (手指)、 MT_TOOL_PEN (笔)、 MT_TOOL_PALM (手掌)* active :触摸状态, true 表示按下,如果是第一次按下系统会为其分配一个 ID ,false 表示抬起**/
bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active)
/* 上报绝对坐标事件* dev :MT 设备对应的 input_dev* code :事件类型,ABS_MT_POSITION_X 或 ABS_MT_POSITION_Y 分别表示 X 轴和 Y 轴,* value : X 轴或 Y 轴坐标值**/
void input_report_abs(struct input_dev *dev,unsigned int code, int value)
/* 单点触摸模拟* dev :MT 设备对应的 input_dev* use_count :为 true 则要上报触摸点数**/
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
/* 用于产生 SYN_MT_REPORT 事件* dev :MT 设备对应的 input_dev**/
void input_mt_sync(struct input_dev *dev)
/* 上报触摸坐标* input :MT 设备对应的 input_dev* prop :prop触摸屏特性* x,y :触摸坐标* multitouch :是否是多点触摸,true表示多点触摸**/
void touchscreen_report_pos(struct input_dev *input, const struct touchscreen_properties *prop, unsigned int x, unsigned int y, bool multitouch)
/* 根据设备树配置初始化触摸屏特性,根据input对象absbit的设置完成对absinfo的配置* input :MT 设备对应的 input_dev* multitouch :是否是多点触摸,true表示多点触摸* prop :返回触摸屏特性**/
void touchscreen_parse_properties(struct input_dev *input, bool multitouch, struct touchscreen_properties *prop)

上报触摸事件

Type A 上报顺序:

1. ABS_MT_POSITION_X        通过 ABS_MT_POSITION_X 上报第一个触摸点的 X 坐标数据,input_report_abs
2. ABS_MT_POSITION_Y        通过 ABS_MT_POSITION_Y 上报第一个触摸点的 Y 坐标数据,input_report_abs
3. SYN_MT_REPORT            上报 SYN_MT_REPORT 事件,input_mt_sync
4. ABS_MT_POSITION_X        通过 ABS_MT_POSITION_X 上报第二个触摸点的 X 坐标数据
5. ABS_MT_POSITION_Y        通过 ABS_MT_POSITION_Y 上报第二个触摸点的 Y 坐标数据
6. SYN_MT_REPORT            上报 SYN_MT_REPORT 事件
......                      继续上报剩余的坐标
7. SYN_REPORT               上报 SYN_REPORT 事件,input_sync

Type B 上报顺序:

1. ABS_MT_SLOT             上报第一个触摸点的 ABS_MT_SLOT 事件,input_mt_slot
2.  ABS_MT_TRACKING_ID     通过 ABS_MT_TRACKING_ID 上报第一个触摸点的 ID,input_mt_report_slot_state
3. ABS_MT_POSITION_X       通过 ABS_MT_POSITION_X 上报第一个触摸点的 X 坐标数据,input_report_abs
4. ABS_MT_POSITION_Y       通过 ABS_MT_POSITION_Y 上报第一个触摸点的 Y 坐标数据,input_report_abs
5. ABS_MT_SLOT             上报第二个触摸点的 ABS_MT_SLOT 事件
6. ABS_MT_TRACKING_ID	   通过 ABS_MT_TRACKING_ID 上报第二个触摸点的 ID
7. ABS_MT_POSITION_X       通过 ABS_MT_POSITION_X 上报第二个触摸点的 X 坐标数据
8. ABS_MT_POSITION_Y       通过 ABS_MT_POSITION_Y 上报第二个触摸点的 Y 坐标数据
......
9. SYN_REPORT             上报 SYN_REPORT 事件,input_sync

移植 tslib

tslib 是一个开源的用于操作触摸屏的库,它可以简化触摸屏编程,同时也集成了一些触摸屏测试工具,其移植过程如下:

基于 buildroot 移植

buildroot中集成了tslib,在编译时选择其即可

make menuconfig
-> Target packages -> Libraries -> Hardware handling -> [*] tslib

基于源码移植

  1. 解压 tslib ,并进入源码目录
tar -vxf tslib-1.21.tar.xz
cd tslib-1.21/
  1. 配置 tslib
#--host=arm-none-linux-gnueabihf编译器前缀
#--prefix=/安装到目标机的目录
./configure --host=arm-none-linux-gnueabihf --prefix=/
  1. 编译 tslib
make
  1. 安装 tslib
#会在当前目录下创建一个tmp目录,并将此目录作为目标机的跟目录进行安装
make install DESTDIR=$PWD/tmp
  1. 将安装后得到的头文件和库文件拷贝到工具链中(如果不基于 tslib 开发应用程序可以忽略此步骤)
#/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/include是工具链头文件路径
cp ./tmp/include/* /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/include
#/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib是工具链库文件路径
cp -d ./tmp/lib/*so* /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib
  1. 将安装后得到的文件拷贝到目标板跟文件系统中
#../../../../rootfs/是nfs根文件系统目录
cp -r tmp/* ../../../../rootfs/

基于 tslib 开发应用程序

  1. 使用 ts_setup 打开触摸屏设备,并初始化 tslib
  2. 使用 ioctl(int ts_fd(struct tsdev *ts), EVIOCGABS(ABS_MT_SLOT), struct input_absinfo *slot) 获取触摸屏的 slot 信息,并计算触摸点数
  3. 使用 ts_read_mt 读取触摸屏数据
  4. 最后通过 ts_close 关闭触摸屏设备

tslib 常用函数

//ts_sample结构体
struct ts_sample {//X 坐标int x;//Y 坐标int y;//按压力大小unsigned int pressure;//时间struct timeval tv;
};
//ts_sample_mt结构体
struct ts_sample_mt {//X 坐标int x; //Y 坐标int y; //按压力大小unsigned int pressure;//触摸点 slotint slot;//IDint tracking_id;int tool_type;int tool_x;int tool_y;unsigned int touch_major;unsigned int width_major;unsigned int touch_minor;unsigned int width_minor;int orientation;int distance;int blob_id;//时间struct timeval tv;//BTN_TOUCH 的状态short pen_down;//此次样本是否有效标志 触摸点数据是否发生更新short valid;
};
/* 打开触摸屏设备,并初始化 tslib* dev_name 触摸设备名称,为NULL则会在系统中搜索* nonblock 是否阻塞* 成功返回tslib设备句柄**/
struct tsdev *ts_setup(const char *dev_name, int nonblock)
/* 读取多点触摸屏数据* ts tslib设备句柄* samp 指向一个struct ts_sample_mt *对象,用于返回触摸坐标* nr 表示采样次数,采样值依次存入samp[0][0] samp[0][1] ... samp[0][max_slots] samp[1][0] samp[1][1] ...*    samp[1][max_slots] ... 中* 成功返回实际采样次数**/
int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
/* 读取单点触摸屏数据* ts tslib设备句柄* samp:指向一个 struct ts_sample 对象,用于返回触摸坐标*  nr :表示采样次数。* 成功返回实际采样次数**/
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
/* 关闭触摸屏设备,并释放tslib资源* ts tslib设备句柄* 成功返回0**/
int ts_close(struct tsdev *ts)

编写GT1151驱动

硬件原理图

在这里插入图片描述
在这里插入图片描述

GT1151寄存器简介

配置信息:
在这里插入图片描述
其中配置信息的byte1~4表示触摸屏尺寸:
在这里插入图片描述
产品ID:
在这里插入图片描述触摸坐标:
虽然一共有10组触摸点坐标寄存器组,但是它最多支持同时跟踪5个触摸点
在这里插入图片描述

驱动代码编写

设备树编写

在顶层设备树中引用i2c2节点,并加入如下内容:

&i2c2 {pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c2_pins_a>;pinctrl-1 = <&i2c2_pins_sleep_a>;status = "okay";gt91xx@14 {compatible = "alientek,gt91xx";reg = <0x14>;x-size = <800>;y-size = <480>;reset-gpios = <&gpioh 15 GPIO_ACTIVE_LOW>;irq-gpios = <&gpioi 1 GPIO_ACTIVE_LOW>;interrupt-parent = <&gpioi>;interrupts = <1 IRQ_TYPE_EDGE_FALLING>;};
};

在 stm32mp15-pinctrl.dtsi 的 &pinctrl 节点中修改 i2c2 的引脚配置为如下内容

	i2c2_pins_a: i2c2-0 {pins {pinmux = <STM32_PINMUX('H', 4, AF4)>, /* I2C2_SCL */<STM32_PINMUX('H', 5, AF4)>; /* I2C2_SDA */bias-disable;drive-open-drain;slew-rate = <0>;};};i2c2_pins_sleep_a: i2c2-1 {pins {pinmux = <STM32_PINMUX('H', 4, ANALOG)>, /* I2C2_SCL */<STM32_PINMUX('H', 5, ANALOG)>; /* I2C2_SDA */};};

注意:设备树的内容需要根据硬件实际连接进行修改
用make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,然后用新的.dtb文件启动系统

驱动代码编写

驱动代码主要包括以下几个部分:

  1. 初始化GT1151硬件
  2. 初始化并注册触摸屏输入设备
  3. 注册触摸屏中断
  4. 在触摸屏中断中上报触摸点信息
    完整的驱动代码如下所示:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>#define GT1151_CONFIG_REG		0x8050		//配置寄存器起始
#define GT1151_PRODUCT_ID_REG	0x8140		//产品ID寄存器
#define GT1151Q_GSTID_REG		0x814E		//GT1151Q当前检测到的触摸情况
#define GT1151Q_TP1_REG			0x814F		//第1个触摸点数据地址
#define GT1151Q_TP2_REG			0x8157		//第2个触摸点数据地址
#define GT1151Q_TP3_REG			0x815F		//第3个触摸点数据地址
#define GT1151Q_TP4_REG			0x8167		//第4个触摸点数据地址
#define GT1151Q_TP5_REG			0x816F		//第5个触摸点数据地址//配置数据长度
#define GT1151_HW_CONFIG_LEN	239
//支持的触摸点数量
#define GTP_MAX_TOUCH			5typedef struct gt1151 {struct input_dev *inputdev;						/* 输入设备句柄 */struct i2c_client *client;						/* I2C client句柄 */int rst_pin;									/* 复位引脚 */int intr_pin;									/* 中断引脚 */uint32_t x_range;								/* 触摸屏宽度 */uint32_t y_range;								/* 触摸屏高度 */uint8_t last_touch_state[GTP_MAX_TOUCH];		/* 缓存各个触摸点上一次的触摸状态 */uint8_t write_buffer[512];						/* I2C写数据缓存 */uint8_t gt1151_config[GT1151_HW_CONFIG_LEN];	/* gt1151配置数据缓存 */
}gt1151_t;static const uint16_t tp_reg[GTP_MAX_TOUCH] = {
#if GTP_MAX_TOUCH >= 1GT1151Q_TP1_REG,
#endif
#if GTP_MAX_TOUCH >= 2GT1151Q_TP2_REG,
#endif
#if GTP_MAX_TOUCH >= 3GT1151Q_TP3_REG,
#endif
#if GTP_MAX_TOUCH >= 4GT1151Q_TP4_REG,
#endif
#if GTP_MAX_TOUCH >= 5GT1151Q_TP5_REG,
#endif
};static int gt1151_read(gt1151_t *gt1151, uint16_t reg, uint8_t *data, uint16_t lenght)
{uint8_t addr[2];struct i2c_msg msg[2];addr[0] = (uint8_t)((reg >> 8) & 0xFF);addr[1] = (uint8_t)(reg & 0xFF);//从机地址msg[0].addr = gt1151->client->addr;//表示写msg[0].flags = 0;//buf是一个指针,指向了要发送的数据msg[0].buf = addr;//msg[0].buf的数据长度msg[0].len = 2;msg[1].addr = gt1151->client->addr;//表示读msg[1].flags = I2C_M_RD;msg[1].buf = data;msg[1].len = lenght;if(i2c_transfer(gt1151->client->adapter, msg, 2)  == 2)return 0;else return -EIO;
}static int gt1151_write(gt1151_t *gt1151, uint16_t reg, uint8_t *data, uint16_t lenght)
{struct i2c_msg msg[1];if(lenght > sizeof(gt1151->write_buffer)-2)return -EIO;memset(gt1151->write_buffer, 0, sizeof(gt1151->write_buffer));//只能用一个msg发送,分多个msg时msg衔接的时候不会等待设备的ACK信号,可能会导致失败gt1151->write_buffer[0] = (uint8_t)((reg >> 8) & 0xFF);gt1151->write_buffer[1] = (uint8_t)(reg & 0xFF);memcpy(&gt1151->write_buffer[2], data, lenght);//从机地址msg[0].addr = gt1151->client->addr;//表示写msg[0].flags = 0;//buf是一个指针,指向了要发送的数据msg[0].buf = gt1151->write_buffer;//msg[0].buf的数据长度msg[0].len = 2 + lenght;if(i2c_transfer(gt1151->client->adapter, msg, 1)  == 1)return 0;elsereturn -EIO;
}static irqreturn_t gt1151_irq_thread(int irq, void *arg)
{uint8_t i;uint8_t clr_intr;uint8_t touch_number;uint8_t phy_id;uint16_t x_coordinate;uint16_t y_coordinate;int32_t result;uint8_t buffer[5];uint8_t now_touch_state[GTP_MAX_TOUCH];gt1151_t *gt1151 = (gt1151_t*)arg;//读取触摸状态寄存器result = gt1151_read(gt1151, GT1151Q_GSTID_REG, buffer, 1);if(result != 0){printk("read reg failed\r\n");return IRQ_HANDLED;}//获取触摸点数touch_number = buffer[0] & 0x0F;//复位当前触摸状态memset(now_touch_state, 0, sizeof(now_touch_state));//依次处理触摸点for(i = 0; (i < touch_number) && (i < GTP_MAX_TOUCH); i++) {//读取一个触摸点result = gt1151_read(gt1151, tp_reg[i], buffer, 5);if(result != 0){printk("read reg failed\r\n");return IRQ_HANDLED;}//获取触摸点ID,这里通过物理ID来区分slotphy_id = buffer[0] & 0x0F;//ID不合法if(phy_id >= GTP_MAX_TOUCH)continue;//获取触摸坐标x_coordinate = buffer[1] | (buffer[2] << 8);y_coordinate = buffer[3] | (buffer[4] << 8);//记录当前触摸点的状态now_touch_state[phy_id] = 1;//通过第phy_id个slot上报ABS_MT_SLOT事件,表示即将上报第phy_id的触摸信息input_mt_slot(gt1151->inputdev, phy_id);//上报触摸状态input_mt_report_slot_state(gt1151->inputdev, MT_TOOL_FINGER, true);//上报触摸坐标input_report_abs(gt1151->inputdev, ABS_MT_POSITION_X, x_coordinate);input_report_abs(gt1151->inputdev, ABS_MT_POSITION_Y, y_coordinate);}//处理松开的点for(i = 0; i < GTP_MAX_TOUCH; i++){//上一次处于按下状态而此次处于松开状态if((!now_touch_state[i]) && (gt1151->last_touch_state[i])){//触摸IDphy_id = i;//上报触摸信息input_mt_slot(gt1151->inputdev, phy_id);input_mt_report_slot_state(gt1151->inputdev, MT_TOOL_FINGER, false);}}//记录此次触摸状态memcpy(gt1151->last_touch_state, now_touch_state, sizeof(gt1151->last_touch_state));//进行单点触摸模拟,并上报触摸点数input_mt_report_pointer_emulation(gt1151->inputdev, true);//上报完成,进行事件同步input_sync(gt1151->inputdev);//清除触摸屏中断标志clr_intr = 0x00;gt1151_write(gt1151, GT1151Q_GSTID_REG, &clr_intr, 1);//返回IRQ_HANDLED,表示中断被成功处理return IRQ_HANDLED;
}static int gt1151_reset(gt1151_t *gt1151)
{uint8_t data;if(gt1151->client->addr != 0x14){printk("address bad\r\n");return -EINVAL;}//进行软件复位data = 0x02;gt1151_write(gt1151, 0X8040, &data, 1);mdelay(100);data = 0x0;gt1151_write(gt1151, 0X8040, &data, 1);mdelay(100);return 0;
}static int32_t gt1151_config(gt1151_t *gt1151)
{uint16_t checksum;int i;int result;uint8_t buffer[5];uint8_t *config = gt1151->gt1151_config;uint16_t cfg_len = GT1151_HW_CONFIG_LEN;//读取产品IDgt1151_read(gt1151, GT1151_PRODUCT_ID_REG, buffer, 4);buffer[4] = '\0';printk("gt1151 ID: %s\r\n", (char*)buffer);//加载配置数据result = gt1151_read(gt1151, GT1151_CONFIG_REG, config, cfg_len);if(result != 0){printk("load config failed\r\n");return result;}//配置触摸屏尺寸config[1] = gt1151->x_range & 0xFF;config[2] = gt1151->x_range >> 8;config[3] = gt1151->y_range & 0xFF;config[4] = gt1151->y_range >> 8;//计算和校验checksum = 0;for (i = 0; i < (cfg_len - 3); i += 2) checksum += ((uint16_t)config[i] << 8) + config[i+1];checksum = 0 - checksum;config[cfg_len-3] = (uint8_t)(checksum >> 8);config[cfg_len-2] = (uint8_t)(checksum & 0xFF);config[cfg_len-1] = 0x01;//更新配置数据return gt1151_write(gt1151, GT1151_CONFIG_REG, config, cfg_len);
}static int gt1151_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int result;uint32_t irq_flags;gt1151_t *gt1151;printk("%s\r\n", __FUNCTION__);//分配gt1151设备gt1151 = devm_kmalloc(&client->dev, sizeof(gt1151_t), GFP_KERNEL);if(!gt1151){printk("malloc mem failed\r\n");return -ENOMEM;}memset(gt1151, 0, sizeof(gt1151_t));//在client中设置驱动私有数据client->dev.driver_data = gt1151;gt1151->client = client;//尺寸默认800*480gt1151->x_range = 800;gt1151->y_range = 480;//获取GPIO号gt1151->rst_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);if(gt1151->rst_pin < 0){printk("get reset gpio failed\r\n");return gt1151->rst_pin;}gt1151->intr_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);if(gt1151->intr_pin < 0){printk("get irq gpio failed\r\n");return gt1151->intr_pin;}//申请GPIOresult = devm_gpio_request(&client->dev, gt1151->rst_pin, "gt1151-reset");if(result != 0){printk("request reset gpio failed\r\n");return result;}result = devm_gpio_request(&client->dev, gt1151->intr_pin, "gt1151-irq");if(result != 0){printk("request irq gpio failed\r\n");return result;}//获取尺寸of_property_read_u32(client->dev.of_node, "x-size", &gt1151->x_range);of_property_read_u32(client->dev.of_node, "y-size", &gt1151->y_range);printk("size:%d*%d\r\n", gt1151->x_range, gt1151->y_range);//复位gt1151result = gt1151_reset(gt1151);if(result != 0){printk("reset failed\r\n");return result;}//配置gt1151result = gt1151_config(gt1151);if(result != 0){printk("config failed\r\n");return result;}//分配输入设备gt1151->inputdev = devm_input_allocate_device(&client->dev);if(!gt1151->inputdev){printk("alloc input device failed\r\n");return -ENOMEM;}//初始化输入设备gt1151->inputdev->name = client->name;gt1151->inputdev->id.bustype = BUS_I2C;// // input_mt_init_slots 函数的 flags 为 INPUT_MT_DIRECT 会设置此事件// __set_bit(EV_KEY, gt1151->inputdev->evbit);// // input_set_abs_params 函数会设置此事件// __set_bit(EV_ABS, gt1151->inputdev->evbit);// // input_mt_init_slots 函数的 flags 为 INPUT_MT_DIRECT 会设置此按键// __set_bit(BTN_TOUCH, gt1151->inputdev->keybit);//  // input_mt_init_slots 函数的 flags 为 INPUT_MT_DIRECT 会设置此位//__set_bit(INPUT_PROP_DIRECT, gt1151->inputdev->propbit);input_set_abs_params(gt1151->inputdev, ABS_X, 0, gt1151->x_range, 0, 0);input_set_abs_params(gt1151->inputdev, ABS_Y, 0, gt1151->y_range, 0, 0);input_set_abs_params(gt1151->inputdev, ABS_MT_POSITION_X, 0, gt1151->x_range, 0, 0);input_set_abs_params(gt1151->inputdev, ABS_MT_POSITION_Y, 0, gt1151->y_range, 0, 0);// result = input_mt_init_slots(gt1151->inputdev, GTP_MAX_TOUCH, 0);//flags为INPUT_MT_DIRECT会设置EV_KEY、BTN_TOUCH事件,并将ABS_MT_POSITION_X和ABS_MT_POSITION_Y拷贝到ABS_X和ABS_Yresult = input_mt_init_slots(gt1151->inputdev, GTP_MAX_TOUCH, INPUT_MT_DIRECT);if(result != 0){printk("mt init failed\r\n");return result;}//注册输入设备result = input_register_device(gt1151->inputdev);if(result != 0){printk("input register failed\r\n");return result;}//设置中断引脚为输入gpio_direction_input(gt1151->intr_pin);//获取中断触发方式irq_flags = irq_get_trigger_type(gt1151->client->irq);if(irq_flags == IRQF_TRIGGER_NONE)irq_flags = IRQF_TRIGGER_FALLING;//中断处理过程中暂时关闭此中断irq_flags |= IRQF_ONESHOT;//注册中断处理函数result = devm_request_threaded_irq(&client->dev, client->irq, NULL, gt1151_irq_thread, irq_flags, client->name, gt1151);if(result != 0){printk("request irq failed\r\n");input_unregister_device(gt1151->inputdev);return result;}return 0;
}static int gt1151_remove(struct i2c_client *client)
{gt1151_t *gt1151;printk("%s\r\n", __FUNCTION__);gt1151 = client->dev.driver_data;input_unregister_device(gt1151->inputdev);return 0;
}static const struct i2c_device_id gt1151_id[] = {//采用设备树匹配,这里可以为空{"atk-gt1151", 0},{ /* Sentinel */ }
};static const struct of_device_id gt1151_of_match[] = {{ .compatible = "atk-gt1151"},{ /* Sentinel */ }
};static struct i2c_driver gt1151_driver = {.probe = gt1151_probe,.remove = gt1151_remove,.driver = {.owner = THIS_MODULE,.name = "gt1151",.of_match_table = gt1151_of_match, },//I2C驱动的此项参数不能为NULL.id_table = gt1151_id, 
};static int __init gt1151_init(void)
{int result;printk("%s\r\n", __FUNCTION__);result = i2c_add_driver(&gt1151_driver);if(result != 0){printk("add gt1151 driver failed\r\n");return result;}return 0;
}static void __exit gt1151_exit(void)
{printk("%s\r\n", __FUNCTION__);i2c_del_driver(&gt1151_driver);
}module_init(gt1151_init);
module_exit(gt1151_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("gt1151 driver");

驱动测试程序编写

驱动测试程序基于tslib进行编写,在编写驱动测试程序前需要完成对tslib的移植,确保编译工具链中有tslib的库文件和头文件,根文件系统中有tslib的库文件,在连接时还需要 -lts 去连接tslib的库文件。驱动测试程序的完整代码如下所示:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/time.h>
#include <getopt.h>
#include <errno.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include "tslib.h"int main(void)
{int i;int result;int32_t max_slots;struct tsdev *ts;struct ts_sample_mt *samp_mt[1];struct input_absinfo slot;//打开触摸屏设备,并初始化tslibts = ts_setup(NULL, 0);if(!ts){printf("tslib setup failed\r\n");return -1;}//获取触摸屏信息if(ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0){ts_close(ts);printf("get device info failed\r\n");return -1;}max_slots = slot.maximum + 1 - slot.minimum;//根据触摸点数分配缓存触摸点的内存samp_mt[0] = malloc(max_slots*sizeof(struct ts_sample_mt));if(!samp_mt[0]){ts_close(ts);printf("alloc memory failed\r\n");return -1;}while(1){result = ts_read_mt(ts, samp_mt, max_slots, 1);if (result < 0) {printf("ts_read_mt err\n");ts_close(ts);free(samp_mt[0]);return -1;}for (i = 0; i < max_slots; i++){if (samp_mt[0][i].valid){printf("slot %d\tid=%d\tx=%d\ty=%d\r\n",samp_mt[0][i].slot, samp_mt[0][i].tracking_id, samp_mt[0][i].x, samp_mt[0][i].y);}}}
}

上机测试

  1. 根据硬件原理图对设备树进行修改,然后用命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,并用新的设备树启动目标板。
  2. 从这里下载代码并进行编译,然后将编译出来的.ko文件和.out文件拷贝到目标板根文件系统的root目录中
  3. 执行命令insmod gt1151.ko加载触摸屏驱动
    在这里插入图片描述
  4. 执行命令./tslib_app.out运行测试程序,此时用手指触摸触摸屏,便可看到输出的触摸点信息在这里插入图片描述

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

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

相关文章

【Java】springboot

文章目录 Spingboot1、起步依赖2、构建springboot工程jar包3、springboot配置文件4、多环境配置5、maven和boot多环境兼容问题6、配置文件分类7、springboot整合mybatis Spingboot springboot用来简化spring的初始搭建以及开发过程。 比方说&#xff0c;创建一个springmvc程序…

Ps2022版DR5插件扩展窗口不展示及未正确签署等问题修复

前言 最近在安装DR5的时候遇到了一些报错问题&#xff0c;翻看了几篇文章找了一些实质性的方案&#xff0c;亲测有效&#xff0c;有同样问题的小伙伴自己对号入座哈。 窗口扩展不显示问题 问题 很多人第一次安装DR5时会发现这个【窗口-扩展】是灰色的&#xff0c;且没有DR5…

class087 动态规划中根据数据量猜解法的技巧【算法】

class087 动态规划中根据数据量猜解法的技巧【算法】 2023-12-24 14:36:06 算法讲解087【必备】动态规划中根据数据量猜解法的技巧 code1 打 怪 兽 // 贿赂怪兽 // 开始时你的能力是0&#xff0c;你的目标是从0号怪兽开始&#xff0c;通过所有的n只怪兽 // 如果你当前的能力…

35c3 krautflare

参考这篇文章可以彻底了解本题的漏洞所在 https://xz.aliyun.com/t/6527 由于Math.expm1经过patch以后的返回值不可能是-0&#xff0c;但是patch的地方是在typer优化中&#xff0c;所以实际上如果没有优化的话是可以返回-0的&#xff0c;这就意味着如果我们先不停地Math.expm1…

【C++】unordered_set/unordered_multiset/unordered_map/unordered_multimap

我们下面来学习C的另外两个容器&#xff1a;unordered_set和unordered_map 目录 一、unordered系列关联式容器 二、unordered_map 2.1 unordered_map的介绍 2.2 unordered_map的接口说明 2.2.1 unordered_map的构造 2.2.2 unordered_map的容量 2.2.3 unordered_map的迭…

java的XWPFDocument3.17版本学习

maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version> </dependency> 测试类&#xff1a; import org.apache.poi.openxml4j.exceptions.InvalidFormatExcep…

探索应用程序的指路明灯:Route 和 Router 入门指南(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

一站式指南:第 377 场力扣周赛的终极题解

比赛详情 比赛地址 题目一很简单题目二主要是题目长了点&#xff0c;其实解法很常规(比赛后才意识到)题目三套用Dijkstra算法题目四没时间解答水平还有待提升(其实就是需要灵活组合运用已知的算法&#xff0c;有点类似大模型的Agent) 题解和思路 第一题&#xff1a;最小数字…

信号与线性系统翻转课堂笔记10——傅里叶变换的性质

信号与线性系统翻转课堂笔记10——傅里叶变换的性质 The Flipped Classroom10 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&…

Echarts社区推荐

Apache Echarts官方示例中&#xff0c;有的demo并不能完全符合我们的需求&#xff0c;下面推荐几个Echarts社区&#xff0c;以便快速搭建项目。 1. isqqw 官方地址 &#xff1a;https://www.isqqw.com/ 2. makepie 官方地址 &#xff1a;https://www.makeapie.cn/echarts 3. P…

神经网络:深度学习优化方法

1.有哪些方法能提升CNN模型的泛化能力 采集更多数据&#xff1a;数据决定算法的上限。 优化数据分布&#xff1a;数据类别均衡。 选用合适的目标函数。 设计合适的网络结构。 数据增强。 权值正则化。 使用合适的优化器等。 2.BN层面试高频问题大汇总 BN层解决了什么问…

2024年【陕西省安全员C证】考试总结及陕西省安全员C证复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 陕西省安全员C证考试总结考前必练&#xff01;安全生产模拟考试一点通每个月更新陕西省安全员C证复审考试题目及答案&#xff01;多做几遍&#xff0c;其实通过陕西省安全员C证模拟考试题库很简单。 1、【多选题】下列…