触摸屏驱动s3c2410_ts.c分析

来源:互联网 发布:学校装网络信号屏蔽器 编辑:程序博客网 时间:2024/06/06 06:32

Linux设备驱动工程师之路——触摸屏驱动s3c2410_ts.c分析

K-Style

转载请自 http://blog.csdn.net/ayangke 

一、触摸屏硬件知识

 

1.模块原理图

S3C2440有8路的ADC通道其中触摸屏控制器接口XP,XM,YP,YM与四路ADC通道复用四个IO引脚。从原理图看出8路ADC只有一个A/D转换器,通过一个8选1开关MUX来选通哪一路A/D通道进行转换。触摸屏控制会产生两个中断,一个触摸屏中断INT_ADC,一个ADC_转换完成中断INT_ADC。ADC需要时钟才能工作,因为它需要设置采样率。


再复习一下ARM裸机实验时触摸屏寄存器操作流程

流程:

         初始化

         1设置采样延时和分频值ADCDLY ADCCON

         2中断相关设置

         3设置触摸屏AD转换为等待中断模式 ADCTSC

         中断服务函数

         4清相关挂起寄存器

         5设置转换模式,一般为连续x,y转换

6 启动转换

          7 转换完成后读取x y坐标ADCDAT0ADCDAT1

          8 设置触摸屏AD转换为等待中断模式,设置等待弹起中断ADCTSC

9 弹起中断发生后,设置触摸屏转换为等待中断模式,等待下一次触笔按下rADCTSC

         程序

二、触摸屏驱动程序

 

1.模块初始化

[html] view plaincopyprint?
  1. static int __init s3c2410ts_init(void)  
  2. {  
  3.     struct input_dev *input_dev;  
  4.   
  5.     //获取时钟  
  6.     adc_clock = clk_get(NULL, "adc");  
  7.     if (!adc_clock) {  
  8.         printk(KERN_ERR "failed to get adc clock source\n");  
  9.         return -ENOENT;  
  10.     }  
  11.     clk_enable(adc_clock);  
  12.     //使能时钟  
  13.     //需要时钟的是因为触摸屏要用到ADC转换,而完成ADC转换则需要时钟(采用时间)  
  14.   
  15.     //映射ADC的IO内存  
  16.     base_addr=ioremap(S3C2410_PA_ADC,0x20);  
  17.     if (base_addr == NULL) {  
  18.         printk(KERN_ERR "Failed to remap register block\n");  
  19.         return -ENOMEM;  
  20.     }  
  21.   
  22.     //初始化触摸屏的IO引脚  
  23.     /* Configure GPIOs */  
  24.     s3c2410_ts_connect();  
  25.   
  26.     //设置预分频值  
  27.     iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),\  
  28.              base_addr+S3C2410_ADCCON);  
  29.     iowrite32(0xffff,  base_addr+S3C2410_ADCDLY);//设置采用延时  
  30.     iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);  
  31. //设置触摸屏控制器为等待按下中断  
  32.   
  33.     /* Initialise input stuff */  
  34.     //申请一个input设备  
  35.     input_dev = input_allocate_device();  
  36.   
  37.     if (!input_dev) {  
  38.         printk(KERN_ERR "Unable to allocate the input device !!\n");  
  39.         return -ENOMEM;  
  40.     }  
  41.   
  42.     dev = input_dev;  
  43.       
  44.     //设置可被支持的事件为同步、按键、绝对坐标事件  
  45. dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);  
  46. //设置按键时间类型为触摸屏  
  47.     dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);  
  48.   
  49.     //设置事件数值范围X,Y坐标范围为0到3FF,按键数值范围从0,到1  
  50.     input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);  
  51.     input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);  
  52.     input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);  
  53.   
  54.     //设置设备的身份信息  
  55.     dev->name = s3c2410ts_name;  
  56.     dev->id.bustype = BUS_RS232;  
  57.     dev->id.vendor = 0xDEAD;  
  58.     dev->id.product = 0xBEEF;  
  59.     dev->id.version = S3C2410TSVERSION;  
  60.   
  61.   
  62.     /* Get irqs */  
  63.     //获取触摸屏中断IRQ_TC,ADC转换完成中断IRQ_ADC  
  64.     if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM,  
  65.         "s3c2410_action", dev)) {  
  66.         printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");  
  67.         iounmap(base_addr);  
  68.         return -EIO;  
  69.     }  
  70.     if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,  
  71.             "s3c2410_action", dev)) {  
  72.         printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");  
  73.         iounmap(base_addr);  
  74.         return -EIO;  
  75.     }  
  76.   
  77.     printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);  
  78.   
  79.     /* All went ok, so register to the input system */  
  80.     //注册设备  
  81.     input_register_device(dev);  
  82.   
  83.     return 0;  
  84. }  

我们再来看看模块初始化函数中初始化触摸屏的IO引脚的s3c2410_ts_connect();函数

