触摸屏
就是上报多点触摸信息, 通过触摸芯片, 多点电容触摸屏本质就是I2C驱动, 通过中断读取, 得到信息之后Linux使用INPUT进行输入
Linux有触摸屏上报的流程
Document/input/multi-touch-protocol.txt
有两种协议, 大多数使用的是Type B, Type A比较简单, 没有轨迹追踪, 会上报所有的数据, Type使用于有硬件追踪的数据
上报触摸信息时候, 通过上报事件进行上报, include/uapi/linux/input.h中
#define ABS_MT_SLOT 0x2f /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
......
最常用的事件就是, ABS_MT_POSITION_X, ABS_MT_POSITION_Y和ABS_MT_BLOB_ID, 分别上报对应的坐标以及区分坐标点, 一般先上报点的横坐标, 纵坐标, 点和点之间使用ABS_MT_BLOB_ID隔离
void input_mt_sync(struct input_dev *dev)
对于TypeA类型的设备,通过input_mt_sync()函数来隔离不同的触摸点数据信息, 实际是上报SYN_MT_REPORT信息, 通知接收者获取当前触摸数据,并且准备接收下一个触摸点数据
static inline void input_sync(struct input_dev *dev)
所有上报结束之后使用的函数, SYN_REPORT事件
void input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active);
用来上报一个点的, 如果是添加一个新的触摸点,那么此函数的第三个参数active要设置为true,linux内核会自动分配一个ABS_MT_TRACKING_ID值, 产生ABS_MT_TRACKING_ID和ABS_MT_TOOL_TYPE事件, ABS_MT_TOOL_TYPE事 件 指 定 触 摸 类 型 ( 是 笔 还 是 手 指 等 )
dev:MT设备对应的input_dev。tool_type:触摸类型,可以选择MT_TOOL_FINGER(手指)、MT_TOOL_PEN(笔)或MT_TOOL_PALM(手掌),对于多点电容触摸屏来说一般都是手指。active:true,连续触摸,input子系统内核会自动分配一个ABS_MT_TRACKING_ID给slot。false,触摸点抬起,表示某个触摸点无效了,input子系统内核会分配一个-1给slot,表示触摸点溢出
void input_mt_slot(struct input_dev *dev, int slot)
此函数用于产生ABS_MT_SLOT事件,告诉内核当前上报的是哪个触摸点的坐标数据, dev:MT设备对应的input_dev。slot:当前发送的是哪个slot的坐标信息,也就是哪个触摸点
int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,unsigned int flags)
用于初始化MT的输入slots,编写MT驱动的时候必须先调用此函数初始化slots, dev:MT设备对应的input_dev,因为MT设备隶属于input_dev。num_slots:设备要使用的SLOT数量,也就是触摸点的数量。flags:其他一些flags信息
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
dev:MT设备对应的input_dev。code:要上报的是什么数据,可以设置为ABS_MT_POSITION_X或ABS_MT_POSITION_Y,也就是X轴或者Y轴坐标数据。value:具体的X轴或Y轴坐标数据值
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
dev:MT设备对应的input_dev。use_count:true,有效的触摸点数量;false,追踪到的触摸点数量多于当前上报的数量。返回值:无。
实际流程
- Type A
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT
- Type B
ABS_MT_SLOT 0 //input_mt_slot
ABS_MT_TRACKING_ID 45 //input_mt_report_slot_state
ABS_MT_POSITION_X x[0] //input_report_abs
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT
TypeB设备驱动需要给每个识别出来的触摸点分配一个slot,后面使用这个slot来上报触摸点信息。可以通过slot的ABS_MT_TRACKING_ID来新增、替换或删除触摸点
一个非负数的ID表示一个有效的触摸点,-1这个ID表示未使用slot。一个以前不存在的ID表示这是一个新加的触摸点
中断线程化
驱动的主框架就是i2c, 需要使用中断处理, 中断处理函数中上报, 要使用到INPUT子系统框架
devm_request_threaded_irq函数==> 中断进程化, 用于申请中断,作用和request_irq函数类似, 中断线程化以后中断将作为内核线程运行,而且也可以被赋予不同的优先级,任务的优先级可能比中断线程的优先级高,这样做的目的就是保证高优先级的任务能被优先处理
下半部可以被延迟处理,但是依旧先于线程执行,中断线程化可以让这些比较耗时的下半部与进程进行公平竞争
devm
“devm_“前缀, 使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理。
实现
ft5426: ft5426@38 {
compatible = "edt,edt-ft5426";
reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc
&pinctrl_tsc_reset>;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
status = "okay";
};
.proc/device-tree/soc/aps-bus@2100000/i2c@021a40000
-
搭建框架
-
初始化引脚
使用函数devm_gpio_request_one
一次性初始化引脚
-
初始化中断
-
初始化ft5426
-
读取坐标上报
if(touch_num) { /* 单点触摸按下 */
gt9147_read_regs(dev, GT_TP1_REG, touch_data, 5);
id = touch_data[0] & 0x0F;
if(id == 0) {
input_x = touch_data[1] | (touch_data[2] << 8);
input_y = touch_data[3] | (touch_data[4] << 8);
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true); //为连续触摸
input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);
}
} else if(touch_num == 0){ /* 单点触摸释放 */
input_mt_slot(dev->input, id);
input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false); //不是连续触摸
}
测试
tslib, 用于测试触摸屏
- 安装
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool
- 编译
./autogen.sh
./configure --host=arm-linux-gnueabihf --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/tslib
make//编译
make install//安装
-
复制到根文件
-
et/profile加入
export TSLIB_TSDEVICE=/dev/input/event2 //使用的设备
export TSLIB_CALIBFILE=/etc/pointercal //校准文件
export TSLIB_CONFFILE=/etc/ts.conf //触摸配置文件
export TSLIB_PLUGINDIR=/lib/ts //插件目录
export TSLIB_CONSOLEDEVICE=none //
export TSLIB_FBDEVICE=/dev/fb0 //关联的目录
- 使用
ts_calibrate //校准,不满意删除文件/etc/pointercal
ts_test_mt //测试
设置驱动为内核
打开Linux,
jiao@jiao-virtual-machine:~/linux/IMX6ULL/Bord_Drivers/23_multitouch$ cp gt9147. /home/jiao/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/drivers/input/touchscreen/
之后在这个文件夹最后加上obj-y += gt9147.o