Linux驱动开发(16):输入子系统–电容触摸驱动实验
有关电容触摸的基础知识内容可以参考野火STM32相关教程,这里只介绍电容触摸驱动的相关内容。
本章配套源码、设备树以及更新固件位于“~/embed_linux_driver_tutorial_imx6_code/linux_driver/touch_scream_GTxxx”目录下。
触摸面板通过双面胶粘在显示屏上,他们在硬件上没有关联,通常情况下我们会设置触摸面板的“分辨率”与显示屏的分辨率一致。触摸芯片自动完成触摸信息的采集和处理,我们要做的就是配置触摸芯片、读取触点坐标。
常见的触摸接口原理图如下所示:
触摸芯片有四个引脚连接到了单片机,其中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 >1x_chip_data;case 911:case 9271:case 9110:case 927:case 928:return >911_chip_data;case 912:case 9157: // fire 新增return >9x_chip_data;case 917: // fire 新增return >917_chip_data;case 5688: // fire 新增return >5688_chip_data;case 967:return >967_chip_data;default:return >9x_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寸显示屏)。
屏蔽之后重启开发板,由于内核驱动没有对应的设备树,开发板启动后无法触摸。下一步就要将我们修改的驱动程序通过“insmod”命令加载到系统。
编译本章配套驱动程序,将生成的.ko文件拷贝到开发板。由于系统默认关闭了较低等级的内核打印,为便于观察驱动加载情况,我们需要手动开启所有等级的内核打印,命令如下“echo 8 1 4 7 > /proc/sys/kernel/printk”。注意,数字之间 要有空格,并且系统重启之后要重新执行该命令。执行成功后直接使用insmod 命令加载驱动,如下所示。
驱动成功加载之后输出以上内容,正常情况下这时触摸已经能够正常运行。下面简单说明一下内核打印内容。
-
标号①是读取得到的原始ID,在驱动中它是一个字符串。
-
标号②中ID是将原始ID(字符串)转化为数字(字母被忽略),version是版本。
-
标号③输出的是更新固件的名字,它的格式为“goodix_xxx_cfg.bin”中间是触摸的ID。
-
标号④,输出获取更新固件失败了,这是因为我们没有添加更新固件。我们之前说过,触摸屏出厂时已经配置好了,所以通常情况下我们无需再次更新。
-
标号⑤,输出触摸对应的input编号。
更新触摸固件的方法很简单,介绍如下。首先将“~/embed_linux_driver_tutorial_imx6_code/linux/touch_scream_GTxxx”下的固件拷贝到开发板的“/lib/firmware”目录下,然后重启开发板,按照 前面的步骤重新加载驱动即可。添加更新固件后再次加载驱动输入结果如下所示。
从上图可以看出,添加固件后驱动成功获取更新固件,根据输出内容可知,更新固件长度为242,使 用的是16位校验(不同型号触摸更新固件长度和校验位数不同)。
通过如下命令,我们可以查看到系统中的输入设备,如无意外会有event0、event1等设备(这里本实验对应的触摸屏设备是event1)。
ls /dev/input/
下面我们使用hexdump简单检测一下触摸功能。
hexdump /dev/input/event1
执行命令后,在屏幕上轻触,会打印出大量格式化的数据,这些就是触摸数据了。
相关文章:

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

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

现代光学基础5
总结自老师的讲义 yt5 开卷考试复习资料:光探测器与光伏技术 目录 光探测器(Photodetector) 工作原理二极管电路连接方式响应度(Responsivity)微弱光检测超导纳米线单光子探测光电二极管噪声 太阳能电池࿰…...

力扣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如何实现防抖?
第一 防抖就是我们设置一个调用时间,点击后设置时间开始倒计时,如果再次点击会重新倒计时 npm或yarn安装: 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客户端进行数据搜索可以分为两步 构建并发起请求 代码解读: 第一步,创建SearchRequest对象,指定索引库名第二步,利用request.source()构建DSL,DSL中可以包含查询、分页、排序、高亮等 query…...

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

在Ubuntu 18.04.6 LTS安装OpenFace流程
一、修改配置:将gcc8,g8作为默认选项 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 100 sudo update-alternatives --config gcc 选择版本,再查看gcc --version sudo update-alternatives --install /usr/bin/g g /usr/bin/g-…...

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

