当前位置: 首页 > news >正文

Linux驱动开发(16):输入子系统–电容触摸驱动实验

有关电容触摸的基础知识内容可以参考野火STM32相关教程,这里只介绍电容触摸驱动的相关内容。

本章配套源码、设备树以及更新固件位于“~/embed_linux_driver_tutorial_imx6_code/linux_driver/touch_scream_GTxxx”目录下。

触摸面板通过双面胶粘在显示屏上,他们在硬件上没有关联,通常情况下我们会设置触摸面板的“分辨率”与显示屏的分辨率一致。触摸芯片自动完成触摸信息的采集和处理,我们要做的就是配置触摸芯片、读取触点坐标。

常见的触摸接口原理图如下所示:

02|

触摸芯片有四个引脚连接到了单片机,其中scl、sda是IIC通信引脚。RSTN是触摸的复位引脚,配置触摸芯片时也会用到。INT是中断引脚,默认高电平。发生触摸后该引脚会发送一个低电平的脉冲信号。

从硬件连接我们可以大致得知电容触摸驱动要用到IIC设备驱动、中断驱动。由于电容触摸驱动要向应用层提交触摸消息所以还会用到输入子系统

IIC设备驱动、中断驱动和输入子系统在之前章节已经介绍,我们现在可以自己从零写一个触摸驱动,但是我们不这样做,因为大多情况下触摸芯片厂商已经编写好了触摸驱动,我们只需要稍加修改就可以使用。

我们使用的触摸芯片型号如下,4.3寸使用的GT5688,5寸使用的是GT917S或GT9157,7寸屏使用的是GT911。它们都属于GOODIX公司生产的触摸芯片,厂商已经写好了触摸驱动并且添加默认添加到了内核中。源码位于“内核根目录/drivers/input/touchscreen/goodix.c”这 个驱动程序并不能完全适配我们使用的这些触摸型号,所以我们需要稍加修改才能使用。下面我们结合源码简单介绍驱动实现原理以及如何根据实际需要修改驱动。

1. 添加设备树节点

设备树节点有两个任务,第一,添加触摸使用的引脚,第二,添加IIC设备节点。介绍如下。

根据之前讲解,触摸芯片共占用4个IO口。这四个IO如下所示。

引脚

功能

SNVS_TAMPER9引脚

复用为GPIO5_IO09用作触摸芯片的irq引脚,接收触摸中断

LCD_RESET引脚

复用为GPIO3_IO04用作触摸芯片的复位引脚

UART4_TX_DATA引脚

复用为I2C1_SCL,用作IIC1的SCL引脚

UART4_RX_DATA引脚

复用为I2C1_SDA,用作IIC1的 SDA引脚

知道了各个引脚的复用功能,在设备树中把它们追加到iomuxc就很容易了,需要注意的是SNVS_TAMPER9引脚被复用为GPIO5_IO09,需要追加到iomuxc_snvs节点,以“SNVS”开头的引脚与普通引脚使用的驱动不同,特别注意要分开。源码如下:

在设备树中添加引脚信息:

&iomuxc {/*-----------其他内容省略-----------*/pinctrl_i2c1: i2c1grp {fsl,pins = <MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0>;};pinctrl_tsc_reset: tscresetgrp {fsl,pins = </* used for tsc reset */MX6UL_PAD_LCD_RESET__GPIO3_IO04         0x10b0>;};
};&iomuxc_snvs {pinctrl_tsc_irq: tsc_irq {fsl,pins = <MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09        0x4001b8b0>;};
};

触摸驱动作为一个IIC设备挂载在IIC1总线上,它和我们之前讲解的iic接口的OLED驱动一样,需要在IIC1设备节点下追加相应的子节点,不同的是这里用到的两个GPIO和一个中断,设备节点如下所示。

在iic1节点追加:

&i2c1 {/*--------------第一部分--------------*/clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;#address-cells = <1>;#size-cells = <0>;status = "okay";gtxx_tsc@5d {compatible = "fire,gt9xx_test";/*--------------第二部分--------------*/pinctrl-0 = <&pinctrl_tsc_reset>;pinctrl-1 = <&pinctrl_tsc_irq>;reg = <0x5d>;       -----------------①status = "okay";/*--------------第三部分--------------*//*gpio*/reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;irq-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>;/*--------------第四部分--------------*//*中断*/interrupt-parent = <&gpio5>;interrupts = <9 IRQ_TYPE_EDGE_FALLING>;irq-flags = <2>;                /*1:rising 2: falling*/};
};

