OFN鼠标驱动(八) -- inputs3c2410_ts.c的分析

 通讯的准备工作做完了,下面我们看看鼠标数据是怎么给系统的。

和OFN最贴近的设备当然就是TS -- 触摸屏了。

于是我们先来看看系统自带的TS的驱动代码:

其实这个文件分析完之后,我们基本已经能够完成OFN驱动的移植动作了。

 

-------------------------------------------------------------------------------------------------

 

建议在看本文前,先看IIC驱动。

 

分析完这个函数,我们发现,其实INPUT的核心操作很简单,就是调用

input_report_abs

input_report_key

等函数报告数据,当然,还要用

input_set_abs_params

等函数设置参数

 

补充一点,这个文件分析的时候我漏了一个地方:

static struct timer_list    touch_timer =

              TIMER_INITIALIZER(touch_timer_fire, 0, 0);

初始化一个TMR中断函数,这个函数在AD中断达到指定次数后开启,目的是为了在采集TS数据中断后,最后补一次抬起操作。

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

同样的,我们先看看宏和一些定义,通过原理图和IC规格,我们可以看到,S3C2440直接带有触摸屏接口。

为了理解方便,我们打乱了代码的顺序,从入口开始分析。

 

#define   S3C2410TSVERSION   0x0101

 

//这里是设置ADCTSC寄存器的宏

#define   WAIT4INT(x)  (((x)<<8)                  //0-检测按下中断, 1-检测抬起中断

       | S3C2410_ADCTSC_YM_SEN                 //YM有效(挂GND)

       | S3C2410_ADCTSC_YP_SEN                  //YP无效(作为AD)

       | S3C2410_ADCTSC_XP_SEN                  //XP无效(作为AD)

       | S3C2410_ADCTSC_XY_PST(3))             //等待中断

 

#define AUTOPST      (

       S3C2410_ADCTSC_YM_SEN                  //YM有效(挂GND)

       | S3C2410_ADCTSC_YP_SEN                  //YP无效(作为AD)

       | S3C2410_ADCTSC_XP_SEN                  //XP无效(作为AD)

       | S3C2410_ADCTSC_AUTO_PST             //连续检测X坐标和Y坐标

       | S3C2410_ADCTSC_XY_PST(0))             //无操作

 

#define   DEBUG_LVL    KERN_DEBUG            

//定义调试信息输出等级,当要改变调试信息时,直接改变这里就可以了

 

static char      *s3c2410ts_name = "s3c2440 TouchScreen";

 

struct      s3c2410ts {

       struct input_dev     *dev;             //INPUT设备

       long                     xp;

       long                     yp;

       int                        count;

       int                        shift;

       char                     phys[32];

};

 

static      struct s3c2410ts     ts;                         //ts结构体

static      void __iomem       *base_addr;           //操作的寄存器

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//模块装载时候运行的注册和卸载函数,主要是注册了驱动

int __init       s3c2410ts_init(void)

{

       return     driver_register(&s3c2410ts_driver);

}

 

void __exit    s3c2410ts_exit(void)

{

       driver_unregister(&s3c2410ts_driver);

}

 

module_init(s3c2410ts_init);

module_exit(s3c2410ts_exit);

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//设备驱动

static struct device_driver     s3c2410ts_driver = {

       .name                  = "s3c2410-ts",                    //驱动名

       .bus             = &platform_bus_type,         //总线(定义在base的平台文件下)

       .probe         = s3c2410ts_probe,               //探测函数

