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>;  // 双边沿触发
};
相关推荐
阳光九叶草LXGZXJ6 小时前
达梦数据库-报错-15-列【XXX】长度超出定义
linux·运维·数据库·sql·学习
陈卫军老师6 小时前
陈卫军语录全集总结:12句话,一条主线
经验分享·笔记
mlidongfeng8 小时前
【学习】mKernel 分析
学习
一只小小的芙厨10 小时前
【线性DP】
学习·算法·动态规划
2501_9269783310 小时前
给自指系统接一个外部锚 —— 一个关于「用 AI 观察自己」的方法
人工智能·经验分享·笔记·机器学习·ai写作
li星野10 小时前
DO — Auxiliary Verb (One-Page Notes)
学习
minglie110 小时前
关系的有向管道模型
学习
~kiss~12 小时前
LangChain 基础学习
学习·langchain
一木 之林12 小时前
提示词工程学习总结:用 Few-shot 把大模型调教成金融文本分类、抽取与匹配的自动化流水线
人工智能·学习·计算机视觉·金融·分类
传奇开心果编程12 小时前
【Compose Multiplatform 跨端开发学与练】第7课 平台适配与互操作
android·windows·学习·ui·ios·kotlin·composer