结合以上内容简单介绍如下:

第一部分,定义IIC1的一些基本属性,主要包括 IIC1使用的引脚以及IIC1的scl的时钟频率,时钟频率的范围要参考触摸芯片手册,不能超出芯片最大支持的频率。我们使用的GTxxx触摸芯片最大支持400KHz,这里将频率设置为100KHz,满足要求。

第二部分,这里是添加的触摸驱动要使用的中断引脚以及复位引脚。标号①处是触摸芯片在IIC1总线上的地址

第三部分,添加使用的GPIO,这里使用GPIO子系统将触摸芯片使用的irq引脚、rest引脚复用为GPIO.

第四部分,添加中断相关内容,这里将触发方式设置为上升和下降沿触发,具体内容可参考中断章节,这里不再赘述。

2022年8月后购买的4.3寸以及5寸的屏幕触摸芯片都为gt1151,其i2c地址都是0x14,如果是这个日期之后购买的屏幕,需要修改通信地址为14,如下所示。如果是7寸屏幕则无需修改。

确认触摸芯片并修改地址:

     gtxx_tsc@14 {compatible = "fire,gt9xx_test";/*--------------第二部分--------------*/pinctrl-0 = <&pinctrl_tsc_reset>;pinctrl-1 = <&pinctrl_tsc_irq>;reg = <0x14>;       -----------------①status = "okay";/*--------------第三部分--------------*/

2. goodix官方触摸驱动讲解

由于goodix官方触摸驱动稍复杂,这里只讲解实现方法,以及如何简单修改驱动以适配多种触摸芯片,阅读本小节时推荐打开“内核根目录/drivers/input/touchscreen/goodix.c”源码。

2.1. 修改设备树匹配信息

和其他驱动类似,打开官方驱动后首先要找到“设备树匹配”相关内容。如下所示。

在iic1节点追加:

static const struct of_device_id goodix_of_match[] = {{ .compatible = "goodix,gt1151" },{ .compatible = "goodix,gt911" },{ .compatible = "goodix,gt9110" },{ .compatible = "goodix,gt912" },{ .compatible = "goodix,gt927" },{ .compatible = "goodix,gt9271" },{ .compatible = "goodix,gt928" },{ .compatible = "goodix,gt967" },---------------①{ }
};
MODULE_DEVICE_TABLE(of, goodix_of_match);
#endifstatic struct i2c_driver goodix_ts_driver = {   -----②.probe = goodix_ts_probe,.remove = goodix_ts_remove,// .id_table = goodix_ts_id, --------------------③.driver = {.name = "Goodix-TS",-------------------------④.acpi_match_table = ACPI_PTR(goodix_acpi_match),.of_match_table = of_match_ptr(goodix_of_match),.pm = &goodix_pm_ops,},
};

结合以上代码介绍如下。标号①,这里就是用于和设备树节点匹配的匹配值,我们将前面编写的设备树节点添加进去即可。标号②,这个就是i2c设备驱动结构体,它代表了一个I2C设备。标号③处是传统的匹配方式,我们不用可以屏蔽掉。标号④处是驱动的名字,如果内核也开启了GOODIX触摸驱动(默认是开启了) 这里需要修改驱动名字,例如我们例程中将其修改为“Goodix-TS-CHANGE”.

2.2. prob函数实现

.prob函数完成初始化工作,代码如下:

.prob函数:

static int goodix_ts_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct goodix_ts_data *ts;int error;/*------------------第一部分---------------*/dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); ------①if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {  -----②dev_err(&client->dev, "I2C check functionality failed.\n");return -ENXIO;}/*------------------第二部分---------------*/ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);if (!ts)return -ENOMEM;ts->client = client;i2c_set_clientdata(client, ts);init_completion(&ts->firmware_loading_complete);/*------------------第三部分---------------*/error = goodix_get_gpio_config(ts);  -------------③if (error)return error;if (ts->gpiod_int && ts->gpiod_rst) {/* reset the controller */error = goodix_reset(ts);--------------------④if (error) {dev_err(&client->dev, "Controller reset failed.\n");return error;}}error = goodix_i2c_test(client);----------------⑤if (error) {dev_err(&client->dev, "I2C communication failure: %d\n", error);return error;}error = goodix_read_version(ts);----------------⑥if (error) {dev_err(&client->dev, "Read version failed.\n");return error;}ts->chip = goodix_get_chip_data(ts->id);--------⑦dev_err(&client->dev, " goodix_get_chip_data \n");/*------------------第四部分---------------*/if (ts->gpiod_int && ts->gpiod_rst) {/* update device config */ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,"goodix_%d_cfg.bin", ts->id); --------⑧if (!ts->cfg_name)return -ENOMEM;error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,&client->dev, GFP_KERNEL, ts,goodix_config_cb);--------------------⑨if (error) {dev_err(&client->dev,"Failed to invoke firmware loader: %d\n",error);return error;}return 0;}else{error = goodix_configure_dev(ts);if (error)return error;}return 0;
}

