首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序

  • 25-03-04 13:41
  • 3711
  • 10944
blog.csdn.net

往期内容

本专栏往期内容:

  1. input子系统的框架和重要数据结构详解-CSDN博客
  2. input device和input handler的注册以及匹配过程解析-CSDN博客
  3. input device和input handler的注册以及匹配过程解析-CSDN博客
  4. 编写一个简单的Iinput_dev框架-CSDN博客
  5. GPIO按键驱动分析与使用:input_dev层-CSDN博客

I2C子系统专栏:

  1. 专栏地址:IIC子系统_憧憬一下的博客-CSDN博客
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序


目录

  • 往期内容
  • 前言
  • 1. 驱动程序框架
  • 2. 设备树示例
  • 3. 驱动程序分析
    • 3.1 分配/设置/注册input_dev
    • 3.2 注册中断处理函数
    • 3.3 中断处理函数分析
  • 4.编写

前言

  • Linux 4.x内核

    • Documentation\devicetree\bindings\input\touchscreen\goodix.txt
    • drivers/input/touchscreen/gt9xx/gt9xx.c ?gt9xx.c
  • 设备树

    • IMX6ULL:Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts

本节主要讲解内核中提供的gt9xx.c,来看看是如何通过input子系统来实现一个IIC协议的传输,之后在此基础上编写一个简单的input子系统的IIC驱动

1. 驱动程序框架

img

2. 设备树示例

&i2c2 {
	gt9xx@5d {
			compatible = "goodix,gt9xx";
			reg = <0x5d>;
			status = "okay";
			interrupt-parent = <&gpio1>;
			interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;
			/*pinctrl-1 = <&pinctrl_tsc_irq>;*/
			/*
			 pinctrl-names = "default", "int-output-low", "int-output-high", "int-input";
			 pinctrl-0 = <&ts_int_default>;
			 pinctrl-1 = <&ts_int_output_low>;
			 pinctrl-2 = <&ts_int_output_high>;
			 pinctrl-3 = <&ts_int_input>;
			*/
			reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
			irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;
			irq-flags = <2>;                /*1:rising 2: falling*/

			touchscreen-max-id = <5>;
			touchscreen-size-x = <800>;
			touchscreen-size-y = <480>;
			touchscreen-max-w = <1024>;
			touchscreen-max-p = <1024>;
			/*touchscreen-key-map = <172>, <158>;*/ /*KEY_HOMEPAGE, KEY_BACK*/

			goodix,type-a-report = <0>;
			goodix,driver-send-cfg = <0>;
			goodix,create-wr-node = <1>;
			goodix,wakeup-with-reset = <0>;
			goodix,resume-in-workqueue = <0>;
			goodix,int-sync = <0>;
			goodix,swap-x2y = <0>;
			goodix,esd-protect = <0>;
			goodix,pen-suppress-finger = <0>;
			goodix,auto-update = <0>;
			goodix,auto-update-cfg = <0>;
			goodix,power-off-sleep = <0>;
			/* ...... */
	};
};	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

这段设备树(Device Tree)定义配置了一个基于 I2C 总线的 Goodix GT9xx 系列触摸屏设备,设备地址为 0x5d。

1. 节点和基础属性

