嵌入式 Linux 使用 input 子系统读取触摸屏坐标(应用层代码详解)

Linux应用层读取触摸屏(input子系统)实战笔记

文章标题:嵌入式Linux 使用input子系统读取触摸屏坐标(应用层代码详解)

标签:#嵌入式Linux #input子系统 #触摸屏 #Linux应用开发

前言

在嵌入式开发中,触摸屏属于标准input输入设备,内核通过input子系统向上层应用提供统一接口。
用户空间不需要操作寄存器,直接读写 /dev/input/eventX 设备文件,就能获取触摸坐标、按下/抬起状态。
本文从零讲解完整代码、原理、易错点,适合嵌入式应用开发者入门与面试复习。

环境:Linux开发板 | 单点电阻屏(文末附带多点电容屏适配方案)

一、基础原理

内核input子系统向上暴露字符设备:/dev/input/event0~eventX
所有输入事件(按键、触摸屏、鼠标)统一使用结构体 struct input_event 封装上报。

struct input_event {
    struct timeval time;   // 事件发生时间戳
    __u16 type;    // 事件大类
    __u16 code;    // 事件子类型
    __s32 value;   // 事件对应数值
};

核心事件宏(来自头文件 <linux/input.h>

作用
EV_ABS 绝对坐标事件(触摸屏、摇杆)
ABS_X / ABS_Y 单点触摸X、Y坐标
EV_KEY 按键类事件
BTN_TOUCH 触摸按下/抬起检测
EV_SYN 同步事件,SYN_REPORT代表一帧触摸数据发送完毕

重点:触摸屏会分多次发送X坐标、Y坐标,必须等到SYN_REPORT才代表一组数据完整,不能读到XY就直接打印!

二、完整可编译代码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <linux/input.h>

int main(void)
{
    // 1. 打开触摸设备节点
    int touch_fd = open("/dev/input/event0", O_RDWR);
    if (touch_fd < 0)
    {
        fprintf(stderr,"touch open failed! errno:%d, msg:%s\n", errno, strerror(errno));
        return -1;
    }

    struct input_event ev;
    int tx = 0, ty = 0;

    // 2. 循环读取触摸事件
    while (1)
    {
        read(touch_fd, &ev, sizeof(ev));

        // 解析绝对坐标 X Y
        if (ev.type == EV_ABS)
        {
            if (ev.code == ABS_X)
            {
                tx = ev.value;
            }
            else if (ev.code == ABS_Y)
            {
                ty = ev.value;
            }
        }

        // 检测手指按下/抬起
        if (ev.type == EV_KEY && ev.code == BTN_TOUCH)
        {
            if (ev.value == 1)
            {
                printf("【触摸按下】\n");
            }
            else if (ev.value == 0)
            {
                printf("【触摸抬起】\n");
            }
        }

        // 收到同步标志,打印完整坐标
        if (ev.type == EV_SYN && ev.code == SYN_REPORT)
        {
            printf("触摸坐标 X = %d , Y = %d\n", tx, ty);
        }
    }

    close(touch_fd);
    return 0;
}

三、编译与运行

  1. 交叉编译(根据自己交叉编译器修改)
arm-linux-gnueabihf-gcc touch_test.c -o touch_test
  1. 开发板运行
# 普通用户通常权限不足,需要sudo
sudo ./touch_test

四、前期排查:确认触摸屏设备节点

很多人直接写event0导致打开失败,先执行这条命令确认触摸屏对应event编号:

cat /proc/bus/input/devices

重点寻找 Touchscreen 字段,查看对应的 eventX。

也可以使用系统自带工具预览原始事件:

evtest /dev/input/event0

五、单点屏 → 多点电容屏修改方案

如果是电容多点触摸屏,不再使用ABS_X/ABS_Y,要使用MT系列宏:

if (ev.type == EV_ABS)
{
    if (ev.code == ABS_MT_POSITION_X)
        tx = ev.value;
    else if (ev.code == ABS_MT_POSITION_Y)
        ty = ev.value;
}

六、高频踩坑总结【重点】

  1. ❌ 错误:读到ABS_X/ABS_Y立刻打印坐标
    ✅ 正确:等待EV_SYN + SYN_REPORT再输出完整XY
    原因:X、Y事件是分两次发送,不等同步会重复打印、坐标不同步。

  2. ❌ 权限不足 open 返回-1
    ✅ 使用sudo运行程序,或者修改udev规则永久放开权限

  3. ❌ 忘记头文件 <linux/input.h>
    现象:EV_ABS、ABS_X等宏报未定义

  4. ❌ read没有阻塞处理(上面代码read是阻塞调用,无触摸时程序休眠,不占用CPU)

七、面试学习建议(划重点)

不需要死记完整代码,但主干逻辑必须能手写:

  1. open打开设备文件 + 错误判断模板
  2. while循环read input_event骨架
  3. EV_ABS解析坐标 + SYN_REPORT同步输出
    面试官最喜欢提问:为什么要等待SYN_REPORT?
    回答参考:
    触摸屏上报坐标时,X、Y是两个独立事件先后发送;SYN_REPORT代表当前一次触摸的所有数据传输完成,此时XY数值才是一组匹配的坐标。

八、拓展思考

  1. 如果不想阻塞read,可以配合select/poll实现非阻塞读取触摸;
  2. 获取到原始坐标后,通常需要坐标校准,转换成LCD屏幕像素坐标;
posted @ 2026-07-11 10:24  刘梓健  阅读(24)  评论(0)    收藏  举报