10.2多点触摸屏驱动
2023-12-24 17:45:12
多点电容触摸(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;
//触摸点 slot
int slot;
//ID
int 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 5
typedef 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 >= 1
GT1151Q_TP1_REG,
#endif
#if GTP_MAX_TOUCH >= 2
GT1151Q_TP2_REG,
#endif
#if GTP_MAX_TOUCH >= 3
GT1151Q_TP3_REG,
#endif
#if GTP_MAX_TOUCH >= 4
GT1151Q_TP4_REG,
#endif
#if GTP_MAX_TOUCH >= 5
GT1151Q_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;
else
return -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来区分slot
phy_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]))
{
//触摸ID
phy_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;
//读取产品ID
gt1151_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*480
gt1151->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;
}
//申请GPIO
result = 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);
//复位gt1151
result = gt1151_reset(gt1151);
if(result != 0)
{
printk("reset failed\r\n");
return result;
}
//配置gt1151
result = 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_Y
result = 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;
//打开触摸屏设备,并初始化tslib
ts = 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运行测试程序,此时用手指触摸触摸屏,便可看到输出的触摸点信息
文章来源:https://blog.csdn.net/lf282481431/article/details/135025602
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!