
简介ET6238驱动是一份面向嵌入式开发者的I2C接口驱动源码用于控制ET6238芯片驱动最多16行8列LED矩阵显示适合数字仪表盘、信息屏、游戏设备等需要段位显示的项目。资源包共2个文件包含1个C源文件与1个头文件压缩包仅2KB体量轻便便于直接集成到现有工程。头文件对外暴露初始化、命令与数据写入、亮度调节等接口源文件则实现I2C底层通信、从设备地址设置、LED段位位操作及PWM灰度控制并兼顾超时重试等错误处理与同步、异步调用方式。已有405人学习下载可作为快速上手ET6238显示控制的参考实现帮助开发者理清I2C寻址、矩阵刷新与亮度调节的代码组织思路减少从零调试的时间成本。1. 从一颗触摸芯片说起ET6238 驱动到底能帮你省下多少事如果你手里有一块带 ET6238 触摸控制器的板子插上电却发现系统根本不认它或者能识别但坐标乱跳、多点触控时断时续那你大概率需要一份能直接编译进内核的驱动源码。ET6238 是一颗电容式触摸屏控制芯片常见于中小尺寸的工业 HMI、车载中控和便携终端。它通过 I2C 总线与主控通信上报触摸坐标、按压状态和手势事件。这份 C 源码驱动的价值在于它把芯片初始化、中断处理、坐标解析、多点触控协议上报这一整条链路都写清楚了你不需要对着数据手册从零啃寄存器直接改改配置就能跑。适合谁适合正在做嵌入式 Linux 或裸机触摸屏适配的开发者尤其是那些拿到源码包却不知道从哪个文件开始看的人。2. 驱动架构拆解从 I2C 设备树到 input 子系统的完整链路2.1 驱动分层与文件职责拿到一份 ET6238 驱动源码先别急着编译。我一般会先看目录结构搞清楚每个文件在整条链路里扮演什么角色。典型的 ET6238 驱动会分成这么几块文件/模块职责关键依赖et6238_core.c芯片初始化、寄存器读写、固件加载I2C 子系统et6238_ts.c触摸事件解析、坐标转换、上报input 子系统et6238_irq.c中断注册、中断处理、唤醒机制GPIO / 中断子系统et6238_cfg.h寄存器地址、阈值、分辨率等宏定义无et6238_i2c.cI2C 设备匹配、probe/remove 实现设备树 / ACPI这个分层不是随便切的。core 层只关心“怎么让芯片开始工作”ts 层只关心“怎么把原始数据变成 input 事件”irq 层只关心“什么时候去读数据”。三层之间通过函数指针和回调衔接改一层基本不影响另外两层。常见做法是 core 层导出一个et6238_read_touch_data()给 ts 层调用irq 层在中断里触发一个工作队列工作队列再去调 ts 层的解析函数。2.2 设备树节点与 I2C 地址配置ET6238 挂载在 I2C 总线上设备树里必须有一个对应的节点。很多新手翻车就翻在这里地址写错、中断引脚配错、复位引脚没加驱动 probe 直接返回 -ENODEV。i2c1 { status okay; clock-frequency 400000; et6238: touchscreen2a { compatible etek,et6238; reg 0x2a; /* I2C 从机地址7 位 */ interrupt-parent gpio1; interrupts 12 IRQ_TYPE_EDGE_FALLING; /* INT 引脚下降沿触发 */ reset-gpios gpio1 13 GPIO_ACTIVE_LOW; /* RST 引脚低有效 */ irq-gpios gpio1 12 GPIO_ACTIVE_LOW; touchscreen-size-x 800; touchscreen-size-y 1280; touchscreen-inverted-x; touchscreen-swapped-x-y; vdd-supply vcc_3v3; status okay; }; };这段设备树里几个参数值得逐条说清楚。reg 0x2a是 ET6238 的 I2C 从机地址具体值看硬件原理图上的 ADDR 引脚接法接 GND 和接 VCC 地址不一样。interrupts里的触发方式要和芯片 INT 引脚的实际行为匹配ET6238 一般配下降沿或低电平触发配错了要么中断丢要么中断风暴。touchscreen-size-x/y是触摸面板的物理分辨率必须和屏参一致否则坐标会缩放错位。touchscreen-inverted-x和touchscreen-swapped-x-y这两个属性是给坐标轴方向不对时用的先不加跑起来看坐标方向再决定。提示设备树改完记得重新编译 dtb 并更新到板子上只改 dts 不编译等于没改。2.3 probe 函数里的初始化顺序probe 函数是驱动的入口顺序错了芯片可能起不来。我见过有人先注册 input 设备再去初始化芯片结果中断来了 input 还没准备好直接空指针。正确的顺序应该是拿 GPIO → 硬复位 → I2C 通信验证 → 读芯片 ID → 加载配置 → 注册 input → 申请中断。static int et6238_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct et6238_ts_data *ts; int ret; ts devm_kzalloc(client-dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; ts-client client; i2c_set_clientdata(client, ts); /* 1. 获取并复位 GPIO */ ts-reset_gpio devm_gpiod_get_optional(client-dev, reset, GPIOD_OUT_LOW); if (IS_ERR(ts-reset_gpio)) return PTR_ERR(ts-reset_gpio); /* 2. 硬复位拉低 10ms 再拉高等待芯片内部初始化 */ if (ts-reset_gpio) { gpiod_set_value_cansleep(ts-reset_gpio, 0); msleep(10); gpiod_set_value_cansleep(ts-reset_gpio, 1); msleep(50); /* 数据手册要求的上电稳定时间 */ } /* 3. 验证 I2C 通信读芯片 ID */ ret et6238_i2c_read(ts, ET6238_REG_CHIP_ID, ts-chip_id); if (ret 0) { dev_err(client-dev, failed to read chip id: %d\n, ret); return ret; } if (ts-chip_id ! ET6238_CHIP_ID) { dev_err(client-dev, chip id mismatch: 0x%02x\n, ts-chip_id); return -ENODEV; } /* 4. 加载触摸配置参数 */ ret et6238_load_config(ts); if (ret 0) return ret; /* 5. 注册 input 设备 */ ret et6238_input_init(ts); if (ret 0) return ret; /* 6. 申请中断最后一步 */ ret devm_request_threaded_irq(client-dev, client-irq, NULL, et6238_irq_handler, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, client-name, ts); if (ret 0) { dev_err(client-dev, failed to request irq: %d\n, ret); return ret; } return 0; }这段代码里每一步都有讲究。devm_gpiod_get_optional用 optional 是因为有些板子复位引脚直接接 RC 电路不需要软件控制。复位后的msleep(50)是给芯片内部固件启动留时间短了读 ID 会失败。读 ID 那步是判断 I2C 通不通的最快方式比读坐标寄存器更可靠。IRQF_ONESHOT表示中断线程化后只触发一次配合IRQF_TRIGGER_FALLING使用避免中断处理函数里做 I2C 读时被同级中断打断。2.4 中断处理与坐标上报ET6238 的中断处理不能直接在硬中断里做 I2C 读因为 I2C 传输可能睡眠。常见做法是用request_threaded_irq把中断线程化或者硬中断里调度一个工作队列。下面这段是线程化中断处理函数的典型写法static irqreturn_t et6238_irq_handler(int irq, void *dev_id) { struct et6238_ts_data *ts dev_id; u8 touch_num; int ret; /* 读触摸点数量寄存器 */ ret et6238_i2c_read(ts, ET6238_REG_TOUCH_NUM, touch_num); if (ret 0) { dev_err(ts-client-dev, read touch num failed\n); return IRQ_HANDLED; } touch_num 0x0F; /* 低 4 位有效高 4 位保留 */ if (touch_num 0 || touch_num ET6238_MAX_TOUCH) { et6238_report_release(ts); /* 上报抬起事件 */ return IRQ_HANDLED; } /* 读取所有触摸点坐标 */ ret et6238_read_touch_data(ts, touch_num); if (ret 0) return IRQ_HANDLED; et6238_report_touch(ts, touch_num); return IRQ_HANDLED; }touch_num寄存器低 4 位表示当前按下的手指数量0 表示全部抬起。ET6238_MAX_TOUCH一般是 5 或 10看具体型号。et6238_report_release里要对每一个之前上报过的 slot 调用input_mt_slot和input_mt_report_slot_state(false)漏掉任何一个都会导致系统里有个“幽灵手指”一直按着。et6238_read_touch_data里读坐标时要注意字节序ET6238 的坐标寄存器是 16 位高字节在前还是低字节在前看数据手册读反了坐标会变成乱码。3. 编译与运行从源码到板子上跑通触摸3.1 内核配置与编译选项ET6238 驱动要编译进内核先得让 Kconfig 和 Makefile 认识它。在驱动目录下加两行# drivers/input/touchscreen/Makefile obj-$(CONFIG_TOUCHSCREEN_ET6238) et6238.o et6238-objs : et6238_core.o et6238_ts.o et6238_irq.o et6238_i2c.o# drivers/input/touchscreen/Kconfig config TOUCHSCREEN_ET6238 tristate ET6238 I2C touchscreen depends on I2C depends on INPUT select INPUT_MT select INPUT_MT_DIRECT help Say Y here to enable ET6238 touchscreen support.tristate表示可以编译成模块调试阶段建议编成模块改完 insmod 就行不用反复烧内核。select INPUT_MT和INPUT_MT_DIRECT必须选上否则多点触控协议上报会编译报错。编译命令就是标准的内核模块编译make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- modules -j$(nproc)编完在drivers/input/touchscreen/下会生成et6238.ko拷到板子上insmod et6238.ko就能加载。3.2 加载后验证触摸是否正常驱动加载后先看内核日志有没有 probe 成功dmesg | grep et6238 # 期望输出类似 # et6238 1-002a: chip id: 0x62 # et6238 1-002a: touchscreen registered # input: et6238 as /devices/platform/soc/1c2ac00.i2c/i2c-1/1-002a/input/input3然后确认 input 设备节点出现了ls /dev/input/ # 应该能看到 eventX用 cat /proc/bus/input/devices 确认哪个是 et6238 cat /proc/bus/input/devices | grep -A5 et6238最直接的验证是用evtest工具evtest /dev/input/event3 # 手指按上去应该能看到 ABS_MT_POSITION_X/Y 事件如果evtest里坐标不动先查中断计数cat /proc/interrupts | grep et6238 # 按屏幕时计数应该增加不增加说明中断没触发中断计数不涨九成是设备树里中断触发方式配错了或者 INT 引脚被别的驱动占用了。中断涨但坐标不动那就是 I2C 读数据失败用i2cdetect -y 1确认芯片地址能不能扫到。3.3 坐标校准与方向调整坐标方向不对是最常见的问题。现象是手指往右滑光标往上跑或者左右镜像。解决方式有两种改设备树属性或者改驱动里的坐标转换函数。设备树方式最简单加touchscreen-inverted-x、touchscreen-inverted-y、touchscreen-swapped-x-y这三个属性的组合就能覆盖八种方向。如果设备树属性不够用就在et6238_report_touch里改static void et6238_report_touch(struct et6238_ts_data *ts, u8 touch_num) { int i; for (i 0; i touch_num; i) { u16 x ts-touch_data[i].x; u16 y ts-touch_data[i].y; /* 坐标转换根据实际方向调整 */ if (ts-swap_xy) swap(x, y); if (ts-invert_x) x ts-max_x - x; if (ts-invert_y) y ts-max_y - y; input_mt_slot(ts-input, i); input_mt_report_slot_state(ts-input, MT_TOOL_FINGER, true); input_report_abs(ts-input, ABS_MT_POSITION_X, x); input_report_abs(ts-input, ABS_MT_POSITION_Y, y); } input_mt_sync_frame(ts-input); input_sync(ts-input); }swap_xy交换 X/Y 轴invert_x和invert_y做镜像翻转。input_mt_sync_frame是收尾动作告诉 input 子系统这一帧数据完了。漏掉input_sync的话事件会攒在缓冲区里不发出表现就是触摸完全没反应。4. 避坑与排查ET6238 驱动调试中最容易翻车的几个点4.1 中断风暴导致系统卡死现象加载驱动后系统变卡top里看到irq/xx-et6238占用率很高/proc/interrupts里中断计数疯涨。原因中断触发方式配成了电平触发但芯片 INT 引脚在数据没读完后一直保持有效电平导致中断反复触发。或者中断处理函数里没有清中断标志。解决把设备树里的IRQ_TYPE_EDGE_FALLING改成边沿触发或者在中断处理函数开头先读一次状态寄存器清标志。ET6238 一般用下降沿触发就够了不要用电平触发。4.2 probe 返回 -ENODEV 但 I2C 能扫到地址现象i2cdetect能看到 0x2a 有设备但驱动 probe 失败日志报chip id mismatch。原因读芯片 ID 的寄存器地址写错了或者读出来的字节序不对。有些板子 I2C 上拉电阻偏大400kHz 下读数据不稳定读回来的 ID 是随机值。解决先用i2cget手动读一次 ID 寄存器确认值对不对i2cget -y 1 0x2a 0x00 w # 对比数据手册里的 CHIP_ID 默认值如果手动读也不对把 I2C 频率降到 100kHz 试试。如果手动读对但驱动读不对检查驱动里et6238_i2c_read的寄存器地址偏移和字节序处理。4.3 多点触控只上报一个点现象evtest里只能看到一个手指的坐标第二个手指按上去没反应。原因input_mt_init_slots时 slot 数量传了 1或者ABS_MT_SLOT没有正确设置。还有一种情况是input_mt_report_slot_state的第三个参数传了 false导致 slot 被标记为未使用。解决确认input_mt_init_slots(ts-input, ET6238_MAX_TOUCH, INPUT_MT_DIRECT)里第二个参数是最大触摸点数不是 1。每个触摸点上报前必须调input_mt_slot(ts-input, i)指定 slot 编号i 从 0 开始。4.4 坐标跳变或漂移现象手指不动坐标自己小幅跳动或者滑动时坐标突然跳到边缘。原因坐标寄存器读取时没有做有效性判断读到了无效数据。ET6238 在触摸抬起瞬间坐标寄存器可能还保留着上一次的值如果此时 touch_num 已经变成 0 但代码还在读坐标就会上报脏数据。解决在读坐标前先判断touch_num是否大于 0等于 0 直接上报 release。另外可以在驱动里加一个简单的滤波连续两次坐标差值超过阈值就丢弃当前帧。4.5 休眠唤醒后触摸失效现象系统 suspend 再 resume 后触摸没反应dmesg里也没有中断计数增加。原因suspend 时芯片进入了低功耗模式resume 后没有重新初始化。或者 GPIO 状态在 suspend 时被改变INT 引脚不再触发中断。解决实现et6238_suspend和et6238_resume回调resume 里重新做一次硬复位和配置加载。如果芯片支持自动唤醒确认ET6238_REG_POWER_MODE寄存器在 resume 后被正确设置。5. 二次开发改造从能跑到好用我一般会动这几个地方驱动跑通只是第一步真正放到产品里还得改。我拿到一份能跑的 ET6238 驱动后通常会从三个方向做二次开发性能调优、功能扩展、可维护性改造。性能调优方面最直接的是改中断上报频率。ET6238 默认每次中断上报一帧数据如果手指快速滑动中断太频繁会占 CPU。我一般会在中断处理里加一个简单的节流两次上报间隔小于 5ms 就跳过用jiffies做判断。另一个点是 I2C 传输优化把连续读多个寄存器的操作合并成一次i2c_transfer减少总线开销。实测下来合并传输能把一次触摸读取从 6 次 I2C 传输降到 2 次。功能扩展方面ET6238 支持手势识别但很多驱动只实现了基础触摸。我会在et6238_irq_handler里加一个手势寄存器读取把双击、长按、滑动方向解析出来通过input_report_key上报。手势阈值可以在et6238_cfg.h里定义成宏方便不同项目调整。还有一个实用改造是加 sysfs 节点把固件版本、触摸点数、坐标范围暴露出来产线测试时不用接串口就能读。可维护性改造方面我会把芯片相关的寄存器操作全部收进et6238_reg.h和et6238_reg.c用宏定义代替裸地址。这样换一颗同系列的芯片只需要改寄存器定义文件核心逻辑不动。另外加一套 debugfs 接口运行时能动态改阈值和坐标方向不用重新编译驱动。验证改造效果的方法很简单改完先insmod加载用evtest看事件流是否正常然后用getevent -lt看时间戳确认上报间隔稳定最后跑一个滑动测试脚本连续画圈 100 次看有没有丢点或跳点。我一般会写一个简单的 Python 脚本读/dev/input/eventX统计每秒事件数和坐标方差方差突然变大就说明有脏数据。注意改中断节流参数时不要拍脑袋定值先用evtest测出实际触摸帧率再根据帧率设节流窗口。设大了会感觉触摸不跟手设小了等于没节流。从那以后我每次改完驱动都强制走一遍“insmod → evtest 看事件 → getevent 看时间戳 → 滑动测试脚本跑 100 次”这个流程少一步都可能把问题带到产线。希望帮到你。本文还有配套的精品资源点击获取