       .remove         = s3c2410ts_remove,            //移除函数

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

先挑软柿子捏,看remove函数

看完这个函数后,我们大概可以想到探测函数做了哪些事情了:申请中断,开AD时钟,注册一个输入设备

static int        s3c2410ts_remove(struct device   *dev)

{

       disable_irq(IRQ_ADC);               //禁止ADC中断

       disable_irq(IRQ_TC);                  //禁止TC中断

       free_irq(IRQ_TC,ts.dev);

       free_irq(IRQ_ADC,ts.dev);          //释放这两个中断的资源

 

       if (adc_clock) {                          

              clk_disable(adc_clock);         //禁止AD的时钟

              clk_put(adc_clock);                     //时钟的占有者减1

              adc_clock = NULL;              //挂空

       }

 

       input_unregister_device(ts.dev);    //卸载INPUT设备

       iounmap(base_addr);                   //解除IO内存映射

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

static struct clk       *adc_clock;           //申明了一个AD时钟

 

//探测函数,这个函数执行了之后,会等待TS的按下中断

static int __init      s3c2410ts_probe(struct device *dev)

{

       struct s3c2410_ts_mach_info *info;

       //设备的平台数据类型为mach_info

       info = ( struct s3c2410_ts_mach_info *)dev->platform_data;

 

       //如果平台上没有挂载数据,则出错。

       if (!info)       

       {

              printk(KERN_ERR "Hm... too bad : no platform data for ts\n");

              return -EINVAL;

       }

 

       //获取AD的时钟

       adc_clock = clk_get(NULL, "adc");

       if (!adc_clock) {

              printk(KERN_ERR "failed to get adc clock source\n");

              return -ENOENT;

       }

      

       clk_enable(adc_clock);          //使能AD时钟

 

       //从S3C2410_PA_ADC的物理地址开始,映射0x20个地址

       base_addr=ioremap(S3C2410_PA_ADC,0x20);

       if (base_addr == NULL) {

              printk(KERN_ERR "Failed to remap register block\n");

              return -ENOMEM;

       }

 

       //这个函数的实现在文件的开头了,将GPIO口设置为TS接口

       s3c2410_ts_connect();

 

       if ((info->presc&0xff) > 0)          //如果设置了AD时钟的预分频

              writel(S3C2410_ADCCON_PRSCEN |        //AD转换预分频器有效

                     S3C2410_ADCCON_PRSCVL(info->presc&0xFF),   //AD时钟的预分频

                     base_addr+S3C2410_ADCCON);

       else

              writel(0,  base_addr+S3C2410_ADCCON);  //不使用预分频

 

       //设置AD转换开始的延迟时间

       if ((info->delay&0xffff) > 0)

              writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);

 

       //等待TS按下中断,YM接GND,YP,XP做AD

       writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);

 

       //将ts清0,重新设置

       memset(&ts, 0, sizeof(struct s3c2410ts));

      

       ts.dev = input_allocate_device();          //申请一个input_dev结构体

       ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);      

       ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT(BTN_TOUCH);

       //设置绝对坐标的参数,看参数应该是设置最大最小值范围之类的

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

 

       sprintf(ts.phys, "ts0");

 

       ts.dev->private = &ts;                         //设备的私有数据为自己的整个结构体

       ts.dev->name = s3c2410ts_name;

       ts.dev->phys = ts.phys;                       //设备的物理地址,ts.phys没定义

 

       ts.dev->id.bustype = BUS_RS232;              //总线类型为RS232

       ts.dev->id.vendor = 0xDEAD;             //品牌ID

       ts.dev->id.product = 0xBEEF;             //产品ID

       ts.dev->id.version = S3C2410TSVERSION;              //版本号

 

       ts.shift = info->oversampling_shift;      //这个参数来自平台

 

       //申请AD中断

       if (request_irq(IRQ_ADC,                  //中断请求号

              stylus_action,                             //中断响应函数

              SA_SAMPLE_RANDOM,           //中断标记

              "s3c2410_action",                       //设备名

              ts.dev))                                      //关联的设备

       {

              printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");

              iounmap(base_addr);

              return -EIO;

       }

       //申请TC中断

       if (request_irq(IRQ_TC,

              stylus_updown,

              SA_SAMPLE_RANDOM,

              "s3c2410_action",

              ts.dev))

       {

              printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");

              iounmap(base_addr);

              return -EIO;

       }

 

       printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);

 

       /* All went ok, so register to the input system */

       input_register_device(ts.dev);              //注册INPUT设备

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

OK,我们现在再来看看这两个中断做了什么事。

在分析代码之前,我们先说一下TS的工作原理:

在探测函数时,没有启动AD,所以只有TS中断有效,但检测到TS按下时,触发中断。然后将TS设置为自动采集属性,在设置自动采集属性的同时将ADCTSC的[1:0]设置为0,即无任何操作,这样就不在会触发TS中断,最后,启动AD。