prob函数较长,但是理解起来很简单,也没有什么需要修改的地方,结合源码简单介绍如下:

第一部分,进入.prob函数后做了一些简单的检查,比如,标号①处打印触摸设备的i2c地址。这个地址是在触摸的设备节点中设置的地址。标号②处检查是否支持IIC功能。

第二部分,为goodix_ts_data类型的结构体变量ts申请空间并初始化,在驱动中使用goodix_ts_data结构体保存触摸驱动信息。结构体原型如下。

.goodix_ts_data结构体:

struct goodix_ts_data {struct i2c_client *client;  //i2c 从设备结构体struct input_dev *input_dev; //输入设备结构体const struct goodix_chip_data *chip;  //goodix相关内容struct touchscreen_properties prop;   //未知内容unsigned int max_touch_num;     //做大支持的触摸点unsigned int int_trigger_type;  //触摸类型struct gpio_desc *gpiod_int;    //触摸中断引脚struct gpio_desc *gpiod_rst;    //触摸芯片复位引脚u16 id;                                         //触摸芯片u16 version;                    //版本const char *cfg_name;           //名字struct completion firmware_loading_complete;  //固件加载完成标志unsigned long irq_flags;        //中断标记
};

后面的初始化将会使用这个结构体。结构体成员含义见注释,这里不再赘述。接着回到.prob函数。

第三部分,完成一些基本的初始化。这部分内容调用一些以“goodix_"开头的函数,这些函数是goodix官方实现的一些函数,定义在该文件内,从函数名我们可以大致知道函数的功能,简单说明如下,标号③,获取触摸芯片rst和int使用的GPIO。标号④,复位触摸芯片。标号⑤,测试IIC,尝试与触摸芯片通信 。标号⑥,读取触摸芯片版本,后边会根据触摸芯片版本来初始化触摸芯片。标号⑦,根据标号⑥获取的触摸芯片型号指定触摸的一些配置参数。函数原型如下所示。

 goodix_get_chip_data函数:

    /*------------------第一部分------------------*/
