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

有关电阻触摸的基础知识内容可以参考野火STM32相关教程,这里只介绍电阻触摸驱动的相关内容。与一般的微处理器 不同,本节使用的imx6ull内自带触摸屏控制器,只需要把电阻触摸屏的信号线接到对应的IO即可,通过配置imx6ull 触摸屏控制器获取触点坐标值。

1. TSC 触摸屏控制器介绍

触摸屏系统由三个部分组成,它们分别是TSC控制器、TSC_ANA、ADC。其中,TSC控制器是整个系统的核心, 负责为 TSC_ANA 和 ADC 提供控制信号,实现电阻屏的触摸检测和触点坐标测量;TSC_ANA根据TSC控制器 提供的信号负责向电阻屏的 X+、X-、Y+、Y- 四根线提供正负电压;ADC则根据TSC控制器的坐标转换信号负责 采集触点x轴和y轴的电压,并进行坐标值转换。TSC控制器、TSC_ANA、ADC三个模块协同工作,形成一个触摸屏系统。

1

上图触摸屏系统有以下几种工作状态:

  • 空闲状态: 当完成坐标测量后,TSC会停留在空闲状态。只有当TSC_FLOW_CONTROL寄存器start_sen位被设置为1时, TCS才会开始进行触摸检测或者坐标测量;当TSC_FLOW_CONTROL寄存器的disable位被设置为1时,完成当前的任务后返回空闲状态。

  • 预充电状态: 这是触摸检测前的工作状态,在该状态下,电阻屏的其中一层被设置为正电压。

  • 触摸检测状态: 如果TSC控制器设置为自动测量,当检测到触摸后,TSC自动控制ADC测量坐标,否则,需要通过软件设置TSC_FLOW_CONTROL寄存器 start_measure位开始测量坐标。

  • 坐标测量状态: 在坐标测量状态下,TSC通过硬件自动控制TSC_ANA和ADC进行坐标测量,无需软件干预。如果设置了ADC硬件平均功能, ADC对采集到的数据进行求均值。

  • 数据有效性检测状态: 在测量到坐标值之后,TSC会再一次检测是否有触摸,如果没有检测到触摸,则判断先前测量到的坐标值是无效的;否则,测量到的坐标是有效的。

  • 中断状态: 每个中断都可以通过TSC_INT_EN、TSC_INT_SIG_EN设置。

  • 复位状态: TSC提供硬件复位ipg_reset_b和软件复位sw_rst两种复位方式。

  • 调试状态: 一旦该状态被使能,所有的TSC输出信号将会被软件控制。软件可以通过调试接口获取所有TSC的输入信号。此外,还可以通过 获取debug寄存器对应位的值,获取TSC当前所处在的工作状态。

触摸屏控制器相关寄存器介绍:

这里只做简单介绍,详细内容请查阅《IMX6ULLRM(6ULL用户手册).pdf》的ADC和TSC章节内容

1、TSC_BASIC_SETTING 寄存器

1

该寄存器我们需用配置三个地方:

  • MEASURE_DELAY_TIME: 由于电阻屏检测的是触点的电压信号,TSC检测到触点按下后,ADC需要等待触点的电压稳定后再进行坐标 测量,这个等待的时间就是测量延迟时间。

  • 4_5_WIRE:imx6ull触摸屏控制器支持4线模式和5线模式,我们使用的是4线的电阻屏,需要把它设置为4线模式;

  • AUTO_MEASURE:设置TSC检测到触点按下之后,是否自动启动坐标测量,这里我们选择自动测量。

2、TSC_PS_INPUT_BUFFER_ADDR 寄存器

1

该寄存器用于设置预充电时间,即在TSC控制器进入检测状态之前,会对电阻屏其中一层进行预充电(即设置为正电压),只有达到预充电时间要求之后, 才可以进行到下一个检测状态。

3、TSC_INT_EN 寄存器

1

这个是中断使能寄存器,通过该寄存器可以设置空闲中断、触摸检测中断、坐标测量完成中断。在本实验中,只需使能坐标测量完成中断,坐标测量完成后 产生中断,我们可以在中断处理函数中读取测量到的坐标值。

4、TSC_INT_SIG_EN 寄存器

1

中断信号使能寄存器,前面我们使能了坐标测量中断,那么对应的这里需要设置MEASURE_SIG_EN使能测量信号。 此外,还需要设置VALID_SIG_EN使能数据有效性判断。判断坐标有效性的机制:在坐标测量完成之后, 再次检测是否有触点按下,如果有,则测量到的坐标是有效的;否则,测量到的坐标无效。

5、TSC_FLOW_CONTROL 寄存器

1

