• S3C6410触摸屏驱动分析


    一. device的注册
    1.0 两个注册
    //在smdk6410_machine_init中既注册了touchscreen的私有信息也注册了ts资源

    1. 1 在arch/arm/mach-s3c64xx/mach-smdk6410.c中
      2 static void __init smdk6410_machine_init(void)
      3 {
      4     //在arch/arm/mach-s3c64xx/dev-ts.c中
      5     s3c_ts_set_platdata(&s3c_ts_platform);                                  //1.设备私有信息的注册
      6     //在driver/base/platform.c中
      7     platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));   //2.设备资源的注册
      8 }
    1.1 ts私有信息的注册
    在arch/arm/mach-s3c64xx/mach-smdk6410.c中
    1.  1 static struct s3c_ts_mach_info s3c_ts_platform __initdata = {
       2     .delay               = 10000,                    //延时
       3     .presc               = 49,                       //分频
       4     .oversampling_shift  = 2,                        //分频
       5     .resol_bit           = 12,                       //精度     
       6     .s3c_adc_con         = ADC_TYPE_2,               //分频     
       7 };
       8 smdk6410_machine_init
       9 {
      10     //下面这个函数在arch/arm/mach-s3c64xx/dev-ts.c中
      11     s3c_ts_set_platdata(&s3c_ts_platform);
      12 }
    1.2 ts设备资源的注册
    1.  1 //在arch/arm/mach-s3c64xx/dev-ts.c中
       2 static struct resource s3c_ts_resource[] = {
       3     [0] = {
       4         .start = SAMSUNG_PA_ADC,                        //0x7E00B000
       5         .end = SAMSUNG_PA_ADC + SZ_256 - 1,             //0x7E00B100    
       6         .flags = IORESOURCE_MEM,
       7     },
       8     [1] = {
       9         .start = IRQ_PENDN,                             //0x5e=94
      10         .end = IRQ_PENDN,
      11         .flags = IORESOURCE_IRQ,
      12     },
      13     [2] = {
      14         .start = IRQ_ADC,                                //0x5f=95
      15         .end = IRQ_ADC,
      16         .flags = IORESOURCE_IRQ,
      17     }
      18 };
      19 struct platform_device s3c_device_ts = {
      20     .name         = "s3c-ts",
      21     .id         = -1,
      22     .num_resources     = ARRAY_SIZE(s3c_ts_resource),
      23     .resource     = s3c_ts_resource,
      24 };
      25 //在arch/arm/mach-s3c64xx/mach-smdk6410.c中
      26 static struct platform_device *smdk6410_devices[] __initdata = {
      27     &s3c_device_ts,                //把ts放入到总的ts列表中
      28 }
      29 //在arch/arm/mach-s3c64xx/mach-smdk6410.c中
      30 static void __init smdk6410_machine_init(void)
      31 {
      32     //在driver/base/platform.c中一起注册
      33     platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
      34 }

    二. device_driver
    2.0 两个宏
    1.  1 #define WAIT4INT(x)              //只是针对于S3C_ADCTSC寄存器
       2         (((x)<<8) |              //<bit8> 0->down 1->up interrupt signal
       3         S3C_ADCTSC_YM_SEN |      //<bit7> 1 = Switch enable (YM = VSSA_ADC)
       4         S3C_ADCTSC_YP_SEN |      //<bit6> 1 = Switch disable (YP=AIN5, Hi-z) 
       5         //XM_SEN                //<bit5> 0 = Switch disable (XM = AIN6, Hi-z)
       6         S3C_ADCTSC_XP_SEN |      //<bit4> 1 = Switch disable (XP=AIN7, Hi-z)
       7         //PULL_UP               //<bit3> 0 = XP Pull-up Enable.
       8         //AUTO_PST              //<bit2> 0 = Normal ADC conversion.         
       9         S3C_ADCTSC_XY_PST(3))    //<bit1-0> 3: Waiting for Interrupt Mode 
      10 #define AUTOPST     
      11     (S3C_ADCTSC_YM_SEN |        //1 = Switch enable (YM = VSSA_ADC)
      12     S3C_ADCTSC_YP_SEN |         //1 = Switch disable (YP=AIN5, Hi-z) 
      13     S3C_ADCTSC_XP_SEN |         //1 = Switch disable (XP=AIN7, Hi-z) 
      14     S3C_ADCTSC_AUTO_PST |       //1 = Auto Sequential measurement of X-position, Y-position
      15     S3C_ADCTSC_XY_PST(0))       //0 = No operation mode
      16 WAIT4INT(x) :
      17             当x=0时,设为等侍down中断
      18              当x=1时,设为等侍up中断
    2.1 初始化
    ok6410的touchscreen在内核源码的位置:driver/input/touchscreen/s3c-ts.c
    device 与 device_driver按名字s3c-ts匹配之后,就进入s3c_ts_probe函数
    1. static struct platform_driver s3c_ts_driver = {
          .probe = s3c_ts_probe,
          .remove = s3c_ts_remove,
          .suspend = s3c_ts_suspend,
          .resume = s3c_ts_resume,
          .driver        = {
              .owner    = THIS_MODULE,
              .name    = "s3c-ts",
          },
      };
      static int __init s3c_ts_init(void)
      {
          return platform_driver_register(&s3c_ts_driver);
      }
      static void __exit s3c_ts_exit(void)
      {
          platform_driver_unregister(&s3c_ts_driver);
      }
      module_init(s3c_ts_init);
      module_exit(s3c_ts_exit);
    2.2 probe函数
    1.  1 static int __init s3c_ts_probe(struct platform_device *pdev)
       2 {
       3     struct resource *res;
       4     struct device *dev;
       5     struct input_dev *input_dev;
       6     struct s3c_ts_mach_info * s3c_ts_cfg;
       7     int ret, size;
       8     dev = &pdev->dev;
       9     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                //获取ts寄存器地址
      10     size = (res->end - res->start) + 1;
      11     ts_mem = request_mem_region(res->start, size, pdev->name);           //申请I/O内存
      12     ts_base = ioremap(res->start, size);                                 //request_mem_region申请的内存在使用前要调用ioremap
      13     ts_clock = clk_get(&pdev->dev, "adc");                               //获取clock    
      14     clk_enable(ts_clock);                                                //在初始化时disable了ts_clock,这个地方要enable
      15     
      16                                                                 //下面这几行是要把ts的配置信息写到寄存器中去
      17     s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev);                        //获取ts的配置信息,
      18                                                                          //ts的私有信息:在arch/arm/mach-s3c64xx/mach-smdk6410.c中
      19     //enable prescaler && 设置prescaler_value=s3c_ts_cfg->presc
      20     writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xff), ts_base+S3C_ADCCON);
      21    
      22    //s3c_ts_cfg->delay=0x10000 --> External input clock 
      23     writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);
      24     
      25     //A/D converter resolution selection--> 12-bit A/D conversion
      26     writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);
      27     //设为等侍down中断模式
      28     writel(WAIT4INT(0), ts_base+S3C_ADCTSC);
      29     ts = kzalloc(sizeof(struct s3c_ts_info), GFP_KERNEL);           //下面这几行是要初始化s3c_ts_info结构体
      30     input_dev = input_allocate_device();
      31    
      32     ts->dev = input_dev;
      33     ts->dev->evbit[0] = ts->dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
      34     ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
      35     if (s3c_ts_cfg->resol_bit==12) {
      36         input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);                //设置x轴的最大最小值
      37         input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);                //设置y轴的最大最小值
      38     } 
      39     input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0);                 //设置Press状态的最大最小值(按下或空闲)   
      40     sprintf(ts->phys, "input(ts)");
      41     ts->dev->name = s3c_ts_name;
      42     ts->dev->phys = ts->phys;
      43     ts->dev->id.bustype = BUS_RS232;
      44     ts->dev->id.vendor = 0xDEAD;
      45     ts->dev->id.product = 0xBEEF;
      46     ts->dev->id.version = S3C_TSVERSION;
      47     ts->shift = s3c_ts_cfg->oversampling_shift;
      48     ts->resol_bit = s3c_ts_cfg->resol_bit;
      49     ts->s3c_adc_con = s3c_ts_cfg->s3c_adc_con;
      50     /* For IRQ_PENDUP */
      51     ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);                //获取中断号
      52     //申请中断,RANDOM表示设备可以看作随机的发生源
      53     ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts); //申请中断
      54    
      55     /* For IRQ_ADC */
      56     ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);               //获取中断号
      57     ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM | IRQF_SHARED, "s3c_action", ts); //申请共享中断
      58     
      59     /* All went ok, so register to the input system */
      60     ret = input_register_device(ts->dev);                                    //把这个input_dev添加到input系统中
      61 }
    ts底板图:

    ts连到核心板图:

    TSXP --> AIN7
    TSYP --> AIN5





    2.3 IRQ_PENDN
    1.  1 static irqreturn_t stylus_updown(int irqno, void *param)
       2 {
       3     unsigned long data0;
       4     unsigned long data1;
       5     if (!ADC_locked4TS())                              //进入中断函数,如果没有加锁,则加上锁
       6         if (s3c_ts_adc_lock(LOCK_TS))                  //如果加锁失败,则直接返回       
       7             return IRQ_HANDLED;        
       8     data0 = readl(ts_base+S3C_ADCDAT0);
       9     data1 = readl(ts_base+S3C_ADCDAT1);
      10     touch_timer_fire(0);
      11     if(ts->s3c_adc_con==ADC_TYPE_2) {
      12          //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear
      13         __raw_writel(0x0, ts_base+S3C_ADCCLRWK);    
      14         //ADCCLRINT: Clear ADC Interrupt
      15         __raw_writel(0x0, ts_base+S3C_ADCCLRINT);   
      16     }
      17     return IRQ_HANDLED;
      18 }

    2.3.1 fire
    1.  1 static void touch_timer_fire(unsigned long data)
       2 {
       3     unsigned long data0;
       4     unsigned long data1;
       5     int pendown;
       6     if (!ADC_locked4TS())                       //如果当前状态是free,说明加锁失败,直接返回
       7         return;
       8     
       9     //这儿的数据读取,是为了判断是down还是up状态
      10     data0 = readl(ts_base+S3C_ADCDAT0);         //
      11     data1 = readl(ts_base+S3C_ADCDAT1);         //12     //data0的bit15: 0->按下状态; 1->松开状态
      13     //如果data0与data1都不为松开状态,就是按下状态
      14     pendown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));
      15     if (pendown) {           //在按下状态,如果有数据则提交数据,      
      16         if (ts->count) {     //这个ts->count是在IRQ_ADC中改变的  
      17             input_report_abs(ts->dev, ABS_X, ts->xp);     //提交
      18             input_report_abs(ts->dev, ABS_Y, ts->yp);     //提交
      19             input_report_key(ts->dev, BTN_TOUCH, 1);       //提交
      20             input_report_abs(ts->dev, ABS_PRESSURE, 1);    //提交  
      21             input_sync(ts->dev);                           //提交
      22         }                      //ts->count>0,说明ADC己经转化过数据了,就提交完数据,  
      23         ts->xp = 0;            //然后把所有数据归零
      24         ts->yp = 0;
      25         ts->count = 0;
      26         //设ADC的模式为自动转换 
      27         writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
      28         //ADCCON bit0 --> A/D conversion starts: 启动adc转换,产生一个IRQ_ADC中断
      29         //注意:这儿是要启动ADC中断,但具体是down还是up中断
      30         writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);  
      31     }
      32     else {                    //如果是松开
      33         ts->count = 0;
      34         input_report_key(ts->dev, BTN_TOUCH, 0);        //提交
      35         input_report_abs(ts->dev, ABS_PRESSURE, 0);     //提交
      36         input_sync(ts->dev);
      37         writel(WAIT4INT(0), ts_base+S3C_ADCTSC);    //等侍按下中断
      38         if (ADC_locked4TS())                       //如果还处于锁定状态
      39             s3c_ts_adc_unlock();                   //释放锁,表示一次按键结束
      40     }
      41 }
    注意: 
    在按下状态,先提交数据,产生ADC中断,
    在松开状态,先提交数据,切换为按下中断

    2.4 IRQ_ADC
    在进入IRQ_ADC中断之前,己经定义了一个时间定时器,它的处理函数是 touch_timer_fire
    1. static struct timer_list touch_timer =
    2.         TIMER_INITIALIZER(touch_timer_fire, 0, 0);
    IRQ_ADC中断处理函数:
    1.  1 static irqreturn_t stylus_action(int irqno, void *param)
       2 {
       3     unsigned long data0;
       4     unsigned long data1;
       5     if (!ADC_locked4TS()) {                        //如果处于未锁定状态,说明出错
       6         if (ADC_free())                            //锁是在IRQ_TS中加上的  
       7             __raw_writel(0x0, ts_base + S3C_ADCCLRINT);
       8         return IRQ_HANDLED;
       9     }
      10     data0 = readl(ts_base+S3C_ADCDAT0);            //
      11     data1 = readl(ts_base+S3C_ADCDAT1);            //
      12     if(ts->resol_bit==12) {
      13         ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT;     //怎么能让我相信这是在求平均值呢?
      14         ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;
      15     }    
      16     ts->count++;
      17     if (ts->count < (1<<ts->shift)) {        //小于4次,ts->shift=2
      18         writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);
      19         writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);
      20     } else {                                 //超过4次,则    
      21         //启动定时器,把超时时间设为jiffies+1,调用touch_timer_fire
      22         mod_timer(&touch_timer, jiffies+1);
      23         //等侍松开            
      24         writel(WAIT4INT(1), ts_base+S3C_ADCTSC);
      25     }
      26     if(ts->s3c_adc_con==ADC_TYPE_2) {
      27         //ADCCLRINTPNDNUP: INT_PNDNUP interrupt clear
      28         __raw_writel(0x0, ts_base+S3C_ADCCLRWK);
      29         //ADCCLRINT: Clear ADC Interrupt
      30         __raw_writel(0x0, ts_base+S3C_ADCCLRINT);
      31     }
      32     return IRQ_HANDLED;
      33 }
    注意: 
    在按下状态,先提交数据,产生ADC中断,
    在松开状态,先提交数据,切换为按下中断

    三.总结
    3.1 按下到松开时的流程如下:


    3.2 文字说明
    初始化时设为等侍down中断模式
        当有触摸笔按下时:        
            a.触发中断,进入stylus_updown函数
                stylus_updown:判断是down中断, 如果ts->count,触发adc中断
            
            b.触发ADC中断,进入stylus_action函数
            stylus_action: ts->count小于4次, 触发adc中断; 
                继续自动检测,直到満足4次
            stylus_action:    时间定时器触发touch_timer_fire,并切换到等侍up中断模式
            c. touch_timer_fire:
                    判断是down中断,汇报坐标信息,触发adc中断,与b进入循环
                    持继汇报触摸笔按下信息
        当有触摸笔松开时:
            a. touch_timer_fire:
                    判断是up中断,汇报坐标信息,切换到等侍按下中断
     
  • 相关阅读:
    IDEA中getgetServletContext()报错
    会话跟踪小结
    Sklearn-CrossValidation交叉验证
    js定时器 数码时钟
    centos7 新建用户并获取root权限
    linux cetos7 yum 安装redis
    mybatis 中 使用 List<Map<String,Object>> Map<String,Object>接收结果
    Mybatis resultMap 中 collection association 的用法
    各大型邮箱smtp服务器及端口收集: SMTP
    Mybatis 动态sql 示例 复杂类型对象 作为参数进行取值
  • 原文地址:https://www.cnblogs.com/chd-zhangbo/p/5587313.html
Copyright © 2020-2023  润新知