static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
{switch (id) {case 1151:return &gt1x_chip_data;case 911:case 9271:case 9110:case 927:case 928:return &gt911_chip_data;case 912:case 9157:         // fire 新增return &gt9x_chip_data;case 917:         // fire 新增return &gt917_chip_data;case 5688:         // fire 新增return &gt5688_chip_data;case 967:return &gt967_chip_data;default:return &gt9x_chip_data;}
}/*------------------第二部分------------------*//*结构体原型*/
struct goodix_chip_data {u16 config_addr;int config_len;int (*check_config)(struct goodix_ts_data *, const struct firmware *);
};static const struct goodix_chip_data gt9x_chip_data = {.config_addr            = GOODIX_GT9X_REG_CONFIG_DATA,------①.config_len             = GOODIX_CONFIG_MAX_LENGTH,-------------②.check_config           = goodix_check_cfg_8,  -------------③
};/*fire 新增 ----*/
static const struct goodix_chip_data gt917_chip_data = {.config_addr            = GOODIX_GT917_REG_CONFIG_DATA,.config_len             = GOODIX_CONFIG_917_LENGTH,.check_config           = goodix_check_cfg_16,
};/*fire 新增 ----*/
static const struct goodix_chip_data gt5688_chip_data = {.config_addr            = GOODIX_GT917_REG_CONFIG_DATA,.config_len             = GOODIX_CONFIG_5688_LENGTH,.check_config           = goodix_check_cfg_16,
};/*------------------第三部分------------------*/
#define GOODIX_CONFIG_917_LENGTH    242    //fire 新增
#define GOODIX_CONFIG_5688_LENGTH   242    //fire 新增#define GOODIX_GT5688_REG_CONFIG_DATA       0x8050  //fire 新增
#define GOODIX_GT917_REG_CONFIG_DATA        0x8050  //fire 新增

结合源码介绍如下。

第一部分,goodix_get_chip_data函数实现,它很简单,仅仅根据芯片ID返回不同的goodix_chip_data结构体地址。goodix_chip_data结构体原型以及初始化实例如第二部分所示,该结构体共有三个参数,第一个用于指定触摸芯片的配置寄存器地址,这个地址是触摸芯片的内部地 址,不同触摸芯片有所不同,查找触摸手册即可,如第三部分所示。第二个参数用于指定配置信息的最大长度,不同触摸芯片配置信息长度是不同的,根据手册设置即可,这里也通过宏定义指出,如第三部分所示。第三个参数是一个函数指针,用于指定“校验”配置信息的函数。再想触摸芯片写入配置信息之前要校验配置信息。根据触摸芯 片的不同分为8位校验和16位校验。稍后会详细讲解校验函数。接着回到.prob函数。

第四部分,这部分内容是驱动程序的重点。从以上三部分可知,到目前为止我们初始化了触摸芯片使用的引脚并且能够与触摸芯片通信了。这部分的内容完成后续的中断的申请、输入设备的注册、触摸配置信息的读取与更新、触摸事件的上报工作。不过不必担心这部分呢内容几乎不需要我们去修改。

标号⑧处使用devm_kasprintf函数根据触摸芯片的ID合成触摸配置文件的文件名(以下简称为固件)。例如我们使用的GT911,它的ID为911,则触摸更新固件名为“goodix_911_cfg.bin”。

有关更新固件这里简单说明如下。通常情况下我们从供应商那里买到的触摸板已经正确配置了固件,如果买野火的屏幕(带触摸)默认也是配置好了触摸固件,无需进行修改。如果是公司用户批量生产通常情况下也可以和触摸屏供应商沟通让供应商按照你的要求提前烧写好固件。

标号⑨处使用request_firmware_nowait函数从用户空间获取固件。它的最后一个参数是一个函数指针,用于指定获取成功后的回调函数,回调函数原型如下所示:

firmware回调函数goodix_config_cb:

static void goodix_config_cb(const struct firmware *cfg, void *ctx)
{struct goodix_ts_data *ts = ctx;int error;if (cfg) {/* send device configuration to the firmware *//*------------------第一部分------------------*/error = goodix_send_cfg(ts, cfg);if (error)goto err_release_cfg;}/*------------------第二部分------------------*/goodix_configure_dev(ts);err_release_cfg:release_firmware(cfg);complete_all(&ts->firmware_loading_complete);
}

从以上代码可以看出,正常情况下该函数只会执行两个以“goodix_”开头的函数,这两个函数完成了后续的初始化。goodix_send_cfg函数完成触摸芯片更新固件的读取、校验、写入工作。goodix_configure_dev函数完成中断的申请、注册,输入设备的注册、设置上报事件等等工作。最终的触摸事件上报由中断服务函数完成。由于这部分内容较长,我们将这两个函数独立出来讲解,如下所示。

2.3. goodix_send_cfg函数实现

函数原型如下所示:

goodix_send_cfg函数实现:

static int  goodix_send_cfg(struct goodix_ts_data *ts,const struct firmware *cfg)
{int error;
/*------------------第一部分------------------*/error = goodix_check_cfg(ts, cfg);if (error)return error;/*------------------第二部分------------------*/error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg->data,cfg->size);if (error) {dev_err(&ts->client->dev, "Failed to write config data: %d",error);return error;}dev_dbg(&ts->client->dev, "Config sent successfully.");
/*------------------第三部分------------------*//* Let the firmware reconfigure itself, so sleep for 10ms */usleep_range(10000, 11000);return 0;
}

以上函数功能是校验从应用空间读取得到的触摸更新固件,如果校验通过则调用第二部分的代码将固件写入触摸芯片,我们重点看第一部分的校验函数。函数实现如下所示。

固件校验函数:

static int goodix_check_cfg(struct goodix_ts_data *ts,const struct firmware *cfg)
{/*------------------第一部分------------------*/if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {dev_err(&ts->client->dev,"The length of the config fw is not correct");return -EINVAL;}/*------------------第二部分------------------*/return ts->chip->check_config(ts, cfg);
}

