多点电容触摸(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
基于源码移植
- 解压 tslib ,并进入源码目录
tar -vxf tslib-1.21.tar.xz
cd tslib-1.21/
- 配置 tslib
#--host=arm-none-linux-gnueabihf编译器前缀
#--prefix=/安装到目标机的目录
./configure --host=arm-none-linux-gnueabihf --prefix=/
- 编译 tslib
make
- 安装 tslib
#会在当前目录下创建一个tmp目录,并将此目录作为目标机的跟目录进行安装
make install DESTDIR=$PWD/tmp
- 将安装后得到的头文件和库文件拷贝到工具链中(如果不基于 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
- 将安装后得到的文件拷贝到目标板跟文件系统中
#../../../../rootfs/是nfs根文件系统目录
cp -r tmp/* ../../../../rootfs/
基于 tslib 开发应用程序
- 使用 ts_setup 打开触摸屏设备,并初始化 tslib
- 使用 ioctl(int ts_fd(struct tsdev *ts), EVIOCGABS(ABS_MT_SLOT), struct input_absinfo *slot) 获取触摸屏的 slot 信息,并计算触摸点数
- 使用 ts_read_mt 读取触摸屏数据
- 最后通过 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文件启动系统
驱动代码编写
驱动代码主要包括以下几个部分:
- 初始化GT1151硬件
- 初始化并注册触摸屏输入设备
- 注册触摸屏中断
- 在触摸屏中断中上报触摸点信息
完整的驱动代码如下所示:
#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(>1151->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", >1151->x_range);of_property_read_u32(client->dev.of_node, "y-size", >1151->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(>1151_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(>1151_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);}}}
}
上机测试
- 根据硬件原理图对设备树进行修改,然后用命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,并用新的设备树启动目标板。
- 从这里下载代码并进行编译,然后将编译出来的.ko文件和.out文件拷贝到目标板根文件系统的root目录中
- 执行命令insmod gt1151.ko加载触摸屏驱动
- 执行命令./tslib_app.out运行测试程序,此时用手指触摸触摸屏,便可看到输出的触摸点信息