Linux下的触摸屏驱动.docx
- 文档编号:9384827
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:18
- 大小:20.46KB
Linux下的触摸屏驱动.docx
《Linux下的触摸屏驱动.docx》由会员分享,可在线阅读,更多相关《Linux下的触摸屏驱动.docx(18页珍藏版)》请在冰豆网上搜索。
Linux下的触摸屏驱动
Linux下的触摸屏驱动
一.触摸屏理论概述
对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码。
下面讲的是基于Mini2440的触摸屏驱动,现在的驱动我们都将设备和驱动分离,挂在平台设备总线上,让设备和驱动去匹配。
而我们在linu2.6.32.2内核版本中的触摸屏驱动仍然没有将设备和驱动分离,这样就不存在匹配问题,这种现象其实我们并不陌生,在我们学习驱动的前期,都会研究简单字符驱动代表LED驱动,那个驱动就是把设备和驱动写在了一起。
总结下,驱动和设备可以分离也可以不分离,建议分离,而本触摸屏驱动没有分离设备和驱动,有兴趣可以将设备和驱动进行分离。
先说明下触摸屏的工作原理,当有人在触摸屏上按下触笔时,触摸屏的四个引脚会产生不同的电压值,这样触摸屏控制器就能检测到这种变化,从而产生INT_TC中断,表示触笔按下。
然后在得到CPU指示的情况下,触摸屏控制器可以根据四个引脚上产生的不同电压值进行AD转换,从而计算出X和Y坐标的数值,并在将这两个值保持到其内部寄存器后,发出INT_ADC中断,表示坐标转换已完成,从而软件就可以读取按下触笔的位置。
二.触摸屏驱动分析
本驱动分析很有特点,我对触摸屏驱动的分析是按照整个触摸事件的执行顺序进行代码分析的,有的函数由于每次被执行完成的任务不同,所以需要多次分析。
同时,我把整个触摸事件的来龙去脉也都说的很清楚了。
驱动分析/driver/input/touchscreen/s3c2410_ts.c
staticint__inits3c2410ts_init(void)
{
structinput_dev*input_dev;
adc_clock=clk_get(NULL,"adc"); //获取时钟
if(!
adc_clock){
printk(KERN_ERR"failedtogetadcclocksource\n");
return-ENOENT;
}
clk_enable(adc_clock); //使能时钟
base_addr=ioremap(S3C2410_PA_ADC,0x20); //物理地址转为虚拟地址
if(base_addr==NULL){
printk(KERN_ERR"Failedtoremapregisterblock\n");
return-ENOMEM;
}
s3c2410_ts_connect(); //触摸屏端口配置
//使能预分频,分频系数为0xff
iowrite32(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
iowrite32(0xffff, base_addr+S3C2410_ADCDLY);//延时
//检查光标按下中断信号,等待中断
iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC);
input_dev=input_allocate_device(); //分配input设备
if(!
input_dev){
printk(KERN_ERR"Unabletoallocatetheinputdevice!
!
\n");
return-ENOMEM;
}
dev=input_dev;
//支持按键事件、坐标事件
dev->evbit[0]=BIT(EV_SYN)|BIT(EV_KEY)|BIT(EV_ABS);
dev->keybit[BITS_TO_LONGS(BTN_TOUCH)]=BIT(BTN_TOUCH);
//对于X轴范围是0-ox3ff,数据误差是0,中心平滑位置是0
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);
dev->name=s3c2410ts_name;
dev->id.bustype=BUS_RS232;
dev->id.vendor=0xDEAD;
dev->id.product=0xBEEF;
dev->id.version=S3C2410TSVERSION;
//申请AD转换中断
if(request_irq(IRQ_ADC,stylus_action,IRQF_SHARED|IRQF_SAMPLE_RANDOM,"s3c2410_action",dev)){
printk(KERN_ERR"s3c2410_ts.c:
CouldnotallocatetsIRQ_ADC!
\n");
iounmap(base_addr);
return-EIO;
}
//申请触摸中断
if(request_irq(IRQ_TC,stylus_updown,IRQF_SAMPLE_RANDOM,
"s3c2410_action",dev)){
printk(KERN_ERR"s3c2410_ts.c:
CouldnotallocatetsIRQ_TC!
\n");
iounmap(base_addr);
return-EIO;
}
printk(KERN_INFO"%ssuccessfullyloaded\n",s3c2410ts_name);
input_register_device(dev);
return0;
}
下面是这个模块加载函数中调用的一个配置端口函数
staticinlinevoids3c2410_ts_connect(void)
{
//将触摸屏用到的四个端口配置成触摸屏模式
s3c2410_gpio_cfgpin(S3C2410_GPG(12),S3C2410_GPG12_XMON);
s3c2410_gpio_cfgpin(S3C2410_GPG(13),S3C2410_GPG13_nXPON);
s3c2410_gpio_cfgpin(S3C2410_GPG(14),S3C2410_GPG14_YMON);
s3c2410_gpio_cfgpin(S3C2410_GPG(15),S3C2410_GPG15_nYPON);
}
我们来分析两种情况,第一种情况,如果没有按下触摸屏
驱动中定义了一个定时器
staticstructtimer_listtouch_timer=
TIMER_INITIALIZER(touch_timer_fire,0,0);
因为这个定时器的期限时间设置为0,这表示当驱动加载后就会执行一次定时函数touch_timer_fire
staticvoidtouch_timer_fire(unsignedlongdata)
{
unsignedlongdata0;
unsignedlongdata1;
intupdown;
data0=ioread32(base_addr+S3C2410_ADCDAT0); //读取X坐标
data1=ioread32(base_addr+S3C2410_ADCDAT1); //读取Y坐标
updown=(!
(data0&S3C2410_ADCDAT0_UPDOWN))&&(!
(data1&S3C2410_ADCDAT0_UPDOWN));//触摸屏是否被按下,如果按下updowm=1
if(updown){
if(count!
=0){
longtmp;
tmp=xp;
xp=yp;
yp=tmp;
xp>>=2;
yp>>=2;
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);
input_sync(dev);
}
xp=0;
yp=0;
count=0;
iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);
iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);
}else{//没有被按下
count=0; //初始化count为0,表示当前AD转换没发生
input_report_key(dev,BTN_TOUCH,0); //向input子系统报告未按下
input_report_abs(dev,ABS_PRESSURE,0);
input_sync(dev);
iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC);//等待按键中断
if(OwnADC){//OwnADC是获取一把锁标示,在此为0
OwnADC=0;
up(&ADC_LOCK);
}
}
}
第二种情况,如果触摸屏被按下,首先触发触摸中断,执行stylus_updown函数
staticirqreturn_tstylus_updown(intirq,void*dev_id)
{
unsignedlongdata0;
unsignedlongdata1;
intupdown;
if(down_trylock(&ADC_LOCK)==0){ //获取一把锁
OwnADC=1; //表示获得锁
data0=ioread32(base_addr+S3C2410_ADCDAT0);//读取X轴数据
data1=ioread32(base_addr+S3C2410_ADCDAT1);//读取Y轴数据
updown=(!
(data0&S3C2410_ADCDAT0_UPDOWN))&&(!
(data1&S3C2410_ADCDAT0_UPDOWN));//触摸屏是否被按下,按下updowm=1
if(updown){
touch_timer_fire(0); //如果触摸屏被按下,执行touch_timer_fire
}else{ //去抖动操作,释放锁
OwnADC=0;
up(&ADC_LOCK);
}
}
returnIRQ_HANDLED;
}
下面我们第二次分析touch_timer_fire,而这次主要是因为触摸中断中调用了这个函数,假设当前触摸屏被按下后,坐标值还没进行AD转换
staticvoidtouch_timer_fire(unsignedlongdata)
{
unsignedlongdata0;
unsignedlongdata1;
intupdown;
data0=ioread32(base_addr+S3C2410_ADCDAT0);
data1=ioread32(base_addr+S3C2410_ADCDAT1);
updown=(!
(data0&S3C2410_ADCDAT0_UPDOWN))&&(!
(data1&S3C2410_ADCDAT0_UPDOWN));
if(updown){ //触摸屏被按下
if(count!
=0){//count是全局变量,统计AD转换次数,目前未AD转换
longtmp;
tmp=xp;
xp=yp;
yp=tmp;
xp>>=2;
yp>>=2;
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);
input_sync(dev);
}
xp=0; //虽然触摸屏被按下,但是未完成AD转换
yp=0;
count=0;
//自动连续测量X和Y坐标
iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);
//AD转换开始且该位在开始后清零
iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);
}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);
if(OwnADC){
OwnADC=0;
up(&ADC_LOCK);
}
}
}
现在我们知道,如果触摸屏被按下,但是AD还没转换完毕,那么我们会开启AD转换,自动测量X和Y坐标,这样就会触发AD转换中断,执行AD转换的中断处理程序。
其实当我们的触摸屏被按下,当X和Y轴获取电压值,然后就会进行AD转换,执行AD转换的中断处理程序。
好了,我们该看看AD转换的中断代码了。
staticirqreturn_tstylus_action(intirq,void*dev_id)
{
unsignedlongdata0;
unsignedlongdata1;
if(OwnADC){//只有触摸屏被按下,相应了触摸中断该标志才为1
data0=ioread32(base_addr+S3C2410_ADCDAT0);//读取X坐标
data1=ioread32(base_addr+S3C2410_ADCDAT1); //读取Y坐标
xp+=data0&S3C2410_ADCDAT0_XPDATA_MASK;//叠加X坐标
yp+=data1&S3C2410_ADCDAT1_YPDATA_MASK;//叠加Y坐标
count++; //统计AD转换次数
if(count<(1<<2)){//如果AD转换次数不足4次
//自动连续测量X和Y坐标
iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);
//AD转换开始且该位在开始后清零
iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);
}else{
mod_timer(&touch_timer,jiffies+1);//四次AD转换后,修改定时时间
iowrite32(WAIT4INT
(1),base_addr+S3C2410_ADCTSC);//等待释放
}
}
returnIRQ_HANDLED;
}
在这个AD转换的中断程序中,有一个全局变量count令人费解,count是什么作用呢?
我们做AD转换时,其实是对一个点进行了四次采样,然后把四次采样结果进行叠加然后取平均。
这样提高AD转换的精确度。
在上面这个AD转换的中断处理程序中,我们知道,当count不足4次时,会继续进行自动连续测量X和Y坐标并开启AD转换,只有当AD转换次数达到四次后,就会修改定时器的时间,使得下一个节拍到来时执行定时器处理程序,并且等待按键被释放。
当AD转换完毕,我们在下一个节拍到来后,会执行一次定时器程序touch_timer_fire
staticvoidtouch_timer_fire(unsignedlongdata)
{
unsignedlongdata0;
unsignedlongdata1;
intupdown;
data0=ioread32(base_addr+S3C2410_ADCDAT0);
data1=ioread32(base_addr+S3C2410_ADCDAT1);
updown=(!
(data0&S3C2410_ADCDAT0_UPDOWN))&&(!
(data1&S3C2410_ADCDAT0_UPDOWN));
if(updown){ //触摸屏被按下
if(count!
=0){//count是全局变量,统计AD转换次数,目前已经4次
longtmp;
tmp=xp;
xp=yp;
yp=tmp; //X和Y轴数据交换
xp>>=2;//因为对同一个点采样四次,这里对X轴取平均
yp>>=2;//因为对同一个点采样四次,这里对Y轴取平均
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 触摸屏 驱动