第一部分,校验固件长度是否大于最大支持的固件长度。官方驱动中这里设置为240由于GT917S和GT5688的固件会超过这个最大值,这里要按照GT917S和GT5688的最大值来计算。最终结果是我们要将“GOODIX_CONFIG_MAX_LENGTH”宏定义的值重新定义为242。

第二部分,调用校验函数。在讲解.prob函数的第三部分,我们根据触摸ID指定了校验函数和地址信息。以GT911为例,如下所示。

gt911的goodix_chip_data结构体:

static const struct goodix_chip_data gt911_chip_data = {.config_addr            = GOODIX_GT9X_REG_CONFIG_DATA,.config_len             = GOODIX_CONFIG_911_LENGTH,.check_config           = goodix_check_cfg_8,
};

可以看到“check_config”是一个函数指针,它指向了“goodix_check_cfg_8”函数,下面将会调用goodix_check_cfg_8函数完成GT911固件的 校验工作,函数实现如下所示。

固件校验函数:

static int goodix_check_cfg_8(struct goodix_ts_data *ts,const struct firmware *cfg)
{int i, raw_cfg_len = cfg->size - 2;u8 check_sum = 0;/*---------------第一部分---------------*/for (i = 0; i < raw_cfg_len; i++)check_sum += cfg->data[i];check_sum = (~check_sum) + 1;/*---------------第二部分---------------*/if (check_sum != cfg->data[raw_cfg_len]) {dev_err(&ts->client->dev,"The checksum of the config fw is not correct");return -EINVAL;}/*---------------第三部分---------------*/if (cfg->data[raw_cfg_len + 1] != 1) {dev_err(&ts->client->dev,"Config fw must have Config_Fresh register set");return -EINVAL;}return 0;
}

校验过程比较简单,与stm32稍有差别。在stm32中我们是计算出配置信息的校验和然后追加到配置信息,然后在最后面添加更新标志。这里读取出来的固件已经加上了校验和并且在固件的最后添加了更新标志,所以这里只需要重新计算校验和并比较是否一致即可代码的第三部分是检测是否有更新标志。

2.4. goodix_configure_dev函数实现

在prob函数的最后会调用两个函数一个是我们上面讲解的固件跟新函数10.2.3 goodix_send_cfg,另外一个是我们这小节要讲解的goodix_configure_dev函数。

总的来说,以上代码是一个IIC设备驱动,它实现了通过IIC1与触摸芯片的通信,但我们最终目标是检测到“按下”或“抬起”事件后通过输入子系统上报给应用层。goodix_configure_dev函数就是用作完成这些后续工作,实现代码如下所示。

完成设备初始化函数:

static int goodix_configure_dev(struct goodix_ts_data *ts)
{int error;/*---------------第一部分---------------*/ts->int_trigger_type = GOODIX_INT_TRIGGER;ts->max_touch_num = GOODIX_MAX_CONTACTS;ts->input_dev = devm_input_allocate_device(&ts->client->dev);if (!ts->input_dev) {dev_err(&ts->client->dev, "Failed to allocate input device.");return -ENOMEM;}ts->input_dev->name = "Goodix Capacitive TouchScreen";ts->input_dev->phys = "input/ts";ts->input_dev->id.bustype = BUS_I2C;ts->input_dev->id.vendor = 0x0416;ts->input_dev->id.product = ts->id;ts->input_dev->id.version = ts->version;/*---------------第二部分---------------*//* Capacitive Windows/Home button on some devices */input_set_capability(ts->input_dev, EV_KEY, KEY_LEFTMETA);input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X);input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y);input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);/* Read configuration and apply touchscreen parameters */goodix_read_config(ts);/* Try overriding touchscreen parameters via device properties */touchscreen_parse_properties(ts->input_dev, true, &ts->prop);/*---------------第三部分---------------*/if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {dev_err(&ts->client->dev, "Invalid config, using defaults\n");ts->prop.max_x = GOODIX_MAX_WIDTH - 1;ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;ts->max_touch_num = GOODIX_MAX_CONTACTS;input_abs_set_max(ts->input_dev,ABS_MT_POSITION_X, ts->prop.max_x);input_abs_set_max(ts->input_dev,ABS_MT_POSITION_Y, ts->prop.max_y);}if (dmi_check_system(rotated_screen)) {ts->prop.invert_x = true;ts->prop.invert_y = true;dev_dbg(&ts->client->dev,"Applying '180 degrees rotated screen' quirk\n");}/*---------------第四部分---------------*/error = input_mt_init_slots(ts->input_dev, ts->max_touch_num,INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);if (error) {dev_err(&ts->client->dev,"Failed to initialize MT slots: %d", error);return error;}error = input_register_device(ts->input_dev);if (error) {dev_err(&ts->client->dev,"Failed to register input device: %d", error);return error;}/*---------------第五部分---------------*/ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;error = goodix_request_irq(ts);if (error) {dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);return error;}return 0;
}