进入AD中断后,会比较conut,不到最大次数时,继续自动采集和AD。到了最大采集次数,就不再启动AD,采集模式改为X采集。

最后在进入TS中断,处理采集的数据。

 

先是AD中断(这个中断在探测函数时没有启动AD,而是在检测到TS按下中断后才启动)

static irqreturn_t    stylus_action(

       int          irq,                             //中断号

       void       *dev_id,                      //关联的设备

       struct pt_regs        *regs)            //寄存器

{

       unsigned long        data0;

       unsigned long        data1;

       disable_irq(IRQ_ADC);        //禁止中断,以免中断一直触发

       data0 = readl(base_addr+S3C2410_ADCDAT0);

       data1 = readl(base_addr+S3C2410_ADCDAT1);

 

       //获取坐标值,计数加1

       ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;

       ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;

       ts.count++;

 

      if (ts.count < (1<<ts.shift)) {        //触发的次数小于最大值

              writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,

                            base_addr+S3C2410_ADCTSC);         

              //再次打开AD转换

              writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,                             base_addr+S3C2410_ADCCON);

       } else {

              mod_timer(&touch_timer, jiffies+1);

              writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);      //换成x坐标测量

       }

       enable_irq(IRQ_ADC);         //add by lili 2007-6-19

       return IRQ_HANDLED;

}

 

//然后是TS中断(按下或者放开)

static irqreturn_t    stylus_updown(

       int                 irq,

       void              *dev_id,

       struct pt_regs        *regs)

{

       unsigned long        data0;

       unsigned long        data1;

       int                        updown;

       disable_irq(IRQ_TC);   

       data0 = readl(base_addr+S3C2410_ADCDAT0);

       data1 = readl(base_addr+S3C2410_ADCDAT1);

 

       //获取光标是按下还是弹起(0-提起, 1-按下)

       updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN))

              && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

 

       //如果是按下触发的中断,调用函数

       if (updown)

              touch_timer_fire(0);

 

       enable_irq(IRQ_TC);            //add by lili 2007-6-19

       return IRQ_HANDLED;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//将TS所用到的IO设置为触摸屏接口

static inline void    s3c2410_ts_connect(void)

{

       s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);

       s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);

       s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);

       s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//这个函数在TS中断中调用(按下中断)

//参数没有使用

static void      touch_timer_fire(unsigned long    data)

{

      unsigned long        data0;

      unsigned long        data1;

       int                        updown;

 

       //读ADCDAT0/1寄存器的值

      data0 = readl(base_addr+S3C2410_ADCDAT0);

      data1 = readl(base_addr+S3C2410_ADCDAT1);

 

       //获取光标是按下还是弹起(0-提起, 1-按下)

      updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN))

              && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

 

      if (updown) {        //光标被按下(TS感应到)

             if (ts.count != 0) {         //有检测到AD中断

                    ts.xp >>= ts.shift;  

                    ts.yp >>= ts.shift;

 

                     //报告坐标

                    input_report_abs(ts.dev, ABS_X, ts.xp);

                    input_report_abs(ts.dev, ABS_Y, ts.yp);

 

                     //报告按键按下

                    input_report_key(ts.dev, BTN_TOUCH, 1);

                    input_report_abs(ts.dev, ABS_PRESSURE, 1);

                    input_sync(ts.dev);

             }

 

              //如果ts.count为0,则只运行这里

             ts.xp = 0;

             ts.yp = 0;

             ts.count = 0;

 

              //XP上拉无效,连续读

             writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,

                            base_addr+S3C2410_ADCTSC);

              //AD转换开始

             writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,                             base_addr+S3C2410_ADCCON);

      } else {                 //光标抬起

             ts.count = 0;

 

              //传递按键值,坐标,同步

              //注意,ts.dev是input类型的数据结构,所以这里只需要传递进这个参数就可以了

             input_report_key(ts.dev, BTN_TOUCH, 0);

             input_report_abs(ts.dev, ABS_PRESSURE, 0);

             input_sync(ts.dev);

 

              //等待按下中断

             writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);

      }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值