设置好前面的寄存器之后,我们需要把TSC_FLOW_CONTROL的DISABLE位清零退出空闲状态,并设置START_SENSE位 开启触摸检测。

5、TSC_MEASEURE_VALUE 寄存器

1

当坐标测量完整后,坐标值最终存储TSC_MEASEURE_VALUE寄存器,其中bit[27~16]存储的是x轴作坐标值, bit[11~0]存储的是y轴坐标值。在中断处理函数里读取该寄存器即可以获取触点的xy轴坐标。

为了节省文章篇幅,寄存器介绍就到此为止,除了配置TSC部分,ADC也需要对其进行配置。其中涉及的配置 内容包括:ADC的时钟源选择、分辨率设置、采样模式设置、ADC输入通道选择、是否使用硬件平均、启动ADC校准等, 涉及到的寄存器有:ADCx_CFG、ADCx_HC0~ADCx_HC5、ADCx_GC、ADCx_GS。详细内容请阅读《IMX6ULLRM(6ULL用户手册).pdf》 的“Chapter 13 Analog-to-Digital Converter (ADC)”章节。

2. 电阻触摸屏实验

本章配套源码以及设备树位于“~/linux_driver/touch_screen_resisitive”目录下。

2.1. 硬件介绍

imx6ull的触摸屏控制器分4线模式和5线模式,我们使用的电阻触摸屏是4线的,在4线模式下, 触摸屏功能接口与GPIO对应的关系如下:

TSC function ports

GPIO ports

ynlr

GPIO1_IO01

ypll

GPIO1_IO02

xnur

GPIO1_IO03

xpul

GPIO1_IO04

在本实验中,电阻屏接口Y-、Y+、X-、X+分别接到开发板的GPIO1_IO01、GPIO1_IO02、GPIO1_IO03、GPIO1_IO04。

2.2. 设备树插件实现

根据电阻触摸屏功能接口所用到的IO,对应的设备树插件如下:

设备树插件 (位于 linux_driver/touch_screen_resisitive/imx-fire-ts-res-4wires-overlay.dts):

#include "../imx6ul-pinfunc.h"
#include "../imx6ull-pinfunc.h"
#include "../imx6ull-pinfunc-snvs.h"
#include "dt-bindings/interrupt-controller/irq.h"
#include "dt-bindings/gpio/gpio.h"
/dts-v1/;
/plugin/;

/ {
        fragment@0 {
            target = <&iomuxc>;
                __overlay__ {
                        pinctrl_tsc: tscgrp {
                                    fsl,pins = <
                                        MX6UL_PAD_GPIO1_IO01__GPIO1_IO01    0xB0
                                        MX6UL_PAD_GPIO1_IO02__GPIO1_IO02    0xB0
                                        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0xB0
                                        MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0xB0
                                    >;
                                };
                            };
        };

    fragment@1 {
        target=<&tsc>;
        __overlay__ {
            compatible = "fire,res_tsc";
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_tsc>;
            xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
            measure-delay-time = <0xfffff>;
            pre-charge-time = <0xffff>;
            touchscreen-average-samples = <32>;
            status = "okay";
        };

    };
};
  • 第10~22行:向pinctrl子系统节点追加电阻触摸屏使用到的引脚。

  • 第30行:向gpio子系统添加GPIO1_IO03引脚,该引脚可以用于辅助判断触摸屏是否有触点按下。在imx6ull触摸屏处于检测状态时, 当有触点按下时,该引脚的电平为低电平;当触点离开触摸屏后,该引脚恢复为高电平。

  • 第31行:设置测量延迟时间。这个时间就是ADC在测量坐标之前需要等待触点电压的稳定的时间。

  • 第32行:设置电阻屏的预充电时间。

  • 第33行:设置平均采样点。

注: 若需使用内核自带的电阻触摸屏驱动,向自行编译linux_driver/touch_screen_resisitive目录下的 imx-fire-touch-resisitive-4wires-overlay.dts设备树插件,具体使用方法请参考: input子系统:电阻触摸屏 章节。

2.3. 驱动程序实现

2.3.1. 驱动入口和出口函数实现

本实验的驱动程序代码是基于平台设备驱动编写的,驱动入口和出口函数仅用于平台驱动的注册和注销,代码如下:

驱动入口和出口函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static struct of_device_id res_ts_of_match[] = {
      {.compatible = "fire,res_tsc",},
      {},
};

static struct platform_driver res_ts_drv = {
      .probe  = res_ts_driver_probe,
      .remove = res_ts_driver_remove,
      .driver = {
              .name = "res_ts_drv",
              .of_match_table = res_ts_of_match,
      },
};