函数内容较多,但是这部分内容不需要我们修改,结合源码介绍如下:

第一部分,根据现有参数初始化输入设备结构体input_dev,一个输入设备结构体代表了一输入设备,再注册它之前需要初始化它的一些参数。

第二部分,同样是初始化输入设备结构体,这部分内容用于设置输入设备能够上报的事件类型以及上报事件。从这里部分代码可以看到上报事件类型有EV_KEY按键事件、绝对坐标事件EV_ABS,绝对坐标事件的键值又分为X坐标值和Y坐标值。

函数goodix_read_config 用于从触摸芯片中读取触摸配置信息并用这些配置信息初始化输入设备。

第三部分,检查主要参数是否出错,如果出错则只用默认的参数配置输入设备。

第四部分,输入设备结构体初始化完成后调用input_register_device函数注册输入设备,注册成功后我们就可以向应用层上报输入触摸事件了。

第五部分,调用goodix_request_irq函数完成中断的申请,函数实现如下所示。

中断申请函数:

static int goodix_request_irq(struct goodix_ts_data *ts)
{return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,NULL, goodix_ts_irq_handler,ts->irq_flags, ts->client->name, ts);
}

申请函数使用了devm_request_threaded_irq函数,在驱动中我们会经常看到“devm_"开头的函数,这些函数大多用于注册、申请工作,使用这一类函数注册、申请的内容无需我们手动注销,驱动退出之前系统会自动完成注销。这里我们重点关注中断的处理函数goodix_ts_irq_handle r,触摸中断发生有将会在中断服务函数中上报触摸事件。

中断服务函数如下所示:

中断服务函数:

static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{struct goodix_ts_data *ts = dev_id;goodix_process_events(ts); -------------------①if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)dev_err(&ts->client->dev, "I2C write end_cmd error\n");return IRQ_HANDLED;
}

标号②处的函数用于处理触摸事件,具体的处理过程这里不再介绍,读者可参考输入子系统章节自行阅读。至此,我么可以知道,当触摸中断发生后将会在中断服务函数中上报输入事件,而应用程序只需要从“/dev/input”目录下对应的节点读取状态即可。

3. 驱动测试

实际使用时我们将LCD和触摸写进一个设备树插件,修改后的设备树 插件保存在“~/embed_linux_driver_tutorial_imx6_code/linux/touch_scream_GTxxx”。4.3寸显示屏使用“imx-fire-lcd43-overlay.dts”设备树插件,5寸和7寸 使用“imx-fire-lcd5-overlay.dts”设备树插件。如何放置设备树插件已经多次介绍,这里不再赘述。

由于系统中默认已经添加了触摸驱动,所以测试之前要将其屏蔽掉。在开发板中打开“/boot/uEnv.txt”, 把LCD和触摸屏的设备树插件 屏蔽掉,添加本实验的设备树插件(这里使用的是5.0寸显示屏)。

03|

屏蔽之后重启开发板,由于内核驱动没有对应的设备树,开发板启动后无法触摸。下一步就要将我们修改的驱动程序通过“insmod”命令加载到系统。

编译本章配套驱动程序,将生成的.ko文件拷贝到开发板。由于系统默认关闭了较低等级的内核打印,为便于观察驱动加载情况,我们需要手动开启所有等级的内核打印,命令如下“echo 8 1 4 7 > /proc/sys/kernel/printk”。注意,数字之间 要有空格,并且系统重启之后要重新执行该命令。执行成功后直接使用insmod 命令加载驱动,如下所示。

03|

