嵌入式Linux中触摸屏驱动的设计与实现
2011-02-09周德荣
周德荣
0 引言
触摸屏作为一种输入设备,具有坚固耐用、反应速度快、节省空间、易于交流等优点,提供简单、方便、自然的人机交互方式,目前被广泛应用于工业控制、电子查询、消费性电产品领域。Linux作为是目前最流行的操作系统之一,在桌面系统、服务器领域有大量用户,具有源代码开放,支持的硬件丰富、高可移植等优点,在嵌入式领域也备受青睐。Linux根据不同设备,将驱动程序分为字符设备驱动、块设备驱动、网络设备驱动三种, Linux输入子系统[1]是对字符类型输入设备驱动实现方式的抽象,是对分散的、多种不同类别的输入设备进行统一处理的内核驱动模型。输入子系统具高效、无Bug和可重用等优点。本文对基于Linux输入子系统的触摸屏驱动进行深入的讨论。
1 硬件平台
S3C2440是三星公司推出的采用ARM920t内核的MCU,集成了丰富的外围设备,其中包括4线电阻式触摸屏控制器和8通道多路复用ADC。触摸屏由触摸检测部件和触摸屏控制器构成,对应S3C2440平台的四线电阻触摸屏的外接电路和S3C2440芯片自带的A/D 转换控制部分。四线电阻触摸屏的外接电路控制上下两层导电层的通断情况以及如何取电压,取电压之后由S3C2440芯片中的A/D将模拟量转换成数字量。S3C2440芯片的A/D转换器有8个输入通道,转换结果为10bit数字,转换过程在芯片内部自动实现,转换的结果从寄存器中取值,再进行一定的转后可直接得到触摸点的坐标。S3C2440提供的ADC和触摸屏接口如图1所示,触摸屏直接与引脚XP,XM,YP和YM连接,对触摸屏两个导电层的通断通过XP,XM,YP和YM 4个引脚控制。通过读写指定的特殊寄存器,S3C2440的触摸屏控制器将自动控制触摸屏接口打开或关闭,按指定操作模式完成触点数据的采集。
图1 S3C2440 ADC和触摸屏接口结构
2 输入子系统体系结构简介
设备驱动程序[2]在Linux内核中占很重要地位,设备驱动以内核模块方式实现,可动态加载和卸载。Linux设备驱动的实现只需根据内核提供的一组相关数据结构和驱动接口标准,完成关键数据结构初始化和回调函数的编写。对字符设备驱动内核提供cdev数据结构和file_operations结构体及操作方法,实现字符设备驱动只需完成cdev的初始化、file_operations中操作函数的实现并向内核注册。
Linux输入子系统是对物理形态各异的功能相似的输入设备的抽象,是内核中字符设备驱动接口的封装。输入子系统由设备驱动层、核心层和事件处理层构成。设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,通过核心层提交给事件处理层;核心层对设备驱动层提供编程接口,对事件处理层的也提供编程接口;事件处理层为用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。基于输入子系统设计驱动时要实现设备驱动层的驱动和事件处理层的驱动,而输入子系统在事件处理层为触摸屏提供标准的事件接口,所以只要须完成设备驱动层的驱动,即硬件寄存器的操作和提交输入事件信息[3]。基于输入子系统的设备驱动层驱动的实现过程如下:
1)驱动模块加载函数中设置输入设备支持输入子系统的事件;Linux内核用input_dev代表一个输入设备,对于触摸屏通过对input_dev实例的evbit[0]的设置来支持同步(EN_SYN)、按键(EN_KEY)和绝对坐标(EV_ABS)事件。
2)通过内核提供的input_register_device() 函数向输入子系统注册输入设备。
3)输入设备发生输入操作时提交所发生的事件及对应键值或坐标等状态信息。触摸屏使用输入子系统提供的通用输入事件驱动程序Evdev,将事件信息打包成Input_event类型进行报告。
3 Linux触摸屏驱动的实现
3.1 触摸屏触点数据采集
S3C2440触摸屏控制器有四种工作模式[4],通 过 读 写 ADCTSC、ADCDAT0、ADCDAT1和ADCDLY寄存器完成触摸屏控制器工作模式的选择和触摸屏触点数据采集。由于触摸动作时间的随机性,驱动设计时选择中断工作方式。设置ADCTSC寄存器为0xD3使触摸屏控制器进入等待中断模式,设置ADCDLY采样延迟时间。当触摸屏被按下,触摸屏控制器将产生INT_TC中断;在INT_TC中断处理程序中,设置ADCTSC寄存器为0x0C, 触摸屏控制器切换为自动X/Y坐标转换模式,将自动转换触点对应的x,y坐标值,并分别写入ADCDAT0寄存器和ADCDTA1寄存器,发出INT_ADC中断表示ADC转换完成;进入INT_ADC中断处理程序读取ADCDAT0寄存器和ADCDTA1寄存器中坐标数据并进行相应转换,数据采集后重新设置ADCTSC寄存器为0xD3使触摸屏控制器进入等待中断模式,等待触摸屏被按下。
3.2 驱动初始化模块
Linux驱动程序以内核模块方式加载运行。实现驱动加载函数s3c2440ts_init()并通过module_init(s3c2440ts_init)向内核注册。在驱动加载函数主要完成:启用ADC所需要的时钟、映射IO地址、初始化ADC和触摸屏控制器相关的寄存器、申请INT_TS和INT_ADC中断、初始化输入设备、将输入设备注册到输入子系统。关键代码如下:
/*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/
adc_initialize();
input_dev = input_allocate_device();
/* 设置触摸屏支持的事件*/
dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY)| BIT(EV_ABS);
/*设置所支持的按键*/
dev->keybit[BITS_TO_LONGS(BTN_TOUCH)]= BIT(BTN_TOUCH);/*设置绝对坐标x、y的最小最大值(0-0x3FF)*/input_set_abs_params(dev,ABS_X,0,0x3FF,0,0);input_set_abs_params(dev,ABS_Y,0,0x3FF,0,0);input_set_abs_params(dev,ABS_PRESSURE,0,1,0,0);
/*申请触摸屏中断,触摸屏按下或提笔时触发*/
request_irq(IRQ_TC,tc_irq,IRQF_SAMPLE_RANDOM,"s3c2440_ts",1);
/*申请ADC中断,AD转换完成后触发*/
request_irq(IRQ_ADC,adc_irq,IRQF_SHARED|IRQF_SAMPLE_RANDOM,"s3c2440_ts",1);
/*注册触摸屏输入设备*/
input_register_device(dev);
3.3 中断处理程序及事件报告
用户对触摸屏进行按下、抬起和拖动等操作时,触发中断INT_TS,内核进入到中断处理函数tc_irq ()进行中断处理。tc_irq ()中,通过ADC_LOCK锁机制保证只有一个驱动程序使用ADC的中断线,通过读取ADCDAT0和ADCDAT1寄存器,判断触摸操作的状态,触摸笔按下时调用ts_timer_fire()进行数据转换。当数据转换完成时产进INT_ADC中断,内核进入中断处理函数adc_irq(),adc_irq()完成触点信息采集并调用ts_timer_fire()进行事件报告。事件报告流程如图2所示。
图2 事件报告流程
ts_timer_fire()是主要完成触点坐标信息向应用层报告。updown、count为静态全局变量,updown触点状态,count代表1个 jiffies 时间内ADC转换的次数,count为0,设置自动X/Y轴坐标转换模式,转换完成后产生相应的INT_ADC中断通知转换完毕。count不为0, input_report_abs()函数向输入子系统报告x,y绝对坐标事件,input_report_key()触摸屏对应按键被按下事件,输入子系统使用input_sync()将报告的事件组成一个evdev包,通过/dev/input/eventX发送出去,应用程序通过读取/dev/input/eventX即可获得事件信息。关键代码如下:
static void ts_timer_fire(unsigned long data)
{
if (updown) {/*updown为1,触点被按下,为0否则抬起*/
if (count != 0) {
/*报告x,y绝对坐标值,触摸屏对应按键被按下,触摸屏的状态*/
input_report_abs(dev, ABS_X, xp);
input_report_abs(dev, ABS_Y, yp);
input_report_key(dev, BTN_TOUCH, 1);
input_report_abs(dev, ABS_PRESSURE, 1);
/*事件同步,组成evdev包提交*/
input_sync(dev);
}
/*设置触摸屏控制器为自动X/Y轴坐标转换模式,自动地进行X轴和Y轴的转换操作,转换完成后产生INT_ADC中断通知转换完毕*/
代码省略…
} else {
count = 0;
/*如果触摸笔是弹起状态,则提出报告,并让触摸屏处于等待触摸的阶段*/
input_report_key(dev, BTN_TOUCH, 0);
input_report_abs(dev, ABS_PRESSURE, 0);
input_sync(dev);
/*设置触摸屏为等待中断模式,等待触摸笔按下*/
iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
}
}
4 结论
随着信息技术的快速发展,嵌入式技术与人们的生活越来越紧密,触摸屏作为一种新型输入设备因具有轻便、占用空间少、方便灵活等优点,应用逐渐普及。要充分发挥触摸屏的优点,嵌入式中驱动设计至关重要。嵌入式Linux中基于输入子系统实现触摸屏驱动时,利用了Linux输入子系统提供标准事件接口,简化了驱动设计,驱动设计的重点变成了触摸屏控制器相关的硬件操作及功能实现,充分体现Linux内核代码的高可重性,对其他类型输入设备驱动程序的设计有一定参考作用。
[1] Sreekrishnan Venkateswaran.Essential Linux Device Drivers[M].Prentice Hall PTR,2009.4.
[2] Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman,魏永明,译.Linux设备驱动程序(第三版)[M].中国电力出版社,2006.
[3] 宋宝华.Linux设备驱动开发详解[M].人民邮电出版社,2008.
[4] 韦东山.嵌入式Linux应用开发完全手册[M].人民邮电出版社,2008.