&i2c2 {
    gt9xx@5d {
        compatible = "goodix,gt9xx";
        reg = <0x5d>;
  • 1
  • 2
  • 3
  • 4
  • &i2c2:指向 I2C 总线控制器的节点,这意味着该设备挂载在 I2C 总线 2 上。
  • gt9xx@5d:表示该触摸屏设备的 I2C 地址为 0x5d。
  • compatible = "goodix,gt9xx";:指定设备兼容字符串,匹配 Goodix 的 GT9xx 系列触摸屏设备驱动程序。
  • reg = <0x5d>;:设备的 I2C 地址,定义为 0x5d。

2. 中断与引脚控制配置

        status = "okay";
        interrupt-parent = <&gpio1>;
        interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;
  • 1
  • 2
  • 3
  • 4
  • 5
  • status = "okay";:设备状态,标记为启用。
  • interrupt-parent = <&gpio1>;:指定中断的父节点为 gpio1,即 GPIO 控制器 1。
  • interrupts = <5 IRQ_TYPE_EDGE_FALLING>;:中断配置,使用 GPIO1 控制器的第 5 个引脚,触发模式为下降沿触发(IRQ_TYPE_EDGE_FALLING)。
  • pinctrl-names 和 pinctrl-0:配置引脚控制器,pinctrl-names 指定了配置的名字,pinctrl-0 则定义了相关引脚配置(pinctrl_tsc_reset 和 pinctrl_touchscreen_int),用于触摸屏重置和中断引脚初始化。

3. GPIO 和中断标志

        reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
        irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;
        irq-flags = <2>; 
  • 1
  • 2
  • 3
  • reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;:触摸屏的复位引脚定义在 gpio5 的第 2 引脚,信号低电平有效。
  • irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;:触摸屏的中断引脚配置为 gpio1 控制器的第 5 引脚,设置为下降沿触发。
  • irq-flags = <2>;:中断触发标志,2 表示下降沿触发。

4. 触摸屏参数

        touchscreen-max-id = <5>;
        touchscreen-size-x = <800>;
        touchscreen-size-y = <480>;
        touchscreen-max-w = <1024>;
        touchscreen-max-p = <1024>;
  • 1
  • 2
  • 3
  • 4
  • 5
  • touchscreen-max-id = <5>;:最大触摸点数为 5,表示最多支持 5 个手指的多点触控。
  • touchscreen-size-x 和 touchscreen-size-y:触摸屏 X、Y 轴的分辨率,分别为 800 和 480。
  • touchscreen-max-w 和 touchscreen-max-p:触控宽度和压力的最大值,均为 1024。

5. Goodix 驱动特性

        goodix,type-a-report = <0>;
        goodix,driver-send-cfg = <0>;
        goodix,create-wr-node = <1>;
        goodix,wakeup-with-reset = <0>;
        goodix,resume-in-workqueue = <0>;
        goodix,int-sync = <0>;
        goodix,swap-x2y = <0>;
        goodix,esd-protect = <0>;
        goodix,pen-suppress-finger = <0>;
        goodix,auto-update = <0>;
        goodix,auto-update-cfg = <0>;
        goodix,power-off-sleep = <0>;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • Goodix 特定属性:goodix,xxx 属性定义了一些特性和行为,例如:

    • goodix,type-a-report 和 goodix,driver-send-cfg:控制是否启用 A 类型报告和驱动是否发送配置信息。
    • goodix,create-wr-node:创建 WR(写)节点的标志。
    • goodix,wakeup-with-reset 和 goodix,power-off-sleep:控制唤醒和电源管理的行为。
    • 其他 Goodix 触摸屏特性配置,按需要启用或禁用。

这些属性通过设备树配置,使系统能够正确初始化并管理触摸屏设备。

3. 驱动程序分析

gt9xx.c

3.1 分配/设置/注册input_dev

img

gtp_probe
	ret = gtp_request_input_dev(ts);
			ts->input_dev = input_allocate_device();
			......
			ret = input_register_device(ts->input_dev);

	ret = gtp_request_irq(ts);	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.2 注册中断处理函数

img

ret = request_threaded_irq(ts->client->irq, NULL,
				gtp_irq_handler,
				ts->pdata->irq_flags | IRQF_ONESHOT,
				ts->client->name,
				ts);
  • 1
  • 2
  • 3
  • 4
  • 5

3.3 中断处理函数分析

通过I2C函数( 2c_tansfer)读取数据、上报数据。

img

static irqreturn_t gtp_irq_handler(int irq, void *dev_id)
{
	struct goodix_ts_data *ts = dev_id;

	gtp_work_func(ts);
	return IRQ_HANDLED;
}

static void gtp_work_func(struct goodix_ts_data *ts)
{
    u8 point_state = 0;  // 保存触摸点的状态
    u8 key_value = 0;    // 保存按键的状态
    s32 i = 0;           // 循环变量,用于迭代按键事件
    s32 ret = -1;        // 函数调用返回值
    static u8 pre_key;   // 保存上一次的按键值,用于识别按键变化
    struct goodix_point_t points[GTP_MAX_TOUCH_ID]; // 存储触摸点信息

    // 检查是否正在执行复位操作,若是,则退出函数
    if (test_bit(PANEL_RESETTING, &ts->flags))
        return;

    // 检查工作线程是否启用,若未启用,则退出函数
    if (!test_bit(WORK_THREAD_ENABLED, &ts->flags))
        return;

    // 手势事件处理,检测滑动唤醒功能是否启用,且设备是否处于休眠模式
    if (ts->pdata->slide_wakeup && test_bit(DOZE_MODE, &ts->flags)) {
        // 调用手势处理函数 gtp_gesture_handler
        ret = gtp_gesture_handler(ts);
        if (ret) {
            // 若处理失败,记录错误日志
            dev_err(&ts->client->dev,
                    "Failed handler gesture event %d\n", ret);
        }
        return;  // 处理完成后退出函数
    }

    // 获取触摸点的状态及按键状态
    -----------(1)
    point_state = gtp_get_points(ts, points, &key_value); 
    if (!point_state) {
        // 若未检测到有效的触摸点,记录调试日志
        dev_dbg(&ts->client->dev, "Invalid finger points\n");
        return;  // 没有有效触摸点,直接返回
    }

    // 处理触摸按键事件
    if (key_value & 0xf0 || pre_key & 0xf0) {  // 判断是否有按键事件
        // 处理手写笔的按键
        switch (key_value) {
            case 0x40: // 两个按键都按下
                input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
                input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
                break;
            case 0x10: // 按下第一个按键
                input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
                input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
                dev_dbg(&ts->client->dev, "pen button1 down\n");
                break;
            case 0x20: // 按下第二个按键
                input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
                input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
                break;
            default: // 没有按键按下,恢复初始状态
                input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
                input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
                dev_dbg(&ts->client->dev, "button1 up\n");
                break;
        }
        input_sync(ts->input_dev); // 同步输入事件
        pre_key = key_value;       // 更新上次按键状态
    } 
    else if (key_value & 0x0f || pre_key & 0x0f) {  // 判断是否有面板按键事件
        // 遍历所有按键,检测是否有按键状态发生变化
        for (i = 0; i < ts->pdata->key_nums; i++) {
            if ((pre_key | key_value) & (0x01 << i))
                input_report_key(ts->input_dev,
                                 ts->pdata->key_map[i],     // 具体按键映射
                                 key_value & (0x01 << i));  // 按键状态
        }
        input_sync(ts->input_dev); // 同步输入事件
        pre_key = key_value;       // 更新上次按键状态
    }

    // 选择不同的报告格式进行触摸点的处理
    if (!ts->pdata->type_a_report)
        gtp_mt_slot_report(ts, point_state & 0x0f, points);  // Slot方式
        ----------------------(2)
    else
        gtp_type_a_report(ts, point_state & 0x0f, points);   // Type A方式
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

(1)point_state = gtp_get_points(ts, points, &key_value); : 从 I2C 总线上读取触摸点的数据和触摸状态。提取坐标、压力值、工具类型等信息,若需要则交换 X/Y 坐标。 将手写笔和普通触摸点区分,按需要调整触摸点数据。完成数据读取后,通过发送结束命令结束当前触摸事件。 ret = gtp_i2c_read(ts->client, point_data, 12); //这里内部就调用到了i2c_transfer发起传输

static u8 gtp_get_points(struct goodix_ts_data *ts,
                         struct goodix_point_t *points,
                         u8 *key_value)
{
    int ret;                // 保存 I2C 传输返回状态
    int i;                  // 循环变量
    u8 *coor_data = NULL;   // 指向触摸坐标数据的指针
    u8 finger_state = 0;    // 保存手指状态信息
    u8 touch_num = 0;       // 保存当前触摸点的数量
    u8 end_cmd[3] = {       // 用于向触摸屏发送 "结束" 命令
        GTP_READ_COOR_ADDR >> 8,
        GTP_READ_COOR_ADDR & 0xFF,
        0
    };
    u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH_ID + 1] = {  // 存储触摸点的数据
        GTP_READ_COOR_ADDR >> 8,
        GTP_READ_COOR_ADDR & 0xFF
    };

    // 读取触摸点的坐标数据
    ret = gtp_i2c_read(ts->client, point_data, 12);   //这里内部就调用到了i2c_transfer发起传输
    if (ret < 0) {
        dev_err(&ts->client->dev, "I2C transfer error. errno:%d\n ", ret);
        return 0;  // 读取失败返回0
    }
    finger_state = point_data[GTP_ADDR_LENGTH];  // 获取触摸屏状态
    if (finger_state == 0x00)                    // 若无触摸点
        return 0;

    // 获取触摸点数量(低 4 位表示触摸点数量)
    touch_num = finger_state & 0x0f;
    // 检查触摸状态是否合法,如超出最大触摸数或没有触摸
    if ((finger_state & MASK_BIT_8) == 0 || touch_num > ts->pdata->max_touch_id) {
        dev_err(&ts->client->dev, "Invalid touch state: 0x%x", finger_state);
        finger_state = 0;  // 若非法则重置触摸状态
        goto exit_get_point;  // 跳转到退出点
    }

    // 若有多个触摸点,则读取后续触摸点数据
    if (touch_num > 1) {
        u8 buf[8 * GTP_MAX_TOUCH_ID] = {
            (GTP_READ_COOR_ADDR + 10) >> 8,
            (GTP_READ_COOR_ADDR + 10) & 0xff
        };
        ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
        if (ret < 0) {
            dev_err(&ts->client->dev, "I2C error. %d\n", ret);
            finger_state = 0;
            goto exit_get_point;
        }
        memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
    }

    // 触摸按键事件,低 4 位标识按键事件
    *key_value = point_data[3 + 8 * touch_num];

    // 初始化触摸点信息数组
    memset(points, 0, sizeof(*points) * GTP_MAX_TOUCH_ID);
    for (i = 0; i < touch_num; i++) {
        coor_data = &point_data[i * 8 + 3];  // 获取触摸点的坐标数据
        points[i].id = coor_data[0];         // 触摸点 ID
        points[i].x = coor_data[1] | (coor_data[2] << 8);  // 触摸点的 X 坐标
        points[i].y = coor_data[3] | (coor_data[4] << 8);  // 触摸点的 Y 坐标
        points[i].w = coor_data[5] | (coor_data[6] << 8);  // 触摸点的宽度
        points[i].p = coor_data[5] | (coor_data[6] << 8);  // 压力值

        // 判断是否需要交换 X/Y 坐标
        if (ts->pdata->swap_x2y)
            GTP_SWAP(points[i].x, points[i].y);

        dev_dbg(&ts->client->dev, "[%d][%d %d %d]\n", points[i].id, points[i].x, points[i].y, points[i].p);

        // 判断是否为手写笔触摸点
        if (points[i].id & 0x80) {
            points[i].tool_type = GTP_TOOL_PEN;
            points[i].id = 10;  // 手写笔 ID 固定为 10
            if (ts->pdata->pen_suppress_finger) {
                points[0] = points[i];
                memset(++points, 0, sizeof(*points) * (GTP_MAX_TOUCH_ID - 1));
                finger_state &= 0xf0;
                finger_state |= 0x01;  // 设置第一个触摸点为手写笔
                break;
            }
        } else {
            points[i].tool_type = GTP_TOOL_FINGER;
        }
    }

exit_get_point:
    // 若未处于 RAW 数据模式,写入结束命令以结束当前触摸事件
    if (!test_bit(RAW_DATA_MODE, &ts->flags)) {
        ret = gtp_i2c_write(ts->client, end_cmd, 3);
        if (ret < 0)
            dev_info(&ts->client->dev, "I2C write end_cmd error!");
    }
    return finger_state;  // 返回触摸屏状态
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

(2)gtp_irq_handler中你的gtp_mt_slot_report(ts, point_state & 0x0f, points); // Slot方式。

  • 函数主要负责报告多点触控的触摸点状态,包括其位置、工具类型和压力等。
  • 使用位图(cur_touch 和 pre_touch)跟踪当前和先前的触摸 ID,从而确定哪些触摸点是新触摸、哪些是结束触摸。
static void gtp_mt_slot_report(struct goodix_ts_data *ts, u8 touch_num, 
                               struct goodix_point_t *points)
{
    int i; // 循环变量
    u16 cur_touch = 0; // 当前触摸 ID 位图
    static u16 pre_touch; // 上一帧的触摸 ID 位图
    static u8 pre_pen_id; // 上一次触摸的笔 ID

    // 遍历每个可能的触摸点,最大触摸 ID 由 ts->pdata->max_touch_id 决定
    for (i = 0; i < ts->pdata->max_touch_id; i++) {
        // 检查当前触摸点是否有效
        if (touch_num && i == points->id) {
            // 设置当前触摸点的槽(slot)
            input_mt_slot(ts->input_dev, points->id);

            // 判断工具类型
            if (points->tool_type == GTP_TOOL_PEN) {
                // 如果是笔工具,报告笔的状态为真
                input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, true);
                pre_pen_id = points->id; // 更新上一次的笔 ID
            } else {
                // 否则为手指工具,报告状态为真
                input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
            }
            
            // 报告触摸点的 X 坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, points->x);
            // 报告触摸点的 Y 坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, points->y);
            // 报告触摸点的宽度
            input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, points->w);
            // 报告触摸点的压力
            input_report_abs(ts->input_dev, ABS_MT_PRESSURE, points->p);

            // 更新当前触摸 ID 位图
            cur_touch |= 0x01 << points->id;
            points++; // 移动到下一个触摸点
        } 
        // 如果在上一帧中这个槽是活跃的
        else if (pre_touch & (0x01 << i)) {
            // 设置当前槽为 i
            input_mt_slot(ts->input_dev, i);
            // 检查上次是否是笔 ID
            if (pre_pen_id == i) {
                // 如果是笔,报告笔的状态为假
                input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, false);
                /* 有效的 ID 应小于 10,所以将 ID 设置为 0xff 
                 * 来表示无效状态
                 */
                pre_pen_id = 0xff; // 重置笔 ID
            } else {
                // 否则报告手指状态为假
                input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
            }
        }
    }

    // 更新上一帧的触摸 ID 位图为当前帧
    pre_touch = cur_touch;
    // 同步当前的触摸帧
    input_mt_sync_frame(ts->input_dev);
    // 同步输入设备状态
    input_sync(ts->input_dev);
}
gtp_irq_handler
	gtp_work_func(ts);
		point_state = gtp_get_points(ts, points, &key_value);
			gtp_i2c_read
				i2c_transfer
		gtp_mt_slot_report(ts, point_state & 0x0f, points);
			input_mt_slot
			input_mt_report_slot_state
			input_report_abs
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