static int __init res_ts_driver_init(void)
{
      return platform_driver_register(&res_ts_drv);
}

static void __exit res_ts_driver_exit(void)
{
      platform_driver_unregister(&res_ts_drv);
}

module_init(res_ts_driver_init);
module_exit(res_ts_driver_exit);
MODULE_LICENSE("GPL");
  • 第1~4行:定义电阻触摸屏的设备树匹配表。

  • 第6~13行:定义电阻触摸屏的平台驱动结构体。

  • 第7~8行:在驱动加载注册平台驱动时会与设备树进行匹配,若匹配成功则会执行.probe函数; 在驱动卸载注销平台驱动时.remove函数会被执行; 我们可以在.probe函数实现一些初始化的工作, 在.remove函数实现一些清理工作。

  • 第11行:.of_match_table 用于和设备树节点匹配。

  • 第16~19行:在驱动程序的入口函数注册平台驱动。

  • 第21~24行:在驱动程序的出口函数注销平台驱动。

2.3.2. .prob函数实现

.prob函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static int res_ts_driver_probe(struct platform_device *pdev)
{
  struct device_node *np = pdev->dev.of_node;
  struct imx6ull_rests *ts;
  struct input_dev *input_dev;
  int err;
  int tsc_irq;

  ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
  if (!ts)
    return -ENOMEM;

  input_dev = devm_input_allocate_device(&pdev->dev);
  if (!input_dev)
    return -ENOMEM;

  input_dev->name = "Fire Touchscreen Driver";

  input_dev->open = imx6ull_tsc_open;
  input_dev->close = imx6ull_tsc_close;

  input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
  input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0);
  input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0);

  input_set_drvdata(input_dev, ts);

  ts->dev = &pdev->dev;
  ts->input_dev = input_dev;

  ts->tsc_regs = devm_of_iomap(&pdev->dev, np, 0, NULL);
  if (IS_ERR(ts->tsc_regs)) {
    err = PTR_ERR(ts->tsc_regs);
    dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
    return err;
  }

  ts->adc_regs = devm_of_iomap(&pdev->dev, np, 1, NULL);
  if (IS_ERR(ts->adc_regs)) {
    err = PTR_ERR(ts->adc_regs);
    dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
    return err;
  }

  ts->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
  if (IS_ERR(ts->tsc_clk)) {
    err = PTR_ERR(ts->tsc_clk);
    dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
    return err;
  }

  ts->adc_clk = devm_clk_get(&pdev->dev, "adc");
  if (IS_ERR(ts->adc_clk)) {
    err = PTR_ERR(ts->adc_clk);
    dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
    return err;
  }

  ts->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
  if (IS_ERR(ts->xnur_gpio)) {
    err = PTR_ERR(ts->xnur_gpio);
    dev_err(&pdev->dev,
      "failed to request GPIO tsc_X- (xnur): %d\n", err);
    return err;
  }

  tsc_irq = platform_get_irq(pdev, 0);
  if (tsc_irq < 0) {
    dev_err(&pdev->dev, "no tsc irq resource?\n");
    return tsc_irq;
  }

  err = devm_request_threaded_irq(ts->dev, tsc_irq,NULL, tsc_irq_fn, IRQF_ONESHOT, dev_name(&pdev->dev), ts);
  if (err) {
    dev_err(&pdev->dev,
      "failed requesting tsc irq %d: %d\n",
      tsc_irq, err);
    return err;
  }

  get_tsc_para_from_dt(&pdev->dev, ts);

  err = input_register_device(ts->input_dev);
  if (err) {
    dev_err(&pdev->dev,
      "failed to register input device: %d\n", err);
    return err;
  }

  printk(KERN_EMERG"match success!\n");
  return 0;
}

第9行:分配一个imx6ull_rests结构体,该结构体存放的是电阻屏驱动的一些私有数据,该结构体定义如下:

struct imx6ull_rests {
    struct device *dev;              /* 触摸屏驱动对应的设备 */
    struct input_dev *input_dev;     /* 输入设备 */
    struct imx6ull_tsc *tsc_regs;    /* imx6ull tsc控制器的寄存器 */
    struct imx6ull_adc *adc_regs;    /* imx6ull ADC的寄存器 */
    struct clk *tsc_clk;             /* tsc控制器时钟 */
    struct clk *adc_clk;             /* ADC时钟 */
    struct gpio_desc *xnur_gpio;     /* 辅助检测是否有触摸的gpio */