驱动成功加载之后输出以上内容,正常情况下这时触摸已经能够正常运行。下面简单说明一下内核打印内容。

  • 标号①是读取得到的原始ID,在驱动中它是一个字符串。

  • 标号②中ID是将原始ID(字符串)转化为数字(字母被忽略),version是版本。

  • 标号③输出的是更新固件的名字,它的格式为“goodix_xxx_cfg.bin”中间是触摸的ID。

  • 标号④,输出获取更新固件失败了,这是因为我们没有添加更新固件。我们之前说过,触摸屏出厂时已经配置好了,所以通常情况下我们无需再次更新。

  • 标号⑤,输出触摸对应的input编号。

更新触摸固件的方法很简单,介绍如下。首先将“~/embed_linux_driver_tutorial_imx6_code/linux/touch_scream_GTxxx”下的固件拷贝到开发板的“/lib/firmware”目录下,然后重启开发板,按照 前面的步骤重新加载驱动即可。添加更新固件后再次加载驱动输入结果如下所示。

04|

从上图可以看出,添加固件后驱动成功获取更新固件,根据输出内容可知,更新固件长度为242,使 用的是16位校验(不同型号触摸更新固件长度和校验位数不同)。

通过如下命令,我们可以查看到系统中的输入设备,如无意外会有event0、event1等设备(这里本实验对应的触摸屏设备是event1)。

ls /dev/input/

下面我们使用hexdump简单检测一下触摸功能。

hexdump /dev/input/event1

执行命令后,在屏幕上轻触,会打印出大量格式化的数据,这些就是触摸数据了。

04|

相关文章:

Linux驱动开发(16):输入子系统–电容触摸驱动实验

有关电容触摸的基础知识内容可以参考野火STM32相关教程&#xff0c;这里只介绍电容触摸驱动的相关内容。 本章配套源码、设备树以及更新固件位于“~/embed_linux_driver_tutorial_imx6_code/linux_driver/touch_scream_GTxxx”目录下。 触摸面板通过双面胶粘在显示屏上&#…...

《深入浅出HTTPS​​​​​​​​​​​​​​​​​》读书笔记(24):椭圆曲线密码学

《深入浅出HTTPS​​​​​​​​​​》读书笔记&#xff08;24&#xff09;&#xff1a;椭圆曲线密码学 为了保证DH的密钥对不被破解&#xff0c;提升安全性的主要手段就是增加密钥对的长度&#xff0c;但是长度越长&#xff0c;性能越低。 为了解决性能问题&#xff0c;需要…...

现代光学基础5

总结自老师的讲义 yt5 开卷考试复习资料&#xff1a;光探测器与光伏技术 目录 光探测器&#xff08;Photodetector&#xff09; 工作原理二极管电路连接方式响应度&#xff08;Responsivity&#xff09;微弱光检测超导纳米线单光子探测光电二极管噪声 太阳能电池&#xff0…...

力扣hot100——贪心

121. 买卖股票的最佳时机 class Solution { public:int maxProfit(vector<int>& a) {if (a.size() 1) return 0;int ans 0;int mi a[0];for (int i 1; i < a.size(); i) {ans max(ans, a[i] - mi);mi min(mi, a[i]);}return ans;} };55. 跳跃游戏 class S…...

vue3如何实现防抖?

第一 防抖就是我们设置一个调用时间&#xff0c;点击后设置时间开始倒计时&#xff0c;如果再次点击会重新倒计时 npm或yarn安装&#xff1a; npm install lodash <template><div click"debouncedInputHandler"><button>打印</button>…...

西安电子科技大学初/复试笔试、面试、机试成绩占比

西安电子科技大学初/复试笔试、面试、机试成绩占比 01通信工程学院 02电子工程学院 03计算机科学与技术学院 04机电工程学院 06经济与管理学院 07数学与统计学院 08人文学院 09外国语学院 12生命科学与技术学院 13空间科学与技术学院 14先进材料与纳米科技学院 15网络与信息安…...

spring mvc源码学习笔记之六

pom.xml 内容如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…...

树莓派4b如何连接ov7670摄像头

在树莓派4B上连接和使用OV7670摄像头是一项具有一定技术挑战的任务。这是因为OV7670摄像头是一个原始的CMOS摄像头模块,它通过并行接口与主机通信,而树莓派的GPIO接口通常用于串行接口(如I2C、SPI、UART)通信,不直接支持并行摄像头接口。因此,需要一些额外的硬件和软件工…...

[微服务]分布式搜索Java客户端

快速入门 使用RestClient客户端进行数据搜索可以分为两步 构建并发起请求 代码解读&#xff1a; 第一步&#xff0c;创建SearchRequest对象&#xff0c;指定索引库名第二步&#xff0c;利用request.source()构建DSL&#xff0c;DSL中可以包含查询、分页、排序、高亮等 query…...

