嵌入式 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;
}
三、编译与运行
- 交叉编译(根据自己交叉编译器修改)
arm-linux-gnueabihf-gcc touch_test.c -o touch_test
- 开发板运行
# 普通用户通常权限不足,需要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;
}
六、高频踩坑总结【重点】
-
❌ 错误:读到ABS_X/ABS_Y立刻打印坐标
✅ 正确:等待EV_SYN + SYN_REPORT再输出完整XY
原因:X、Y事件是分两次发送,不等同步会重复打印、坐标不同步。 -
❌ 权限不足 open 返回-1
✅ 使用sudo运行程序,或者修改udev规则永久放开权限 -
❌ 忘记头文件
<linux/input.h>
现象:EV_ABS、ABS_X等宏报未定义 -
❌ read没有阻塞处理(上面代码read是阻塞调用,无触摸时程序休眠,不占用CPU)
七、面试学习建议(划重点)
不需要死记完整代码,但主干逻辑必须能手写:
- open打开设备文件 + 错误判断模板
- while循环read input_event骨架
- EV_ABS解析坐标 + SYN_REPORT同步输出
面试官最喜欢提问:为什么要等待SYN_REPORT?
回答参考:
触摸屏上报坐标时,X、Y是两个独立事件先后发送;SYN_REPORT代表当前一次触摸的所有数据传输完成,此时XY数值才是一组匹配的坐标。
八、拓展思考
- 如果不想阻塞read,可以配合
select/poll实现非阻塞读取触摸; - 获取到原始坐标后,通常需要坐标校准,转换成LCD屏幕像素坐标;

浙公网安备 33010602011771号