    unsigned int measure_delay_time; /* 测量延迟时间 */
    unsigned int pre_charge_time;    /* 电阻屏预充电时间 */
    bool average_enable;             /* 是否使能ADC硬件平均功能 */
    unsigned int average_select;     /* ADC的平均采样点 */
};
  • 第13行:分配一个input_dev结构体。

  • 第17行:设置输入设备的名称。

  • 第19~20行:分别设置input_dev结构体的open、close函数,在open函数中实现触摸屏控制器和ADC的时钟使能、寄存器初始化; 在close函数关闭时钟、关闭触摸屏控制器、关闭ADC。

  • 第22行:设置支持触摸事件

  • 第23~24行:设置x轴、y轴的绝对位移事件,以及位移的范围(位移范围:0x00~0xFFF)。

  • 第26行:把ts设置为输入设备的私有数据,以便在open/close时获取输入设备的私有数据。而不需要全局变量。

  • 第31行:通过设备节点的reg属性,进行tsc触摸屏控制器寄存器地址的映射,得到该寄存对应的虚拟地址。

  • 第38行:通过设备节点的reg属性,进行ADC寄存器地址的映射,得到该寄存对应的虚拟地址。

  • 第45行:获取设备节点tsc触摸屏控制器的时钟。

  • 第52行:获取设备节点adc的时钟。

  • 第59行:获取设备节点名为“xnur”的gpio。

  • 第67行:从平台资源中获取tsc的中断号。

  • 第73行:申请tsc中断,注册tsc_irq_fn中断处理函数。

  • 第81行:从设备树节点获取电阻屏相关的设置参数,并填充ts结构体。

  • 第83行:注册输入设备。

2.3.3. .open函数实现

在.probe函数中,我们并没有对ADC、TSC进行相应的初始化,只填充了imx6ull_rests结构体、申请注册中断处理函数、 注册一个输入设备。ADC、TSC硬件硬件相关的初始化操作放在.open函数,只用当我们使用到触摸屏时才对其进行初始化。 .open函数的代码如下:

.open函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static int imx6ull_tsc_open(struct input_dev *input_dev)
{
  struct imx6ull_rests *tsc = input_get_drvdata(input_dev);
  int err;

  err = clk_prepare_enable(tsc->adc_clk);
  if (err) {
    dev_err(tsc->dev,
      "Could not prepare or enable the adc clock: %d\n",
      err);
    return err;
  }

  err = clk_prepare_enable(tsc->tsc_clk);
  if (err) {
    dev_err(tsc->dev,
      "Could not prepare or enable the tsc clock: %d\n",
      err);
    goto disable_adc_clk;
  }

  err = imx6ull_tsc_init(tsc);
  if (err)
    goto disable_tsc_clk;

  return 0;

disable_tsc_clk:
  clk_disable_unprepare(tsc->tsc_clk);
disable_adc_clk:
  clk_disable_unprepare(tsc->adc_clk);
  return err;
}
  • 第3行:从输入设备中获取私有数据imx6ull_rests结构体。

  • 第6、14行:分别使能adc时钟、tsc时钟。

  • 第22行:对ADC、TSC进行硬件初始化工作。

2.3.4. .close函数实现

在我们不使用触摸屏时,我们应该关闭它,以设备降低功耗。.close函数主要关闭ADC、TSC及其对应的时钟,代码如下:

.close函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static void imx6ull_tsc_close(struct input_dev *input_dev)
{
  struct imx6ull_rests *tsc = input_get_drvdata(input_dev);

  imx6ull_tsc_disable(tsc);

  clk_disable_unprepare(tsc->tsc_clk);
  clk_disable_unprepare(tsc->adc_clk);
}

2.3.5. 触摸屏控制器相关的硬件操作

触摸屏控制器控制器的初始化主要包含两个部分:ADC初始化、TSC初始化。

2.3.5.1. ADC 初始化

