Linux学习笔记--触摸屏驱动

关键数据结构

1. 触摸控制器寄存器结构

复制代码
struct qemu_ts_con {
    volatile unsigned int pressure;  // 压力/触摸状态
    volatile unsigned int x;         // X 坐标
    volatile unsigned int y;         // Y 坐标  
    volatile unsigned int clean;     // 清理标志
};
  • volatile 确保编译器不会优化对这些寄存器的访问

  • 这个结构体映射到硬件的内存区域

2. 全局变量

复制代码
static struct input_dev *g_input_dev;  // 输入设备
static int g_irq;                      // 中断号
static struct qemu_ts_con *ts_con;     // 映射的硬件寄存器
struct timer_list ts_timer;            // 定时器

核心机制:中断 + 定时器

1. 定时器回调函数

复制代码
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));
    }
}
  • 在触摸持续期间,定时器每 10ms 触发一次

  • 持续上报触摸坐标,实现触摸移动的跟踪

为什么需要定时器轮询?

触摸移动的连续性

  • 中断只在触摸开始和结束时触发

  • 但在触摸过程中,手指可能持续移动

  • 定时器轮询可以捕获这些连续的坐标变化

时间间隔选择:10ms

TOUCHSCREEN_POLL_TIME_MS 10 的含义:

  • 100Hz 采样率:每秒采样 100 次坐标

  • 平衡性能与流畅度

    • 太频繁:CPU 占用率高

    • 太稀疏:触摸轨迹不连贯

  • 典型触摸屏的采样率在 60-120Hz 之间

内核定时器机制
定时器初始化:
复制代码
setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL);
时间计算:
复制代码
jiffies + msecs_to_jiffies(10)
  • jiffies: 当前时间(从系统启动开始的节拍数)

  • msecs_to_jiffies(10): 将 10ms 转换为对应的节拍数

  • 结果:定时器在"当前时间 + 10ms"后再次触发

与中断处理函数的协作
中断函数中的定时器启动:
复制代码
// 在 input_dev_demo_isr 中:
if (ts_con->pressure)  // 触摸按下
{
    // ... 上报初始坐标 ...
    
    /* start timer */
    mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
}

2. 中断服务程序

复制代码
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);
        
        // 注意:这里应该停止定时器,但代码中缺失了 del_timer
    }
    
    return IRQ_HANDLED;
}

驱动probe函数详解

1. 设备信息获取

复制代码
// 从设备树获取 GPIO
gpio = of_get_gpio(pdev->dev.of_node, 0);

// 获取内存映射资源
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ts_con = ioremap(io->start, io->end - io->start + 1);

2. 输入设备设置

复制代码
/* set 1: which type event ? */	
__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 2: which event ? */	
__set_bit(BTN_TOUCH, g_input_dev->keybit);  // 触摸按键
__set_bit(ABS_X, g_input_dev->absbit);      // X 坐标
__set_bit(ABS_Y, g_input_dev->absbit);      // Y 坐标

/* set 3: event params ? */	
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);

__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit);

用于设置输入设备的属性 ,表明这是一个直接输入设备

__set_bit 宏:

复制代码
// 内核中的位操作宏
#define __set_bit(nr, addr) set_bit(nr, addr)
  • 作用:将位图中的特定位设置为 1

  • nr:要设置的位号(这里是 INPUT_PROP_DIRECT

  • addr:位图地址(这里是 g_input_dev->propbit

参数说明:

  • INPUT_PROP_DIRECT:输入设备属性常量,值为 0

  • g_input_dev->propbit:输入设备的属性位图

INPUT_PROP_DIRECT 的含义

直接输入设备(Direct Input Device):

  • 定义:用户直接在设备表面上操作,操作位置与光标位置直接对应

  • 典型例子

    • 触摸屏(Touchscreen)

    • 数字化仪(Graphics Tablet)

    • 触摸板(Touchpad)

3. 硬件初始化

复制代码
// 内存映射
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ts_con = ioremap(io->start, io->end - io->start + 1);

// 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);

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

工作流程

1. 触摸按下流程

复制代码
GPIO 中断(上升沿) → 中断处理函数 → 上报触摸开始 → 启动定时器 → 定时器轮询坐标

2. 触摸移动流程

复制代码
定时器每10ms触发 → 读取坐标 → 上报新位置 → 重置定时器

3. 触摸释放流程

复制代码
GPIO 中断(下降沿) → 中断处理函数 → 上报触摸结束 → 停止定时器(代码中缺失)

设备树配置

这个驱动期望的设备树节点:

复制代码
input_dev_demo: input_dev_demo {
    compatible = "100ask,input_dev_demo";
    reg = <0x12345000 0x1000>;        // 内存映射区域
    gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;  // GPIO 引脚
    interrupt-parent = <&gpio1>;
    interrupts = <1 IRQ_TYPE_EDGE_BOTH>;  // 双边沿触发
};
相关推荐
Gin3873 小时前
mooc自动互评脚本笔记---2025年10月11日
笔记
半路程序员3 小时前
Go语言学习(四)
开发语言·学习·golang
蒙奇D索大4 小时前
【C语言加油站】C语言文件操作详解:从“流”的概念到文件的打开与关闭
c语言·开发语言·笔记·学习·改行学it
摇滚侠4 小时前
Spring Boot 3零基础教程,依赖管理机制,笔记06
spring boot·笔记·后端
数据库生产实战4 小时前
Oracle LOB使用入门和简单使用,提供学习用的测试用例!
数据库·学习·oracle
聪明的笨猪猪5 小时前
Java Spring “事务” 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
爱喝水的鱼丶5 小时前
SAP-ABAP:SAP中的用户确认对话框:深入理解与实践POPUP_TO_CONFIRM
运维·开发语言·学习·sap·abap
lingggggaaaa5 小时前
小迪安全学习笔记(一百零二讲)—— 漏扫项目篇&PoC开发&Yaml语法&插件一键生成&匹配结果&交互提取
笔记·学习·安全·网络安全·交互
里昆5 小时前
【COMSOL】结构力学仿真(压缩弹性体)案例心得
学习