TP触摸屏,应该是驱动开发中比较简单并且适合新手入手的模块。不过虽然简单,但涉及到的内容还是比较多的,其中linux相关主要的机制:
1. input 机制
2. 中断、定时器
3. I2C
1.TP的原理:TP一般为电容或为电阻屏,不过现在基本上都是电容屏,可能一些WINCE的设备还会用电阻屏,但Android的基本现在为电容屏,且多点触摸和手套触摸都集成在TP的IC中。当用户触摸电容屏时,由于人体电场,用户手指和工作面形成一个耦合电容,因为工作面上接有高频信号,于是手指吸收走一个很小的电流,这个电流分别从屏的四个角上的电极中流出,且理论上流经四个电极的电流与手指头到四角的距离成比例,控制器通过对四个电流比例的精密计算,得出位置。可以达到99%的精确度,具备小于3ms的响应速度。在实际产品中,当屏幕感应到手指的接触或者靠近,会产生一个外部中断给CPU,在中断中,一般中断下半部,通过I2C总线,从TP的IC中读取相关的信息,经过一定的数据处理,上报X,Y坐标值。
2.linux input机制:
linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。
对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。(工程师主要做的事情)
对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。
3. 中断
中 断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的 程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。
Linux 中断分为两个半部:上半部(tophalf)和下半部(bottom half)。上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部 执行的速度就会很快,可以服务更多的中断请求。但是,仅有"登记中断"是远远不够的,因为中断的事件可能很复杂。因此,Linux引入了一个下半部,来完 成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的 中断打断!下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。
4. 定时器
两种设备进行计时:系统定时器和实时时钟。
实时时钟(RTC):用来持久存放系统时间的设备,即便系统关闭后,靠主板上的微型电池提供电力保持系统的计时。系统启动内核通过读取RTC来初始化墙上时间,改时间存放在xtime变量中。
系统定时器:内核定时机制,注册中断处理程序,周期性触发中断,响应中断处理程序,进行处理执行以下工作:
5. I2C协议
2条双向串行线,一条数据线SDA,一条时钟线SCL。
SDA传输数据是大端传输,每次传输8bit,即一字节。
支持多主控(multimastering),任何时间点只能有一个主控。
总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.
系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。
上一篇主要讲的与TP相关的知识点,讲这个主要是,你对这一模块有个基本的概念,知道其工作原理,这样在实际开发过程中,你才知道怎么去写起的驱动,碰到问题时,出现在哪一块,该怎么去解决。
这一篇,主要根据代码来讲上一篇涉及到的相关机制。TP的连线很简单,一路I2C,一根中断线,VCC、GND,reset。
1. TP主要相关demo:
驱动demo:\kernel\drivers\input\touchscreen\Ft5x06_ts.c
\kernel\drivers\input\touchscreen\Ft5x06_ts.h
dtsi文件:\kernel\arch\arm\boot\dts\qcom\Msm8X16-qrd.dtsi
2. dtsi的相关解释
i2c@f9923000{ //TP所连的I2C的寄存器
focaltech@38{
compatible = "focaltech,5x06"; //I2C驱动match的内容
reg = <0x38>; //I2C地址
interrupt-parent = <&msmgpio>; //中断引脚
interrupts = <1 0x2>;
vdd-supply = <&pm8110_l19>; //I2C供电的电影控制
vcc_i2c-supply = <&pm8110_l14>;
focaltech,name = "ft6x06"; //tp类型
focaltech,family-id = <0x06>;
focaltech,reset-gpio = <&msmgpio 0 0x00>; //复位引脚,,用于初始化时序
focaltech,irq-gpio = <&msmgpio 1 0x00>; //中断引脚
focaltech,display-coords = <0 0 480 800>; //TP的触点范围
focaltech,panel-coords = <0 0 480 800>;
focaltech,button-map= <139 102 158>; //虚拟按键
focaltech,no-force-update;
focaltech,i2c-pull-up;
focaltech,group-id = <1>;
focaltech,hard-reset-delay-ms = <20>;
focaltech,soft-reset-delay-ms = <150>;
focaltech,num-max-touches = <2>;
focaltech,fw-name = "ft_8610_qrd_fw.bin"; //TP固件
focaltech,fw-delay-aa-ms = <100>;
focaltech,fw-delay-55-ms = <30>;
focaltech,fw-upgrade-id1 = <0x79>;
focaltech,fw-upgrade-id2 = <0x08>;
focaltech,fw-delay-readid-ms = <10>;
focaltech,fw-delay-era-flsh-ms = <2000>;
};
};
3. 驱动文件
3.1 I2C驱动注册
static int __init ft5x06_ts_init(void)
{
pr_err("start \n");
return i2c_add_driver(&ft5x06_ts_driver); //I2C驱动注册
pr_err("end \n");
}
3.2 文件接口,of_match_table,需要DTSI中compatible定义的一致。
static struct i2c_driver ft5x06_ts_driver = {
.probe = ft5x06_ts_probe,
.remove = ft5x06_ts_remove,
.driver = {
.name = "ft5x06_ts",
.owner = THIS_MODULE,
.of_match_table = ft5x06_match_tabl
#ifdef CONFIG_PM
.pm = &ft5x06_ts_pm_ops,
#endif
},
.id_table = ft5x06_ts_id,
};
3.3 probe
这个里面代码量太多,就不详细一一说明,这里主要讲解一些基本的步骤。
一般,先给相关的结构体分配存储空间,然后从dt设备树中读取相关的信息,测试I2C是否通。
input_dev = input_allocate_device();//分配输入子系统
data->input_dev = input_dev;
data->client = client;
data->pdata = pdata;
input_dev->name = "ft5x06_ts";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
__set_bit(EV_KEY, input_dev->evbit); //设置有什么事件 按键事件 tp虚拟按键
__set_bit(EV_ABS, input_dev->evbit); //绝对事件
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
input_mt_init_slots(input_dev, pdata->num_max_touches, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, //X坐标事件
pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, //Y坐标事件
pdata->y_max, 0, 0);
ft5x06_power_init(data, true); //电源、GPIO相关的初始化
ft5x06_power_on(data, true); // 上电,时序初始化
err = request_threaded_irq(client->irq, NULL,ft5x06_ts_interrupt,IRQF_ONESHOT,
client->dev.driver->name, data); //请求中断,并把事件处理放在下半部。
psensor_input_dev = input_allocate_device(); 分配子输入设备
err = input_register_device(psensor_input_dev);
ft5x06_update_fw_ver(data); //导入固件信息
ft5x06_update_fw_vendor_id(data); //获取固件版本ID
3.4 中断处理程序 ft5x06_ts_interrupt
static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
这个里面主要是读I2C,根据芯片手册,进行数据出来,然后上报事件。
input_report_abs(ip_dev, ABS_MT_POSITION_X, x);
input_report_abs(ip_dev, ABS_MT_POSITION_Y, y);
input_sync(ip_dev);
当然有些TP,如GT9xxxx,会申请工作队列,然后中断产生会在上半部,启动工作队列,然后屏蔽当前中断,在工作队列事件处理完后,使能中断。有些还会当中断请求失败时,申请一个高精度的定时器,会一直轮询启动工作线程,上报事件。
4. 调试相关经验
4.1 一般TP驱动开发,屏产都会给驱动代码或者PATCH,这时主要合代码进去。
一般找代码内现有的一个TP驱动,按它的添加。主要:
1. 把驱动文件放入kernel\drivers\input\touchscreen\,
2. 修改kconfig和Makefile,加入需要根据宏才能编进去,那么需要在deconfig配置文件中设置为Y.
3. 在DTSI中加入该TP的配置。
4.2 编译boot,在out/target/product/msmXXX/obj/KERNEL_OBJ/driver/input/touchscreen/下,看是否有.o文件没有,有则编译成功。
4.3 把新的boot文件刷入板子,查看内核log,cat proc/kmsg,看是否有该TP驱动的打印信息。
4.4 根据打印信息,判断出错的问题。
一般问题,中断注册不上,资源分配不成功,I2C设备通信失败。
一些经验,I2C总线不通,可能是因为I2C供电的电源没有供电,或者该总线上挂的设备太多影响的,前期调最好I2C总线上,只挂一个设备。
若probe成功,可在中断或者工作线程里面加一些打印log。在adb shell进入终端,输入getevent,手按TP,查看是否有数据打出,对于该TP的输入设备。
5. 查看I2C设备:
root@Android:/sys/bus/i2c # cd devices
cd devices
root@android:/sys/bus/i2c/devices # ls
ls
0-0020
0-0022
0-0036
0-0078
1-000c
1-000d
1-001d
1-0028
1-0029
1-002a
1-0038
1-0060
1-0068
2-001c
i2c-0
i2c-1
i2c-2
root@android:/sys/bus/i2c/devices # cd 0-0036
cd 0-0036
root@android:/sys/bus/i2c/devices/0-0036 # ls
ls
driver
modalias
name
power
subsystem
uevent
root@android:/sys/bus/i2c/devices/0-0036 # cat name
cat name
msm_actuator
root@android:/sys/bus/i2c/devices/0-0036 #