ADC 初始化函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static int imx6ull_adc_init(struct imx6ull_rests *ts)
{
  struct imx6ull_adc *adc_regs = ts->adc_regs;

  adc_regs->CFG &= ~(0xf << 0);
  adc_regs->CFG |= (0x2 << 2);  /* 设置ADC的分辨率为12bit */
  adc_regs->CFG |= (0 << 0);    /* 选择ADC的时钟源,这里选择IPG clock */
  adc_regs->CFG |= (0x3 << 5);  /* 对ADC的时钟源进行8分频 */
  adc_regs->CFG &= ~(0x1 << 4); /* 设置为短采样模式 */

  if (ts->average_enable)
  {
    adc_regs->CFG &= ~(0x3 << 14);
    adc_regs->CFG |= (ts->average_select << 14); /* 设置平均采样点个数 */
  }

  if (ts->average_enable)
  {
    adc_regs->GC |= (0x1 << 5); /* 使能ADC硬件平均功能 */
  }


  adc_regs->HC[1] &= ~(0x1 <<7);  /* 禁止channel3转换完成中断 */
  adc_regs->HC[1] &= ~0x1F;
  adc_regs->HC[1] |= 0x03;        /* 配置channel3为xnur    */

  adc_regs->HC[3] &= ~(0x1 <<7);  /* 禁止channel1转换完成中断 */
  adc_regs->HC[3] &= ~0x1F;
  adc_regs->HC[3] |= 0x01;         /* 配置channel1为ynlr    */

  if(!imx6ull_adc_auto_calibration(ts))
  {
    dev_err(ts->dev, "ADC calibration failed\n");
    return -1;
  }

  return 0;
}
  • 第5~9行:配置ADC的分辨率、ADC时钟源、时钟分频系数,以及采样模式。

  • 第11~15行: 如果使用ADC的硬件平均功能,则设置ADC平均采样点个数。

  • 第19行: 打开ADC硬件平均功能。

  • 第23~29行:配置ADC采集触摸屏坐标的输入通道,在4线模式中,测量X坐标时,ADC的channel1连接到ynlr测量 x轴坐标电压;测量y坐标时,ADC的channel3连接到xnur测量y轴坐标电压。

  • 第31行: 启动ADC自动校准,imx6ull_adc_auto_calibration()函数的代码如下:

static bool imx6ull_adc_auto_calibration(struct imx6ull_rests *ts)
{
  struct imx6ull_adc *adc_regs = ts->adc_regs;
  unsigned int value;

  adc_regs->CFG &= ~(0x1 << 13);  /* 选择软件触发 */
  adc_regs->GS  |= (0x1 << 1);    /* 清除校准完成标记 */

  adc_regs->GC |= (0x1 << 7);     /* 启动ADC校准 */

  mdelay(100);                    /* 等待校准完成 */

  if (adc_regs->GC & (0x1 << 7))  /* 判断校准是否完成, 校准完成该寄存器的bit[7] 会自动清0*/
    return false;

  if (adc_regs->GS &  (0x1 << 1)) /* 判断,校准失败该寄存器的bit[1] 被置1 */
    return false;

  if ((adc_regs->HS & 0x01) == 0) /* 判断是否转换完成 */
    return false;

  value = adc_regs->R[0];         /* 通过读adc_regs->R[0]清除adc_regs->HS转换完成标记 */

  adc_regs->CFG |= (0x1 << 13);   /* 选择硬件触发 */

  return true;
}

上面的代码,需要注意的是在启动ADC校准之前ADC的触发方式必须设置为软件触发,否则无法启动ADC校准。校准完成之后,我们 需要把ADC的触发方式设置为硬件触发,这样TSC控制器可以通过给ADC的触发器发信号控制ADC采集坐标值。

2.3.5.2. TSC 初始化

TSC初始化配置主要包含以下几个方面:

  • 测量延迟时间设置、根据使用的电阻屏配置为4线或者5线检测模式、使能自动测量;

  • 设置电阻屏的预充电时间;

  • 中断和中断信号使能;

  • 启动触摸检测。

具体的初始化代码如下:

TSC 初始化函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static int imx6ull_tsc_config(struct imx6ull_rests *ts)
{
   struct imx6ull_tsc *tsc_regs = ts->tsc_regs;

  tsc_regs->BASIC_SETTING |= ts->measure_delay_time << 8;   /* 设置测量延迟时间 */
  tsc_regs->BASIC_SETTING &= ~(0x1 << 4);                   /* 设置4线检测模式 */
  tsc_regs->BASIC_SETTING |= (0x1 << 0);                    /* 使能自动测量 */

  tsc_regs->PS_INPUT_BUFFER_ADDR = ts->pre_charge_time;     /* 设置预充电时间 */

  tsc_regs->INT_EN = (0x1 << 0);                            /* 测量中断使能 */

  tsc_regs->INT_SIG_EN |= (0x1 << 0);                       /* 测量信号使能 */
  tsc_regs->INT_SIG_EN |= (0x1 << 8);                       /* 有效信号使能 */

  tsc_regs->FLOW_CONTROL |= (0x1 << 12);                    /* 启动触摸检测, 当检测到触摸时,开始测量 */
  tsc_regs->FLOW_CONTROL &= ~(0x1 << 16);                   /* 使能TSC */

  return 0;
}

2.3.6. 中断处理函数

中断处理函数 (位于 linux_driver/touch_screen_resisitive/resisitive_touchscreen.c):