如何使用 `uiautomator2` 控制 Android 设备并模拟应用操作_VIVO手机

在 Android 自动化测试中,uiautomator2 是一个非常强大的工具,能够帮助我们通过 Python 控制 Android 设备执行各种操作。今天,我将通过一个简单的示例,介绍如何使用 uiautomator2 控制 Android 设备,执行特定的应用启动、广告跳过以及其他 UI 操作。此示例的目标是自动化…...

在Ubuntu 18.04.6 LTS安装OpenFace流程

一、修改配置:将gcc8&#xff0c;g8作为默认选项 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 100 sudo update-alternatives --config gcc 选择版本&#xff0c;再查看gcc --version sudo update-alternatives --install /usr/bin/g g /usr/bin/g-…...

C 语言的整型提升问题

目录 引言 一、什么是整型提升 二、为什么会有整型提升 三、整型提升的规则 四、整型提升的影响 五、如何避免整型提升带来的问题 六、总结 引言 在 C 语言中&#xff0c;整型提升&#xff08;Integer Promotion&#xff09;是一个常常被忽视但却非常重要的概念。理解整…...

第0章 机器人及自动驾驶SLAM定位方法全解析及入门进阶学习建议

嗨&#xff0c;各位同学大家好&#xff01;笔者自985硕士毕业后&#xff0c;在机器人算法领域已经深耕 7 年多啦。这段时间里&#xff0c;我积累了不少宝贵经验。本专栏《机器人工程师带你从零入门SLAM》将结合下面的SLAM知识体系思维导图及多年的工作实战总结&#xff0c;将逐…...

video.js视频播放上手

html案例 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>videojs视频播放</title> </head> <link href"https://cdnjs.cloudflare.com/ajax/libs/video.js/7.3.0/video-js.min.cs…...

【LLM-Agent】Building effective agents和典型workflows

note Anthropic的工程经验&#xff1a; 大道至简&#xff0c;尽量维护系统的简洁&#xff1b;尽量让过程更加透明&#xff08;因为你依赖的是LLM的决策&#xff0c;如果只看输出不看过程&#xff0c;很容易陷入难以debug的情况&#xff09;&#xff1b;对LLM需要调用的工具&am…...

《量子比特大阅兵:不同类型量子比特在人工智能领域的优劣势剖析》

在科技的前沿&#xff0c;量子比特与人工智能的融合正开启一扇全新的大门。不同类型的量子比特&#xff0c;如超导、离子阱、光量子等&#xff0c;在与人工智能结合时展现出独特的优势与劣势。 超导量子比特 超导量子比特是目前应用较为广泛且研究相对成熟的量子比特类型。它…...

《探秘开源大模型:AI 世界的“超级引擎”》

《探秘开源大模型:AI 世界的“超级引擎”》 一、开源大模型崛起之路二、开源大模型发展历程回顾(一)早期奠基:理论突破与初步实践(二)快速发展:百花齐放的模型格局(三)当下态势:走向成熟与多元融合三、开源大模型核心技术剖析(一)Transformer 架构:基石之稳(二)…...

el-table行列转换简单版,仅限单行数据

原始数据格式如下&#xff0c;如果不是此格式&#xff0c;请转换成以下格式在进行以下操作 [{ label: name, value: Tom },{ label: age, value: 25 },{ label: country, value: UK } ]代码如下 <template><el-table :data"tableData" style"width: …...

2025年1月4日蜻蜓q旗舰版st完整开源·包含前后端所有源文件·开源可商用可二开·优雅草科技·优雅草kir|优雅草星星|优雅草银满|优雅草undefined

2025年1月4日蜻蜓q旗舰版st完整开源包含前后端所有源文件开源可商用可二开优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined 产品介绍&#xff1a; 本产品主要贡献者优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined-青史留名&#xff0c;时光如川浪淘…...

SQL把字符串按逗号分割成记录

在 SQL 中&#xff0c;可以通过以下方法将字符串按逗号分割&#xff0c;并将每个分割的值作为单独的记录插入到结果集中。以下是针对不同数据库系统的实现方法&#xff1a; 1. 使用 STRING_SPLIT&#xff08;SQL Server 2016&#xff09; STRING_SPLIT 是 SQL Server 提供的内置…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...