第0章 机器人及自动驾驶SLAM定位方法全解析及入门进阶学习建议
嗨,各位同学大家好!笔者自985硕士毕业后,在机器人算法领域已经深耕 7 年多啦。这段时间里,我积累了不少宝贵经验。本专栏《机器人工程师带你从零入门SLAM》将结合下面的SLAM知识体系思维导图及多年的工作实战总结,将逐…...

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的工程经验: 大道至简,尽量维护系统的简洁;尽量让过程更加透明(因为你依赖的是LLM的决策,如果只看输出不看过程,很容易陷入难以debug的情况);对LLM需要调用的工具&am…...

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

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

el-table行列转换简单版,仅限单行数据
原始数据格式如下,如果不是此格式,请转换成以下格式在进行以下操作 [{ 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 产品介绍: 本产品主要贡献者优雅草科技优雅草kir|优雅草星星|优雅草银满|优雅草undefined-青史留名,时光如川浪淘…...

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

C#设计模式(行为型模式):观察者模式
C#设计模式:观察者模式,让对象间通信更优雅 在软件开发中,我们经常会遇到一个对象的状态发生改变,其他对象需要自动更新或做出相应反应的场景。例如: GUI事件处理: 当用户点击按钮时,按钮需要…...

pytorch镜像源
我以为的 pip install torch2.3.1cu118 torchvision0.18.1cu118 torchaudio2.3.1cu118 -f https://download.pytorch.org/whl/torch_stable.html实际上,有很多加速方案 为提高下载速度可以使用国内的镜像源来安装与 CUDA 11.8 兼容的 PyTorch。 方法 1:…...

Verilog语法之常用行为级语法
摘要:本文主要介绍了一些在verilog中的行为级语法,并且提供了大量的运行实际例子,可以通过这些例子感受行为级语法在仿真中的巨大作用。 概述:行为级语法是RTL级的上一层,或者说是比RTL级更高级的语法,其语…...

PADS Logic原理图中有很多页原理图,如何(怎样)删除其中一页或者多页
我们在进行PADS Logic进行原理图设计的时候,有时候可能遇到一次性设计了很多页的原理图,比如说十几页的原理图。那么我们在进行PADS Layout的时候,可能将这些原理图绘制两块板或者多块PCB板,那么这时候我们需要将其中的一张原理图…...

蓝色简洁引导页网站源码
一款蓝色的简洁引导页,适合资源分发和网站备用引导。 1.源码上传至虚拟机或者服务器 2.绑定域名和目录 3.访问域名安装 4.安装完成后就行了 https://pan.quark.cn/s/b2d8b9c5dc7f https://pan.baidu.com/s/17h1bssUNhhR9DMyNTc-i9Q?pwd84sf https://caiyun.139.com…...

Apache PDFBox添加maven依赖,pdf转成图片
要使用Apache PDFBox将PDF文件转换为图片,并将其添加到Maven项目中,您可以按照以下步骤操作: 1. 添加Maven依赖 在您的pom.xml文件中添加Apache PDFBox的依赖。请确保使用最新版本的PDFBox库。截至2025年,以下是推荐的配置&…...

mybatis 和 mybatisPlus 兼容性问题
项目采用的是 mybatis, 后续引入了 mybatisPlus,用 mybatisX 创建的四个类一直报错,提示找不到符号,意识到 mybatis 和 mybatisPlus 的兼容性问题,通过修改配置 两者的配置如下 #配置mybatis配置 mybatis:type-aliase…...

Mono里运行C#脚本23—mono_jit_exec
前面已经分析一部分代码,可以打下基础知识,当然还有很多其它部分的内容,没有深入去了解。 接着下来,我们去探索一下怎么样找到执行应用程序的入口。 在这个嵌入脚本程序里,有一个这样的函数调用: main_function (domain, file, argc - 1, argv + 1); 这个函数的作用,…...

第十一章 图论
/* * 题目名称:连通图 * 题目来源:吉林大学复试上机题 * 题目链接:http://t.cn/AiO77VoA * 代码作者:杨泽邦(炉灰) */#include <iostream> #include <cstdio>using namespace std;const int MAXN 1000 10;int fathe…...

纯前端实现将pdf转为图片(插件pdfjs)
需求来源 预览简历功能在移动端,由于用了一层iframe把这个功能嵌套在了app端,再用一个iframe来预览,只有ios能看到,安卓就不支持,查了很多资料和插件,原理基本上都是用iframe实现的。最终转换思路…...