static irqreturn_t tsc_irq_fn(int irq, void *dev_id)
{
  struct imx6ull_rests *ts = dev_id;
  unsigned int status;
  unsigned int value;
  unsigned int x;
  unsigned int y;

  /* 读取状态寄存器 */
  status = ts->tsc_regs->INT_STATUS;

  /* 清除坐标测量中断信号标记 */
  ts->tsc_regs->INT_STATUS = 0x01;

  /* 启动触摸检测 */
  ts->tsc_regs->FLOW_CONTROL |= (1 << 12);

  if (status & 0x1)
  {
    value = ts->tsc_regs->MEASEURE_VALUE;
    x = (value >> 16) & 0x0fff;
    y = value & 0x0fff;

    if (!tsc_wait_detect_mode(ts) || gpiod_get_value_cansleep(ts->xnur_gpio))
    {
      input_report_key(ts->input_dev, BTN_TOUCH, 1);  /* 按下 */
      input_report_abs(ts->input_dev, ABS_X, x);
      input_report_abs(ts->input_dev, ABS_Y, y);
    }
    else
    {
      input_report_key(ts->input_dev, BTN_TOUCH, 0);  /* 松开 */
    }

    input_sync(ts->input_dev);
  }

  return IRQ_HANDLED;
}
  • 第10、13行:当坐标测量完成后再次检测到有触摸,此时会产生一个坐标测量完中断,并把tsc_regs->INT_STATUS寄存器的bit[0] 坐标测量中断状态标记置1,在中断处理函数中需要向该位写1清0。

  • 第16行:重新启动触摸检测。

  • 第18行:判断该中断是否是坐标测量中断,如果是,接下来就读取测量的坐标值。

  • 第24行:在触摸检测状态下,读取“xnur”对应gpio的逻辑值,当读取到的逻辑值为1时,说明有触摸。 (注: 在设备树中,gpio的有效值为GPIO_ACTIVE_LOW时,读到的逻辑值与实际的物理电平相反)

  • 第26~28行:向输入子系统上报触摸按下、x轴坐绝对位移、y轴坐标绝对位移事件。

  • 第32行:向输入子系统上报触摸松开事件。

  • 第35行:向输入子系统上报同步事件。

2.4. 实验准备

在板卡上的部分GPIO可能会被系统占用,在使用前请根据需要修改 /boot/uEnv.txt 文件, 可注释掉某些设备树插件的加载,重启系统,释放相应的GPIO引脚。

如本节实验中,可能在鲁班猫系统中默认使能了 ADC1 LED 电容屏 的设备功能,GPIO1_IO03、GPIO1_IO03引脚被占用后,设备树可能无法再加载或驱动中无法再申请对应的资源。

方法参考如下:

broken

取消 ADC1 LED 电容屏 设备树插件,以释放系统对应系统资源,操作如下:

broken

dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-ts-res-4wires.dtbo

如若运行代码时出现“Device or resource busy”或者运行代码卡死等等现象, 请按上述情况检查并按上述步骤操作。

如出现 Permission denied 或类似字样,请注意用户权限,大部分操作硬件外设的功能,几乎都需要root用户权限,简单的解决方案是在执行语句前加入sudo或以root用户运行程序。

2.4.1. 编译设备树插件

将 linux_driver/touch_screen_resisitive/imx-fire-ts-res-4wires-overlay.dts 拷贝到 内核源码/arch/arm/boot/dts/overlays 目录下, 并修改同级目录下的Makefile,追加 imx-fire-ts-res-4wires.dtbo 编译选项。然后执行如下命令编译设备树插件:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig

make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

编译成功后生成同名的设备树插件文件(imx-fire-ts-res-4wires.dtbo)位于 内核源码/arch/arm/boot/dts/overlays 目录下。

2.4.2. 编译驱动程序

将 /linux_driver/touch_screen_resisitive 拷贝到内核源码同级目录,执行里面的MakeFile,生成resisitive_touchscreen.ko。

1

2.5. 驱动测试

2.5.1. 加载设备树插件和驱动文件

将设备树插件拷贝到开发板 /usr/lib/linux-image-4.19.35-imx6/overlays/ 目录下,并且在/boot/uEnv.txt中添加 dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-ts-res-4wires.dtbo ,然后 sudo reboot 重启开发板。

1

加载驱动程序 insmod resisitive_touchscreen.ko ,驱动程序加载成功打印match successed。

加载驱动程序

2.5.2. 测试
2.5.2.1. 使用evtest测试屏幕

通过以下命令下载evtest工具

sudo apt install evtest

在终端上执行evtest命令并选择触摸屏进行测试,请根据具体输入设备选择正确设备。测试结果如下:

1

使用evtest获取得到的是原始的数值,需要用户自行进行坐标转换。

2.5.2.2. 使用libts校准电阻触摸屏

