• 驱动06.触摸屏驱动程序


    1.触摸屏的简介

      触摸屏是标准的输入设备,在写驱动程序时采用的之前讲过的输入子系统那套框架。我们无需关心对设备文件的操作,只需关心对硬件寄存器的操作和上报事件即可。

      触摸屏是附在LCD上的一层薄膜,并不是我们平时认识的触摸屏,它只是起到确定坐标的作用。

       S3C2440提供的触摸屏接口有4种处理模式,分别是:正常转换模式、单独的X/Y位置转换模式、自动X/Y位置转换模式和等待中断模式。本例子中用的是等待中断模式

    2.以s3c2410_ts.c为例分析整体框架

    2.1 s3c2410ts_init函数

    1 static int __init s3c2410ts_init(void)
    2 {
    3 //    init_MUTEX(&gADClock);
    4     return platform_driver_register(&s3c2410ts_driver);/*注册s3c2410ts_driver*/
    5 }
    1 static struct platform_driver s3c2410ts_driver = {
    2        .driver         = {
    3            .name   = "s3c2410-ts",
    4            .owner  = THIS_MODULE,
    5        },
    6        .probe          = s3c2410ts_probe,
    7        .remove         = s3c2410ts_remove,
    8 };

    如果出现与其同名的平台设备,将调用其probe函数

    2.2 s3c2410ts_probe函数

    static int __init s3c2410ts_probe(struct platform_device *pdev)
    {    
            /*使能adc时钟*/
        adc_clock = clk_get(NULL, "adc");
        clk_enable(adc_clock);
        
            //映射adc的地址
        base_addr=ioremap(S3C2410_PA_ADC,0x20);
            
             //配置GPIO,初始化
        s3c2410_ts_connect();
             //分配一个input_dev结构体
        input_dev = input_allocate_device();
           
              //设置这个结构体
        ts.dev = input_dev;
            //事件类型:按键类、绝对位移类
        ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
        ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
             //绝对位移类的各种参数:X方向、Y方向、按压类
        input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
        input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
        input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
    
            request_irq(IRQ_ADC,stylus_action,...);//ADC中断
            request_irq(IRQ_TC,stylus_updown,...);//触摸屏中断
             //注册input_dev结构体
        input_register_device(ts.dev);
    
         
    }

    2.3 ADC中断处理函数stylus_action

    static irqreturn_t stylus_action(int irq, void *dev_id)
    {
        unsigned long data0;
        unsigned long data1;
    
    //    if (bADCForTS) {
        
            data0 = ioread32(base_addr+S3C2410_ADCDAT0);
            data1 = ioread32(base_addr+S3C2410_ADCDAT1);
        
            ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
            ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
            ts.count++;
    
    //        bADCForTS = 0;
    //        up(&gADClock);
                
            if (ts.count < (1<<ts.shift)) {
    //                if (!down_trylock(&gADClock)) {
    //                    bADCForTS = 1;
                        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 {
                mod_timer(&touch_timer, jiffies+1);
                iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
            }
    //    }
        return IRQ_HANDLED;
    }

    2.4 触摸屏中断处理函数stylus_updown

     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     data0 = ioread32(base_addr+S3C2410_ADCDAT0);
     8     data1 = ioread32(base_addr+S3C2410_ADCDAT1);
     9 
    10     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
    11 
    12     /* TODO we should never get an interrupt with updown set while
    13      * the timer is running, but maybe we ought to verify that the
    14      * timer isn't running anyways. */
    15 
    16     if (updown)
    17         touch_timer_fire(0);
    18 
    19     return IRQ_HANDLED;
    20 }
     1 static void touch_timer_fire(unsigned long data) //上报事件
     2 {
     3       unsigned long data0;
     4       unsigned long data1;
     5     int updown;
     6 
     7       data0 = ioread32(base_addr+S3C2410_ADCDAT0);
     8       data1 = ioread32(base_addr+S3C2410_ADCDAT1);
     9 
    10      updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
    11 
    12      if (updown) {
    13          if (ts.count != 0) {
    14             long tmp;  
    15 
    16             tmp = ts.xp;
    17             ts.xp = ts.yp;
    18             ts.yp = tmp;
    19 
    20              ts.xp >>= ts.shift;
    21              ts.yp >>= ts.shift;
    22 
    23 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
    24              {
    25                  struct timeval tv;
    26                  do_gettimeofday(&tv);
    27                  printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld
    ", (int)tv.tv_usec, ts.xp, ts.yp);
    28              }
    29 #endif
    30 
    31              input_report_abs(ts.dev, ABS_X, ts.xp);
    32              input_report_abs(ts.dev, ABS_Y, ts.yp);
    33 
    34              input_report_key(ts.dev, BTN_TOUCH, 1);
    35              input_report_abs(ts.dev, ABS_PRESSURE, 1);
    36              input_sync(ts.dev);
    37          }
    38 
    39          ts.xp = 0;
    40          ts.yp = 0;
    41          ts.count = 0;
    42 
    43 //        if (!down_trylock(&gADClock)) {
    44 //            bADCForTS = 1;
    45           iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
    46           iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
    47 //      }
    48      } else {
    49          ts.count = 0;
    50 
    51          input_report_key(ts.dev, BTN_TOUCH, 0);
    52          input_report_abs(ts.dev, ABS_PRESSURE, 0);
    53          input_sync(ts.dev);
    54 
    55          iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
    56      }
    57 }

    2.5 s3c2410ts_remove,s3c2410ts_exit函数做的工作与上述相反。

    3 写代码

    3.1 框架

    (1)分配一个input_dev结构体

    (2)设置

    (3)硬件相关的操作(难点)

    (4)注册

    (5)进一步优化(可有可无,有是最好)       

                                                                                                                       

    --------------------------------------------------------------编辑于2017-01-11 00:56:39

       ADC使用的四个步骤:

    (1)设置ADCCON寄存器,选择输入信号通道,设置A/D转换器时钟。

    (2)设置ADCTSC寄存器,设置其为触摸屏使用。

    (3)设置ADCCON寄存器,启动A/D转换。

    (4)转换结束时,读取ADCDAT0寄存器获取数值。

      1 /*参考s3c2410_ts.c*/
      2 
      3 #include <linux/errno.h>
      4 #include <linux/kernel.h>
      5 #include <linux/module.h>
      6 #include <linux/slab.h>
      7 #include <linux/input.h>
      8 #include <linux/init.h>
      9 #include <linux/serio.h>
     10 #include <linux/delay.h>
     11 #include <linux/platform_device.h>
     12 #include <linux/clk.h>
     13 #include <asm/io.h>
     14 #include <asm/irq.h>
     15 
     16 #include <asm/plat-s3c24xx/ts.h>
     17 
     18 #include <asm/arch/regs-adc.h>
     19 #include <asm/arch/regs-gpio.h>
     20 
     21 struct s3c_ts_regs{
     22     unsigned long adccon;
     23     unsigned long adctsc;
     24     unsigned long adcdly;
     25     unsigned long adcdat0;
     26     unsigned long adcdat1;
     27     unsigned long adcupdn;
     28 
     29 };
     30 
     31 static struct input_dev *s3c_ts_dev;
     32 static struct clk    *adc_clock;
     33 static volatile struct s3c_ts_regs *s3c_ts_regs;
     34 static struct timer_list ts_timer;
     35 
     36 static void wait_pen_down_mode()
     37 {    
     38     s3c_ts_regs->adctsc = 0xd3;
     39 }
     40 
     41 static void wait_pen_up_mode()
     42 {    
     43     s3c_ts_regs->adctsc = 0x1d3;
     44 }
     45 
     46 static void measure_xy_mode()
     47 {
     48     s3c_ts_regs->adctsc = (1<<3) |(1<<2);
     49 }
     50 
     51 static void start_adc()
     52 {
     53     s3c_ts_regs->adccon |= (1<<0); 
     54 }
     55 
     56 static int s3c_filter_ts(int x[], int y[])
     57 {
     58 #define ERR_LIMIT 10
     59 
     60     int avr_x, avr_y;
     61     int det_x, det_y;
     62 
     63     avr_x = (x[0] + x[1])/2;
     64     avr_y = (y[0] + y[1])/2;
     65 
     66     det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);
     67     det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);
     68 
     69     if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
     70         return 0;
     71 
     72     avr_x = (x[1] + x[2])/2;
     73     avr_y = (y[1] + y[2])/2;
     74 
     75     det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);
     76     det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);
     77 
     78     if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
     79         return 0;
     80     
     81     return 1;
     82 }
     83 
     84 static void s3c_ts_timer_func(unsigned long data)
     85 {
     86     if (s3c_ts_regs->adcdat0 & (1<<15))
     87     {
     88         /*pen up*/
     89         input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
     90         input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
     91         input_sync(s3c_ts_dev);
     92         wait_pen_down_mode();
     93     }
     94     else
     95     {
     96         /* 测量X/Y坐标 */
     97         measure_xy_mode();
     98         start_adc();
     99     }
    100 }
    101 
    102 
    103 static irqreturn_t tc_irq(int irq, void *dev_id)
    104 {    
    105     if(s3c_ts_regs->adcdat0 & (1<<15))
    106     {
    107         /*pen up*/
    108         
    109          input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
    110          input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
    111          input_sync(s3c_ts_dev);
    112         wait_pen_down_mode();
    113 
    114     }
    115     else
    116     {
    117         measure_xy_mode();
    118         start_adc();
    119     }
    120     return IRQ_HANDLED;
    121 }
    122 
    123 static irqreturn_t adc_irq(int irq, void *dev_id)
    124 {    
    125     static int cnt = 0;
    126     static int x[4],y[4];
    127     int adcdat0, adcdat1;
    128 
    129     adcdat0 = s3c_ts_regs->adcdat0;
    130     adcdat1 = s3c_ts_regs->adcdat1;
    131 
    132     /*如果发现ADC转换完成后pen up,则丢弃数据*/
    133     if(s3c_ts_regs->adcdat0 & (1<<15))
    134     {    /*pen up*/
    135         cnt = 0;
    136         input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
    137         input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
    138         input_sync(s3c_ts_dev);
    139         wait_pen_down_mode();
    140     }
    141     else
    142     {
    143         /*多次测量,取平均值*/
    144         x[cnt] = adcdat0 & 0x3ff;
    145         y[cnt] = adcdat1 & 0x3ff;
    146         ++cnt;
    147         if (cnt == 4)
    148         {
    149             /*软件过滤*/
    150             if (s3c_filter_ts(x, y))
    151             {            
    152                 //printk("x = %d, y = %d
    ", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);
    153                 input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
    154                 input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
    155                 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
    156                 input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
    157                 input_sync(s3c_ts_dev);
    158             }
    159             cnt = 0;
    160             wait_pen_up_mode();
    161             /*启动定时器实现长按/滑动的情况*/
    162             mod_timer(&ts_timer, jiffies + HZ/100);
    163 
    164         }
    165         else
    166         {
    167             measure_xy_mode();
    168             start_adc();
    169         }
    170 
    171             
    172     }
    173     return IRQ_HANDLED;
    174 }
    175 
    176 
    177 
    178 static int s3c_ts_init(void)
    179 {    /*1.分配一个input_dev结构体*/
    180     s3c_ts_dev = input_allocate_device();
    181     if (!s3c_ts_dev) {
    182         printk(KERN_ERR "Unable to allocate the input device !!
    ");
    183         return -ENOMEM;
    184     }
    185 
    186     /*2 设置这个结构体*/
    187     /*2.1 设置事件类型*/
    188     set_bit(EV_KEY,s3c_ts_dev->evbit);
    189     set_bit(EV_ABS,s3c_ts_dev->evbit);
    190 
    191     /*2.2 设置该类型下的哪一个具体事件*/
    192     set_bit(BTN_TOUCH,s3c_ts_dev->keybit);
    193 
    194     input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);
    195     input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
    196     input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
    197 
    198     /*3 注册*/
    199     input_register_device(s3c_ts_dev);
    200 
    201     /*4 硬件相关的操作*/
    202     /*4.1 使能ADC 时钟*/
    203     adc_clock = clk_get(NULL, "adc");
    204     if (!adc_clock) {
    205         printk(KERN_ERR "failed to get adc clock source
    ");
    206         return -ENOENT;
    207     }
    208     clk_enable(adc_clock);
    209 
    210     /*4.2 寄存器初始化*/
    211     s3c_ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs));
    212     s3c_ts_regs->adccon = (1<<14) |(49<<6);
    213 
    214     /*4.3 申请中断*/
    215     request_irq(IRQ_TC, tc_irq, IRQF_SAMPLE_RANDOM,"tc", NULL);
    216     request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM,"adc", NULL);
    217 
    218     s3c_ts_regs->adcdly = 0xffff;/*待数值稳定后在转换*/
    219 
    220     init_timer(&ts_timer);
    221     ts_timer.function = s3c_ts_timer_func;
    222     add_timer(&ts_timer);
    223     
    224     wait_pen_down_mode();
    225     
    226     return 0;
    227 }
    228 
    229 static void s3c_ts_exit(void)
    230 {
    231     free_irq(IRQ_TC, NULL);
    232     free_irq(IRQ_ADC, NULL);
    233     iounmap(s3c_ts_regs);
    234     input_unregister_device(s3c_ts_dev);
    235     input_free_device(s3c_ts_dev);
    236     del_timer(&ts_timer);
    237     
    238 }
    239 
    240 module_init(s3c_ts_init);
    241 module_exit(s3c_ts_exit);
    242 
    243 
    244 MODULE_LICENSE("GPL");
    触摸屏驱动

    --------------------------------------------------------------编辑于2017-01-11 18:21:04

  • 相关阅读:
    自学python:python学习笔记之Ubuntu 16.04网络的配置
    Python 3 中字符串和 bytes 的区别
    Python map学习笔记
    Python lambda 知识点
    Eclipse中Activiti插件的安装
    Activiti简介
    Jquery ajax回调函数不执行
    Spring Boot安装及入门实现
    mysql给root开启远程访问权限
    linux下安装Tomcat
  • 原文地址:https://www.cnblogs.com/Lwd-linux/p/6272019.html
Copyright © 2020-2023  润新知