有所疑问可以先看:

I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客

编写一个简单的Iinput_dev框架-CSDN博客

4.编写

这里主要是参考?gpio_keys.c,但思路其实和上面介绍的差不多,设置Input_dev并注册、配置事件类型和支持的事件、请求中断注册中断等。

#define TOUCHSCREEN_POLL_TIME_MS 10 // 定义轮询时间,单位为毫秒

// 定义模拟触摸屏硬件数据结构
struct qemu_ts_con {
	volatile unsigned int pressure; // 触摸压力,0表示未触摸,非0表示触摸
	volatile unsigned int x;        // 触摸X坐标
	volatile unsigned int y;        // 触摸Y坐标
	volatile unsigned int clean;    // 清除标志
};

// 全局变量定义
static struct input_dev *g_input_dev;  // 输入设备
static int g_irq;                      // 中断号
static struct qemu_ts_con *ts_con;     // 指向模拟触摸屏的结构体
struct timer_list ts_timer;            // 定时器,用于轮询触摸状态

// 定时器回调函数,用于定期轮询触摸状态
static void ts_irq_timer(unsigned long _data)
{
	// 如果检测到触摸事件
	if (ts_con->pressure) {
		// 报告触摸位置
		input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
		input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
		input_sync(g_input_dev);

		// 重新启动定时器
		mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
	}
}