与电容屏不同,电阻触摸屏获取得到的是原始数据并不是坐标值,想要获取相关的坐标点需要对原始数据进行换算, libts是一种实用的触摸工具,能够用于触摸屏的校准,将触摸位置与屏幕显示位置统一起来。

本小节实验也需要开启显示屏相关设备树插件,在 /boot/uEnv.txt 中,打开lcd的设备树插件,如下图所示:

1

通过以下命令下载libts-bin工具

sudo apt install libts-bin

使用以下命令进行屏幕校准,执行以下两条命令之后,显示屏将会显示需要点击的位置,依次点击完成后完成屏幕。

  • 设置环境变量,并将/dev/input/event2替换为自己的触摸屏输入设备:

export TSLIB_TSDEVICE=/dev/input/event2
  • 进行屏幕校准

ts_calibrate
  • 终端的打印信息如下:

1

执行ts_print命令后触摸电阻屏将会打印出相对应的坐标,如下所示:

1

关于libts工具使用的详细说明可参考以下链接: https://github.com/libts/tslib

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/948812.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

8、RAG论文笔记(Retrieval-Augmented Generation检索增强生成)

RAG论文笔记 1、 **研究背景与动机**2、方法概述3、RAG 模型架构3.1总体架构3.2 Generator&#xff08;生成器&#xff09;3.3 检索器&#xff08;Retriever&#xff09;3.4训练&#xff08;Training&#xff09;3.5**解码方法**&#xff08;求近似 &#xff09;3.6微调的参数 …

简易CPU设计入门:通用寄存器的读写

项目代码下载 请大家首先准备好本项目所用的源代码。如果已经下载了&#xff0c;那就不用重复下载了。如果还没有下载&#xff0c;那么&#xff0c;请大家点击下方链接&#xff0c;来了解下载本项目的CPU源代码的方法。 下载本项目代码 准备好了项目源代码以后&#xff0c;我…

【动手学电机驱动】STM32-MBD(3)Simulink 状态机模型的部署

STM32-MBD&#xff08;1&#xff09;安装 Simulink STM32 硬件支持包 STM32-MBD&#xff08;2&#xff09;Simulink 模型部署入门 STM32-MBD&#xff08;3&#xff09;Simulink 状态机模型的部署 【动手学电机驱动】STM32-MBD&#xff08;3&#xff09;Simulink 状态机模型部署…

Linux运维相关基础知识(二)

