文章目录
- 一、设备树
- 二、驱动程序
- 三、测试
- 四、编译进内核
- 1. 拷贝文件
- 2. 修改对应的 Makefile
- 3. 编译运行
- 4.测试
一、设备树
记得注释掉共用的引脚(有好几处)
在pinctrl_tsc节点下添加:
pinctrl_tsc: tscgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x10B0 /* TSC_INT*/MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0 /* TSC_RST*//*MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0*/>;};
在&i2c2节点下添加:
/* 触摸 */gt911:gt911@5d {compatible = "goodix,gt9111","goodix,gt9xx";reg = <0x5d>;pinctrl-names = "default";//pinctrl-0 = <&pinctrl_tsc//&pinctrl_tsc_reset >; pinctrl-0 = <&pinctrl_tsc>; interrupt-parent = <&gpio1>;interrupts = <9 2>; /* 使用的是9号中断 因为引脚是9 触发方式是2 下降沿触发 */rst-gpio = <&gpio5 9 GPIO_ACTIVE_LOW>;irq-gpio = <&gpio1 9 GPIO_ACTIVE_LOW>;goodix,cfg-group0 = [41 20 03 E0 01 05 BD 00 01 0F 14 0F 5F 32 03 05 00 00 00 00 00 00 00 18 1A 1C 14 89 29 0B 3A 38 8F 04 00 00 01 21 02 1D 00 01 00 00 00 00 00 00 00 00 00 28 50 94 C5 02 08 00 00 04 80 2A 00 80 31 00 83 38 00 8C 41 00 9B 4A 00 9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 14 12 10 0E 0C 0A 08 06 04 02 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 04 06 08 0A 0F 10 12 16 18 1C 1D 1E 1F 20 21 22 FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B7 01];
/* 可以使用 但是触摸不太准 */
/*goodix,cfg-group0 = [5A 20 03 E0 01 05 3D 00 02 08 2808 5A 46 03 05 00 00 00 00 00 0004 04 04 04 03 88 29 0A 4B 4D 0C08 00 00 00 21 02 1D 00 01 00 0000 00 00 00 00 00 00 46 64 94 D502 07 00 00 04 83 48 00 77 4D 006D 53 00 64 59 00 5A 60 00 5A 0000 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 0000 00 02 04 06 08 0A 0C 0E 10 1214 FF FF FF FF 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 0002 04 06 08 0F 10 12 16 18 1C 1D1E 1F 20 21 22 FF FF FF FF FF FFFF FF FF 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 D6 01];*/status = "okay";};
二、驱动程序
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>//#include "gt9.h"/*** file name:gt9xx* date: 2021-08-16 22:14* version:1.0* author:luatao* describe:gt9xx device drive*/
#define GT_CTRL_REG 0X8040 /* GT9147控制寄存器 */
#define GT_MODSW_REG 0X804D /* GT9147模式切换寄存器 */
#define GT_CFGS_REG 0X8047 /* GT9147配置起始地址寄存器 */
#define GT_CHECK_REG 0X80FF /* GT9147校验和寄存器 */
#define GT_PID_REG 0X8140 /* GT9147产品ID寄存器 */#define GT_GSTID_REG 0X814E /* GT9147当前检测到的触摸情况 */
#define GT_TP1_REG 0X814F /* 第一个触摸点数据地址 */
#define GT_TP2_REG 0X8157 /* 第二个触摸点数据地址 */
#define GT_TP3_REG 0X815F /* 第三个触摸点数据地址 */
#define GT_TP4_REG 0X8167 /* 第四个触摸点数据地址 */
#define GT_TP5_REG 0X816F /* 第五个触摸点数据地址 */
#define MAX_SUPPORT_POINTS 5 /* 最多5点电容触摸 *//* 设备结构体 自定义 */
struct gt911_dev{int irq_pin, reset_pin; /* 中断和复位IO */int irqnum; /* 中断号 */// void *private_date; /* 私有数据 */struct input_dev *input; /* input结构体 */struct i2c_client *client; /* i2c客户端 */};/* 定义一个设备结构体 */
struct gt911_dev gt911; /*gt911 设备 */
/*
const unsigned char GT911_CT[]=
{0x5A,0x20,0x03,0xE0,0x01,0x05,0x3D,0x00,0x02,0x08,0x28,0x08,0x5A,0x46,0x03,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x04,0x04,0x03,0x88,0x29,0x0A,0x4B,0x4D,0x0C,0x08,0x00,0x00,0x00,0x21,0x02,0x1D,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x64,0x94,0xD5,0x02,0x07,0x00,0x00,0x04,0x83,0x48,0x00,0x77,0x4D,0x00,0x6D,0x53,0x00,0x64,0x59,0x00,0x5A,0x60,0x00,0x5A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10,0x12,0x14,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x06,0x08,0x0F,0x10,0x12,0x16,0x18,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD6,0x01,
};*//* 从gt911读取多个寄存器数据 @param - *dev : gt911设备 @param - reg : 要读取的寄存器首地址@param - *buf : 读取到的数据 @param - len : 要读取的数据长度 @return :操作结果
*/
static int gt911_read_regs(struct gt911_dev *dev, u16 reg, u8 *buf, int len)
{int ret = 0;u8 regdata[2]; // 寄存器数据struct i2c_msg msg[2]; /* 传输的消息 读的命令 */struct i2c_client *client = (struct i2c_client *)dev->client; /* 私有数据 *//* gt911 寄存器长度为2个字节 */regdata[0] = (reg >> 8) & 0xFF; // 高8位regdata[1] = reg & 0xFF; // 低8位 /* msg[0] 为发送要读取的首地址 */msg[0].addr = client->addr; /* 器件地址 */msg[0].flags = 0; /* 标记为发送数据 */msg[0].buf = ®data[0]; /* 要读取数据的首地址 */msg[0].len = 2; /* reg长度 *//* msg[1]读取数据 */msg[1].addr = client->addr; /* 器件地址 */msg[1].flags = I2C_M_RD; /* 标记为读取数据 */msg[1].buf = buf; /* 读取数据缓冲区 */msg[1].len = len; /* 读取数据长度 */ret = i2c_transfer(client->adapter, msg, 2); /* 向总线发送2个消息 */if(ret == 2){ /* 传输成功 */ret = 0;}else{printk("i2c_transfer failed!\r\n");return -EREMOTEIO;}return ret;
}/* 从gt911多个寄存器写入数据 @param - *dev : gt911设备 @param - reg : 要写入的寄存器首地址@param - *buf : 写入的数据缓冲区@param - len : 要写入的数据长度 @return :操作结果
*/
static s32 gt911_write_regs(struct gt911_dev *dev, uint16_t reg, uint8_t *buf, int len)
{uint8_t buf1[256];struct i2c_msg msg; /* 传输的消息 */struct i2c_client *client = (struct i2c_client *)dev->client; buf1[0] = (reg >> 8) & 0xFF; /* 寄存器首地址 */buf1[1] = reg & 0xFF; memcpy(&buf1[2], buf, len); /* 要写入的数据拷贝到数据buf1中 *//* msg处理数据 */msg.addr = client->addr; /* 器件地址 */msg.flags = 0; /* 标记为写入数据 */msg.buf = buf1; /* 要写入的数据缓冲区 */msg.len = len + 2; /* 写入的数据长度 */return i2c_transfer(client->adapter, &msg, 1); /* 向总线发送1个消息 */
}/* 从gt911读取指定寄存器值 读取一个寄存器@param - *dev : ap3216设备 @param - reg : 要读取的寄存器@return :读取到的寄存器值
*///static unsigned char gt911_read_reg(struct gt911_dev *dev, u8 reg)
//{// struct i2c_client *client = (struct i2c_client *)dev->client; /* 私有数据 *///return i2c_smbus_read_byte_data(client, reg); /* 读取一个字节数据 */
//}/* 向gt911指定寄存器写入指定的值,写一个寄存器 @param - *dev : ap3216设备 @param - reg : 要写入的寄存器@param - data : 要写入的值 @return :无
*/
static void gt911_write_reg(struct gt911_dev *dev, uint16_t reg, uint8_t data)
{uint8_t buf = 0;buf = data;gt911_write_regs(dev, reg, &buf, 1); /* 调用写入多个寄存器的方法 */
}/* 触摸中断处理函数 */
static irqreturn_t gt911_irq_handler(int irq, void *dev_id)
{u8 touch_num = 0, status, havakey ; // 触摸点的数量最大5点触摸 数据是否准备好 是否有按键按下 int input_x, input_y, id = 0; // x,y坐标 触摸IDint ret = 0; // 返回值u8 data,touch_data[5]; // 触摸数据struct gt911_dev *dev = dev_id; // 触摸设备结构体/* 判断是否进入中断 *///printk("cd %s\r\n",__FUNCTION__);/* 读取坐标点寄存器 */ret = gt911_read_regs(dev, GT_GSTID_REG, &data, 1); /* 这一位数据的表示:bit7:1表示坐标(或按键)已经准备好,主控可以读取 0 表示未就绪,数据无效bit4:1表示有按键 0表示无按键(已经松开)bit3~0:屏上的坐标点个数 */if(data == 0x00){ /* 没有触摸数据*/goto fail;}else{ /* 统计触摸信息 */status = data >> 7; // 取最高位 havakey = (data >> 4) & 0x01; touch_num = data & 0x0f; // 只取低4位 }/* 单点触摸 不适用于多点触摸*/if(touch_num){ /* 有触摸按下 */gt911_read_regs(dev, GT_TP1_REG, touch_data, 5); // 读取第一个触摸点 连续读5个数据 id = touch_data[0] & 0x0F; if(id == 0){input_x = (touch_data[1] | (touch_data[2] << 8)) & 0x0fff; // x坐标input_y = (touch_data[3] | (touch_data[4] << 8)) & 0x0fff; // y坐标input_mt_slot(dev->input, id); // 产生ABS_MT_SLOT 事件 报告是哪个触摸点的坐标 input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true); // 指定手指触摸 连续触摸input_report_abs(dev->input, ABS_MT_POSITION_X, input_x); // 上报触摸点坐标信息 input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y); // 上报触摸点坐标信息 printk("x = %d, y = %d\r\n", input_x, input_y); //打印坐标信息 }}else if(touch_num == 0){ // 单点触摸释放 input_mt_slot(dev->input, id); /* 上报触摸点 */input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); // 关闭手指触摸 }input_mt_report_pointer_emulation(dev->input, true); input_sync(dev->input); /* 同步数据 数据上报完成 */data = 0x00; /* 向0x814E寄存器写0 不然就会一直进入中断 */gt911_write_regs(dev, GT_GSTID_REG, &data, 1); //写入fail:return IRQ_HANDLED;
}/* 申请IO并复位gt911 @param - *client : i2C控制器@param - *dev : 自定义的触摸设备@return :0:成功 其他负值 :失败
*/
static int gt911_ts_reset(struct i2c_client * client, struct gt911_dev *dev)
{int ret = 0;printk("cd %s\r\n",__FUNCTION__);/* 申请复位IO */if(gpio_is_valid(dev->reset_pin)){ // 判断gpio是否合法/* 申请复位 IO 并且默认输出高电平 */ret = devm_gpio_request_one(&client->dev, dev->reset_pin,GPIOF_OUT_INIT_LOW,"gt911 reset");if(ret){ // 申请失败printk("request reset_pin failed!\r\n");return ret;}}/* 申请中断IO*/if(gpio_is_valid(dev->irq_pin)){ // 判断gpio是否合法/* 申请复位 IO 并且默认输出高电平 */ret = devm_gpio_request_one(&client->dev, dev->irq_pin, // 引脚编号GPIOF_OUT_INIT_LOW, // 默认的电平状态 "gt911 irq"); // 名字 随便if(ret){ // 申请失败printk("request irq_pin failed!\r\n");return ret;}}/* 初始化gt911 */gpio_set_value(dev->reset_pin, 0); /* 复位 */msleep(10);gpio_set_value(dev->reset_pin, 1); /* 停止复位 */msleep(10);gpio_set_value(dev->irq_pin, 0); /* 拉低INT引脚 */msleep(50);gpio_direction_input(dev->irq_pin); /* INT引脚设置为输入 *//* 有一个地址的判断 */return 0;
}/* gt911 中断初始化 @param - *client : i2C控制器@param - *dev : 自定义的触摸设备@return :0:成功 其他负值 :失败
*/
static int gt911_ts_irq(struct i2c_client * client, struct gt911_dev *dev)
{int ret = 0; // 返回值/* 申请中断 */ret = devm_request_threaded_irq(&client->dev,client->irq,NULL,gt911_irq_handler,IRQF_TRIGGER_FALLING | IRQF_ONESHOT,client->name,>911);if(ret){dev_err(&client->dev, "Unable to request touchscreen IRQ.\r\n");return ret;}printk("gt911 handler irq number: %d\r\n", client->irq); // 打印出中断号return 0;
}/* 发送gt911配置参数 @param - *dev : 自定义的触摸设备@param - mode : 0 :参数不保存到flash 1:参数保存到flash@return :无
*/
/* 屏幕配置信息 */
/*
void gt911_send_cfg(void)
{u8 regdata[186] = {0};unsigned int i = 0,ret = 0;u8 softVersion = 0; // 软件版本号 u8 gt911_id[6] = {0}; // 产品IDu8 irqmode = 0; // 中断触发方式u8 crc = 0; // 校验和 // 读软件版本号 gt911_read_regs(>911, GT_CFGS_REG, &softVersion,1); printk("soft version:%d\r\n", softVersion);// 读取产品ID printk("ID: ");gt911_read_regs(>911, GT_PID_REG, gt911_id,6); for(i = 0; i< 6;i++)printk("%d ", gt911_id[i]);printk("\r\n");// 读中断触发方式 gt911_read_regs(>911, GT_MODSW_REG, &irqmode,1); printk("irqmode:%d\r\n", irqmode);// 读取184个寄存器 gt911_read_regs(>911, GT_CFGS_REG, regdata,184); for(i= 0; i < 186; i++){printk("%#X ", regdata[i]);if(i < 184){crc += regdata[i]; // 校验和 }}printk("\r\n");crc = (~crc) + 1;printk("crc:%d\r\n", crc);// 软件复位 gt911_write_reg(>911, GT_CTRL_REG, 2);// 配置186个寄存器 // 获取设备树的配置信息 ret = of_property_read_u8_array(gt911.client->dev.of_node, "goodix,cfg-group0", regdata, 186);if (ret < 0) {printk("goodix,cfg-group0 property read failed\r\n");} else {printk("reg data:\r\n");for(i = 0; i < 186; i++){printk("%X ", regdata[i]);}printk("\r\n");}gt911_write_regs(>911, GT_CFGS_REG, regdata, sizeof(regdata)); gt911_write_reg(>911,GT_CTRL_REG,2);msleep(100);
}
*//* i2C驱动的probe函数 ,当驱动与设备匹配以后此函数就会执行 */
static int gt911_probe(struct i2c_client *client, const struct i2c_device_id *id)
{u8 ret = 0; gt911.client = client; printk("gt911 driver and device has match!\r\n"); // 提示信息 /* 1. 获取设备树中的中断和复位引脚 */gt911.irq_pin = of_get_named_gpio(client->dev.of_node, "irq-gpio", 0);gt911.reset_pin = of_get_named_gpio(client->dev.of_node, "rst-gpio", 0);printk("get gpios success!\r\n");/* 2. 复位gt911 申请GPIO并复位 */ret = gt911_ts_reset(client, >911);if(ret < 0){printk("gt911 reset failed!\r\n");goto fail;}/* 3. 初始化gt911 */gt911_write_reg(>911, GT_CTRL_REG, 2); /* 软复位 */mdelay(100);gt911_write_reg(>911, GT_CTRL_REG, 0); /* 停止软复位 */mdelay(100);/* 4. input 注册设备*/gt911.input = devm_input_allocate_device(&client->dev);if(!gt911.input){return -ENOMEM;}/* 初始化input */gt911.input->name = client->name;gt911.input->id.bustype = BUS_I2C;gt911.input->dev.parent = &client->dev;/* 设置input设备需要上报事件类型和按键值*/__set_bit(EV_KEY, gt911.input->evbit);__set_bit(EV_ABS, gt911.input->evbit);__set_bit(BTN_TOUCH, gt911.input->keybit);/* 设置input设备 需要上报的绝对坐标 */input_set_abs_params(gt911.input, ABS_X, 0, 800, 0, 0);input_set_abs_params(gt911.input, ABS_Y, 0, 480, 0, 0);input_set_abs_params(gt911.input, ABS_MT_POSITION_X,0, 800, 0, 0);input_set_abs_params(gt911.input, ABS_MT_POSITION_Y,0, 480, 0, 0); /* 初始化多点电容触摸的slots*/ret = input_mt_init_slots(gt911.input, MAX_SUPPORT_POINTS, 0); // 初始化 MT 的输入 slots 触摸点的数量if(ret != 0){printk("MT init failed!\r\n");goto fail;}/* 注册input */ret = input_register_device(gt911.input);if(ret){printk("input register failed!\r\n");goto fail;}/* 最后初始化中断 */ret = gt911_ts_irq(client, >911);if(ret < 0){printk("init irq failed!\r\n");goto fail;}return 0;fail:return ret;
}/* i2c驱动后的remove函数 */
int gt911_remove(struct i2c_client *client)
{/* 释放输入设备 */input_unregister_device(gt911.input);printk("gt911 drive unregsister ok !\r\n");return 0;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id gt911_id[] = {{"goodix,gt911", 0},{}
};
/* 匹配列表 */
static const struct of_device_id gt911_of_match[] = {{.compatible = "goodix,gt9111"},{/* Sentinel */}
};/* i2c驱动结构体 */
struct i2c_driver gt911_i2c_driver = {.driver = {.owner = THIS_MODULE,.name = "gt911", /* 驱动名字 用于和设备匹配 适用于没有设备树的情况*/.of_match_table =gt911_of_match, /* 设备树匹配列表 */},.probe =gt911_probe,.remove =gt911_remove,.id_table = gt911_id, /* id配置列表 */
};module_i2c_driver(gt911_i2c_driver);/* LICENSE 和 AUTHOR 信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luatao");
三、测试
用手指按下屏幕,会打印出触摸的坐标点 (不需要可在程序注释掉)
四、编译进内核
1. 拷贝文件
将gt911.c拷贝drivers/input/touchscreen 目录下
cp gt911.c /home/luatao/linux/linux/luatao_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_luatao/drivers/input/touchscreen/ -f
2. 修改对应的 Makefile
修改 drivers/input/touchscreen 目录下的 Makefile,在最下面添加下面一行:
obj-y += gt911.o
3. 编译运行
修改完成以后重新编译 linux 内核,然后用新的 zImage 启动开发板。
如果驱动添加成功的话系统启动的时候就会输出如图
4.测试
当我们点击屏幕的时候,会打印
说明我们的驱动没有问题。
但是输入
ts_test_mt
的时候可能会报错
这有很多中情况,可能是linux版本和tslib版本不一致。
我这种情况是
/dev/input/event2
变为了
/dev/input/event1
改过来再试
就可以了。