// 中断服务函数,触发触摸事件处理
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{
	// 如果有触摸事件发生
	if (ts_con->pressure) {
		// 报告触摸位置和触摸按键状态
		input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
		input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
		input_event(g_input_dev, EV_KEY, BTN_TOUCH, 1);
		input_sync(g_input_dev);

		// 启动定时器继续监控触摸状态
		mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
	} else {
		// 如果没有触摸,报告触摸键松开
		input_event(g_input_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(g_input_dev);
	}
	
	return IRQ_HANDLED;
}

// 设备初始化函数,负责资源分配和设备注册
static int input_dev_demo_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	int error;
	struct resource *io;
	int gpio;

	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	// 获取GPIO编号
	gpio = of_get_gpio(pdev->dev.of_node, 0);

	// 分配并初始化input_dev结构体
	g_input_dev = devm_input_allocate_device(dev);
	if (!g_input_dev)
		return -ENOMEM;

	// 配置input_dev的基础信息
	g_input_dev->name = "input_dev_demo";
	g_input_dev->phys = "input_dev_demo";
	g_input_dev->dev.parent = dev;
	g_input_dev->id.bustype = BUS_HOST;
	g_input_dev->id.vendor = 0x0001;
	g_input_dev->id.product = 0x0001;
	g_input_dev->id.version = 0x0100;

	// 配置事件类型和支持的事件
	__set_bit(EV_KEY, g_input_dev->evbit);
	__set_bit(EV_ABS, g_input_dev->evbit);
	__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit);
	__set_bit(BTN_TOUCH, g_input_dev->keybit);
	__set_bit(ABS_X, g_input_dev->absbit);
	__set_bit(ABS_Y, g_input_dev->absbit);

	// 配置触摸屏的绝对坐标范围
	input_set_abs_params(g_input_dev, ABS_X, 0, 0xffff, 0, 0);
	input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0);

	// 注册input_dev
	error = input_register_device(g_input_dev);
	if (error)
		return error;

	// 获取硬件寄存器的I/O资源并映射
	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	ts_con = ioremap(io->start, resource_size(io));

	// 获取并请求GPIO的中断号
	g_irq = gpio_to_irq(gpio);
	error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);
	if (error)
		return error;

	// 初始化定时器
	setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL);

	return 0;
}