系列文章目录 Linux常用命令 linux 账号管理与权限设定 Linux运维相关基础知识 文章目录 系列文章目录前言1. 自动任务执行at 与 atdcrontab 与 crond 2. SELinuxtty多任务管理与进程管理相关的命令/proc/* 文件的意义SELinux 3. 守护进程早期SystemV的init管理行为中daemon…

K8s集群平滑升级(Smooth Upgrade of K8S Cluster)

简介&#xff1a; Kubernetes ‌ &#xff08;简称K8s&#xff09;是一个开源的容器编排和管理平台&#xff0c;由Google开发并维护。它最初是为了解决谷歌内部大规模容器管理的问题而设计的&#xff0c;后来在2014年开源&#xff0c;成为云原生技术的核心组成部分。‌‌1 K8…

党员学习交流平台

本文结尾处获取源码。 本文结尾处获取源码。 本文结尾处获取源码。 一、相关技术 后端&#xff1a;Java、JavaWeb / Springboot。前端&#xff1a;Vue、HTML / CSS / Javascript 等。数据库&#xff1a;MySQL 二、相关软件&#xff08;列出的软件其一均可运行&#xff09; I…

uniapp--HBuilder开发

提示&#xff1a;本文为学习内容&#xff0c;若有错误&#xff0c;请联系作者&#xff0c;谦虚受教。 文章目录 前言一、下载HBuilder二、添加modbus相关库1.下载nodejs2.下载modbus库3.项目添加modbus库 三、HBuilder相关功能语句1.文件夹说明2.消息信息框3.开关按钮4.选中按钮…

GraphRAG实践:neo4j试用

文章目录 前言欢迎界面示例数据库使用大模型生成查询语句总结 前言 上回说道&#xff0c;我们使用docker部署了一个neo4j。 我们现在对它进行一些试用。 欢迎界面 在浏览器中输入http://localhost:7474/ 输入对应的东西&#xff0c;点击connect 现在咱的数据库里什么都没有…

两种分类代码:独热编码与标签编码

目录 一、说明 二、理解分类数据 2.1 分类数据的类型&#xff1a;名义数据与序数数据 2.2 为什么需要编码 三、什么是独热编码&#xff1f; 3.1 工作原理&#xff1a;独热编码背后的机制 3.2 应用&#xff1a;独热编码的优势 四、什么是标签编码&#xff1f; 4.1 工作原理&…

【AWS SDK PHP】This operation requests `sigv4a` auth schemes 问题处理

使用AWS SDK碰到的错误&#xff0c;其实很简单&#xff0c;要装个扩展库 保持如下 Fatal error: Uncaught Aws\Auth\Exception\UnresolvedAuthSchemeException: This operation requests sigv4a auth schemes, but the client currently supports sigv4, none, bearer, sigv4-…

限时特惠,香港服务器,低至53元/年

家人们谁懂啊&#xff01;香港服务器这价格简直逆天了&#xff0c;居然比内地的还便宜&#xff01;就拿阿里云来说&#xff0c;人家最低配置的服务器&#xff0c;价格都很难做到这么亲民。 最低配的就不说了&#xff0c;2 核 4G 的配置&#xff0c;应对日常业务稳稳当当&#x…

USB子系统学习(一)USB电气信号

文章目录 1、声明2、USB协议概述3、USB电气信号3.1、USB基础概念3.1.1、低速/全速信号电平3.1.2、高速信号电平 3.2、学习目标3.3、设备断开与连接3.3.1、连接3.3.2、断开 3.4、复位3.5、设备速率识别3.5.1、低速/全速3.5.2、高速 3.6、数据信号3.6.1、低速/全速的SOP和EOP3.6.…

【机器学习篇】从新手探寻到算法初窥:数据智慧的开启之门

文章目录 【机器学习篇】从新手探寻到算法初窥&#xff1a;数据智慧的开启之门前言一、什么是机器学习&#xff1f;二、机器学习的基本类型1. 监督学习&#xff08;Supervised Learning&#xff09;2. 无监督学习&#xff08;Unsupervised Learning&#xff09;3. 半监督学习&a…

SQL-Server链接服务器访问Oracle数据

SQL Server 链接服务器访问 Oracle 离线安装 .NET Framework 3.5 方法一&#xff1a;使用 NetFx3.cab 文件 下载 NetFx3.cab 文件&#xff0c;并将其放置在 Windows 10 系统盘的 C:Windows 文件夹中。 以管理员身份运行命令提示符&#xff0c;输入以下命令并回车&#xff1a; …

【C++】矩阵转置问题详解与优化

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目解析&#x1f4af;第一种实现方式&#xff1a;我的初始做法实现思路优缺点分析 &#x1f4af;第二种实现方式&#xff1a;我的优化做法实现思路优缺点分析 &#x1f4a…

比QT更高效的一款开源嵌入式图形工具EGT-Ensemble Graphics Toolkit

文章目录 EGT-Ensemble Graphics Toolkit介绍EGT具备非常高的图形渲染效率EGT采用了非常优秀的开源2D图形处理引擎-Cairo开源2D图形处理引擎Cairo的优势Cairo 2D图像引擎的性能Cairo 2D图像引擎的实际应用案例彩蛋 - 开源EDA软件KiCAD也在使用Cairo EGT高效的秘诀还有哪些Cairo…

信息系统管理工程师教程第2版(2024年最新版)

信息系统管理工程师教程第2版 目录 第 1 章 信息化发展 第 2 章 信息技术发展 第 3 章 信息系统架构 第 4 章 信息系统治理 第 5 章 信息技术服务管理 第 6 章 软件开发过程管理 第 7 章 系统集成实施管理 第 8 章 信息系统运维管理 第 9 章 云服务及其运营…

Science Robotics让软机器人“活”得更久的3D打印!

软机器人硬件在医疗、探索无结构环境等领域有广泛应用&#xff0c;但其生命周期有限&#xff0c;导致资源浪费和可持续性差。软机器人结合软硬组件&#xff0c;复杂组装和拆卸流程使其难以维修和升级。因此&#xff0c;如何延长软机器人的生命周期并提高其可持续性成为亟待解决…

通过Dockerfile来实现项目可以指定读取不同环境的yml包

通过Dockerfile来实现项目可以指定读取不同环境的yml包 1. 挂载目录2. DockerFile3. 运行脚本deploy.sh4. 运行查看日志进入容器 5. 接口测试修改application-dev.yml 6. 优化Dockerfile7. 部分参数解释8. 优化不同环境下的日志也不同调整 Dockerfile修改部署脚本 deploy.sh重新…

AutoSar架构学习笔记

1.AUTOSAR&#xff08;Automotive Open System Architecture&#xff0c;汽车开放系统架构&#xff09;是一个针对汽车行业的软件架构标准&#xff0c;旨在提升汽车电子系统的模块化、可扩展性、可重用性和互操作性。AUTOSAR的目标是为汽车电子控制单元&#xff08;ECU&#xf…