最近工作中遇到的,这里只做技术分析,不分享相关代码了。
触摸屏原理其实是这样的,有两组控制端口,一组是I2C,一组是一个GPIO做中断。当中断触发时,系统会通过I2C去读取触屏的触摸事件。
控制的关键内容如下:
I2C 通信:在中断处理程序中,使用 i2c_master_recv 函数从触摸屏读取数据。数据的格式由具体的触摸屏硬件决定。
中断处理:当触摸屏检测到触摸事件时,它会触发一个 GPIO 中断,驱动程序的中断处理程序 ts_irq_handler 负责读取数据并向上层报告触摸事件。
输入子系统集成:驱动程序通过 Linux 输入子系统(input_dev)报告触摸事件,最终可以在用户空间接收到这些事件。
这次看到的代码大概是这样的:
Core.c
----lib
----chips
代码大概是三个部分。
先看看Core吧
还是标准的内核module流程。
late_initcall(xxx_ts_init);
// module_init(xxx_ts_init);
module_exit(xxx_ts_exit);
MODULE_AUTHOR("XXX Driver Team");
MODULE_DESCRIPTION("XXX Touchscreen Driver");
MODULE_LICENSE("GPL v2");
这个TP是根据I2C来进行处理的。
#ifdef I2C_PORT
static const struct i2c_device_id hyn_id_table[] = {
{.name = HYN_DRIVER_NAME, .driver_data = 0,},
{},
};
static struct i2c_driver xxx_ts_driver = {
.probe = xxx_ts_probe,
.remove = xxx_ts_remove,
.driver = {
.name = xxx_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = xxx_of_match_table,
},
.id_table = xxx_id_table,
};
match是用来匹配设备树的,这里很重要的就是probe和remove两个函数。虽然现在有init和exit两个函数,但是明显大部分内容都是在probe和remove里面做的。
probe比较复杂。
里面就是调用linux内核的I2C函数,比如i2c_check_functionality,然后开始poweron,这里主要是用pinctrl设置端口,然后regulator_set_voltage设置端口电压。
之后是根据设备树判断设备是否加载,之后开始处理中断。创建中断线程。创建驱动节点。
标准中断流程:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
// 定义中断号 (通常需要从设备树或硬件文档中获取)
#define MY_IRQ_LINE 17
// 中断处理函数
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "Interrupt occurred! IRQ: %d\n", irq);
// 处理中断的逻辑
return IRQ_HANDLED; // 表示中断已经被处理
}
// 初始化模块
static int __init my_driver_init(void)
{
int ret;
printk(KERN_INFO "Initializing interrupt driver...\n");
// 申请中断
ret = request_irq(MY_IRQ_LINE, my_irq_handler, IRQF_SHARED, "my_irq_handler", (void *)(my_irq_handler));
if (ret) {
printk(KERN_ERR "Failed to request IRQ: %d\n", MY_IRQ_LINE);
return ret;
}
printk(KERN_INFO "Interrupt handler registered.\n");
return 0;
}
// 卸载模块
static void __exit my_driver_exit(void)
{
printk(KERN_INFO "Exiting interrupt driver...\n");
// 释放中断
free_irq(MY_IRQ_LINE, (void *)(my_irq_handler));
printk(KERN_INFO "Interrupt handler unregistered.\n");
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple interrupt handler example");
但是这里用的是request_threaded_irq,看说明是新Linux内核可以更高效用多核的方法。
remove则相对比较简单。
待续。。。
当中断响应时,每次发送是一帧:
struct ts_frame{
u8 rep_num;
enum report_typ report_need;
u8 key_id;
u8 key_state;
struct {
u8 pos_id;
u8 event;
u16 pos_x;
u16 pos_y;
u16 pres_z;
}pos_info[MAX_POINTS_REPORT];
};
Lib里面主要是一些辅助操作,大概如下:
1 创建驱动节点封装
2 I2C封装
3 手势功能封装
4 升级功能封装
5 其它一些辅助函数
Chips里面主要就是固件
然后有一个硬件操作的封装,这个不是TP本身的版本,而且高通芯片的版本。也就是说各个高通芯片注册,初始化,reset的方法都会不同。
struct hyn_ts_fuc{
void (*tp_rest)(void);
int (*tp_report)(void);
int (*tp_supend)(void);
int (*tp_resum)(void);
int (*tp_chip_init)(struct hyn_ts_data *ts_data);
int (*tp_updata_fw)(u8 *bin_addr, u16 len);
int (*tp_set_workmode)(enum work_mode mode,u8 enable);
u32 (*tp_check_esd)(void);
int (*tp_prox_handle)(u8 cmd);
int (*tp_get_dbg_data)(u8 *buf,u16 len);
int (*tp_get_test_result)(u8 *buf,u16 len);
};
在上面的probe和remove中,会根据不同的芯片平台,选择不同的回调函数。