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

2024-06-13  

一、触摸屏硬件知识

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.模块初始化

  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 sourcen");  

  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 blockn");  

  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 loadedn", 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();函数

  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.触摸屏中断服务函数

  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);这个函数

  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;  

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。