[html] view plaincopyprint?
  1. static inline void s3c2410_ts_connect(void)  
  2. {  
  3.     s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);  
  4.     s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);  
  5.     s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);  
  6.     s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);  
  7. }  

触摸屏控制器接口是与IO端口复用的。

 

2.触摸屏中断服务函数

[html] view plaincopyprint?
  1. static irqreturn_t stylus_updown(int irq, void *dev_id)  
  2. {  
  3.     unsigned long data0;  
  4.     unsigned long data1;  
  5.     int updown;  
  6.   
  7.     if (down_trylock(&ADC_LOCK) == 0) {  
  8.         OwnADC = 1;  
  9.   
  10.         //读取ADCDAT0和ADCDAT1寄存器,判断是按下中断还是弹起中断  
  11.         //ADCDAT0和ADCDAT1查手册可知其第15位当按下时为0,弹起为1  
  12.         data0 = ioread32(base_addr+S3C2410_ADCDAT0);  
  13.         data1 = ioread32(base_addr+S3C2410_ADCDAT1);  
  14.   
  15.         updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  
  16.   
  17.         if (updown) {  
  18.             touch_timer_fire(0);//当判断为按下时  
  19.         } else {  
  20.             OwnADC = 0;  
  21.             up(&ADC_LOCK);  
  22.         }  
  23.     }  
  24.   
  25.     return IRQ_HANDLED;  
  26. }  

我们再来看touch_timer_fire(0);这个函数

[html] view plaincopyprint?
  1. static void touch_timer_fire(unsigned long data)  
  2. {  
  3.     unsigned long data0;  
  4.     unsigned long data1;  
  5.     int updown;  
  6.   
  7. //读取ADCDAT0和ADCDAT1判断为弹起还是按下  
  8.     data0 = ioread32(base_addr+S3C2410_ADCDAT0);  
  9.     data1 = ioread32(base_addr+S3C2410_ADCDAT1);  
  10.   
  11.     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  
  12.   
  13.     //当按下时  
  14.     if (updown) {  
  15.         if (count != 0) {  
  16.             //当两次转换完成后  
  17.             long tmp;  
  18.                                                                                                    
  19.             tmp = xp;  
  20.             xp = yp;  
  21.             yp = tmp;  
  22.                                                                                                    
  23.                         xp >>= 2;  
  24.                         yp >>= 2;  
  25.             //求取两次转换坐标的平均值,然后报告  
  26.   
  27.             input_report_abs(dev, ABS_X, xp);  
  28.             input_report_abs(dev, ABS_Y, yp);  
  29.   
  30.             input_report_key(dev, BTN_TOUCH, 1);  
  31.             input_report_abs(dev, ABS_PRESSURE, 1);  
  32.             input_sync(dev);  
  33.         }  
  34.   
  35.         xp = 0;  
  36.         yp = 0;  
  37.         count = 0;  
  38.   
  39.         //启动ADC转换  
  40.         //设置ADCTSC,转换模式  
  41.         iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);  
  42.         //设置ADCCON,启动转换  
  43.         iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);  
  44.     } else {  
  45.         //如果是弹起了将count清0,并报告弹起事件  
  46.                 count = 0;  
  47.   
  48.         input_report_key(dev, BTN_TOUCH, 0);  
  49.         input_report_abs(dev, ABS_PRESSURE, 0);  
  50.         input_sync(dev);  
  51.   
  52.         //再次设置触摸屏为等待中断模式  
  53.         iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);  
  54.         if (OwnADC) {  
  55.             OwnADC = 0;  
  56.             up(&ADC_LOCK);  
  57.         }  
  58.     }  

3.ADC中断

[html] view plaincopyprint?
  1. static irqreturn_t stylus_action(int irq, void *dev_id)  
  2. {  
  3.     unsigned long data0;  
  4.     unsigned long data1;  
  5.   
  6.     if (OwnADC) {  
  7.         data0 = ioread32(base_addr+S3C2410_ADCDAT0);  
  8.         data1 = ioread32(base_addr+S3C2410_ADCDAT1);  
  9.   
  10.         //读取转换后X,Y值  
  11.         xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;  
  12.         yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;  
  13.         count++;  
  14.   
  15.         //通过对count的判断进行两次转换,两次转换后求和的值将在触摸屏中断里面报告时间的时候除以2求得平均值然后上报。  
  16.         if (count < (1<<2)) {  
  17.             iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);  
  18.             iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);  
  19.         } else {  
  20.         //当两次转换完成时  
  21.             mod_timer(&touch_timer, jiffies+1);//设置定时器的定时值为一个时钟滴答,一个滴答之后touch_timer_fire函数被调用用来报告时间数值  
  22.         /*该定时器的初始化如下  
  23.         static struct timer_list touch_timer =  
  24.         TIMER_INITIALIZER(touch_timer_fire, 0, 0);  
  25.         */  
  26.             iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);//重新设置触摸屏为等待中断模式  
  27.         }  
  28.     }  
  29.   
  30.     return IRQ_HANDLED;  
  31. }