// 设备卸载函数,释放资源
static int input_dev_demo_remove(struct platform_device *pdev)
{
	del_timer_sync(&ts_timer);       // 停止定时器
	iounmap(ts_con);                 // 取消内存映射
	free_irq(g_irq, NULL);           // 释放中断
	input_unregister_device(g_input_dev); // 注销设备
	return 0;
}

// 设备树匹配表
static const struct of_device_id input_dev_demo_of_match[] = {
	{ .compatible = "100ask,input_dev_demo", },
	{ },
};

// 平台驱动结构体定义
static struct platform_driver input_dev_demo_driver = {
	.probe  = input_dev_demo_probe,
	.remove = input_dev_demo_remove,
	.driver = {
		.name = "input_dev_demo",
		.of_match_table = input_dev_demo_of_match,
	}
};

// 模块初始化函数
static int __init input_dev_demo_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return platform_driver_register(&input_dev_demo_driver);
}

// 模块退出函数
static void __exit input_dev_demo_exit(void)
{
	platform_driver_unregister(&input_dev_demo_driver);
}

module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);

MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
文章知识点与官方知识档案匹配,可进一步学习相关知识
C技能树首页概览220410 人正在系统学习中
注:本文转载自blog.csdn.net的憧憬一下的文章"https://blog.csdn.net/caiji0169/article/details/143314922"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top