Chinaunix首页 | 论坛 | 博客
  • 博客访问: 10678
  • 博文数量: 3
  • 博客积分: 135
  • 博客等级: 民兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-05 19:10
文章分类
文章存档

2012年(3)

我的朋友

分类: LINUX

2012-12-06 21:05:58

cat /proc/bus/input/devices

I: Bus=0013 Vendor=dead Product=beef Version=0101

N: Name="SUN TouchScreen"  //和驱动中定义名字是相同的

P: Phys=

S: Sysfs=/devices/virtual/input/input0

U: Uniq=

H: Handlers=event0

B: EV=b

B: KEY=0

B: ABS=1000003

ls -l /sys/class/input

lrwxrwxrwx 1 root  root    0 Jan  1  1970 event0 -> ../../devices/virtual/input/input0/event0

 

linux/input.h中有定义, struct input_event {

struct timeval time; //按键时间

__u16 type; //类型

__u16 code; //

__s32 value;//

};

type:

EV_KEY,键盘

EV_REL,相对坐标

EV_ABS,绝对坐标

code:

input.h中有定义:

#define ABS_X                     0x00

#define ABS_Y                     0x01

value

根据不同的事件或者说是驱动的不同,代表不同的含义,在触摸屏代表的就是按下的X,Y坐标

输入子系统是由驱动层,输入子系统核心层、事件处理层组成

Input子系统的设备主设备号是固定的

一个输入事件:

user driver ->input core ->Event handler ->userspace

input设备时用input_dev来描述的,设备驱动完成的工作是向系统报告输入的事件(通过input_event结构体描述)

 

 

输入子系统设备驱动层实现原理:

输入设备不在关心设备文件的操作,而是关心对各个硬件寄存器的操作和提交的输入事件

驱动的设备按下面的步骤实现:

1.       在驱动加载模块设置input子系统支持哪些事件。

2.       input设备注册到input系统中

3.       input设备发生输入操作时,提交所发生的事件及对应的键值/坐标等状态。

事件类型:

EV_SYN

EV_KEY

EV_ABS

常见的提交事件

input_report_key(dev,code,value)

input_report_abs()

在提交输入设备的事件后必须用下列方法使事件同步,让它告知input系统,设备驱动已经发出了一个完整的报告

input_sync(dev)

 

驱动实现的机制:

首先是通过变量OWNADC保证TS中断先发生

发生后在TS中断中判断是否按下,如果按下就需要启动ADC转化,跳转到touch_timer_fire(),在ADC转换中获得坐标数据,否则释放ADC资源

touch_timer_fire()函数的执行过程:

在这个函数中是肯定要启动中ADC采样以完成坐标采集功能,让触摸屏控制器退出等待中断等待模式,进入自动转化模式,但是由于简单的算法处理需要多次采样取平均值,这一部分需要在ADC中断服务子程序中进行处理。

touch_timer_fire()函数还要完成一个功能就是报告触摸屏抬起事件,设置触摸屏控制器处于等待按下中断等待模式

其实touch_timer_fire()有两条执行路径:

1.       就是在TS中断中

2.       就是系统启动定时函数,在ADC完成指定4次的采样后,定时1个时钟滴答就去执行这个函数。

touch_timer_fire()完成的功能:

启动ADC,设置连续模式。在按下状态,报告事件和坐标。在抬起状态下,释放ADC资源

定时器所起的作用就是消抖

 

 

ADC中断服务子程序中完成的功能是:

TC控制器的自动转化模式下,采集四次值(有没有必要在设置连续转化模式?调试时进行测试),为了消除屏幕抖动,在内核中使用了定时器,定时执行touch_timer_fire函数,接着设置TC控制器进入等待释放中断等待模式。此时要是有释放中断产生,也是在touch_time_fire() 函数的else语句中进行处理的,在处理中将TS控制器再设置为等待按下中断等待模式。即要完成这样的设置:在按下中设置等待释放中断,在释放时设置等待按下中断模式。

调试分析:

press!

report to inputcore

user running!

ABS_X:558,ABS_Y:  0

user running!

ABS_X:558,ABS_Y:483

user running!

ABS_X:558,ABS_Y:483

user running!

按照上面的调试信息,触摸屏向上值报告了一次就退出了,问题可能出现在ADC只启动了一次。

 

注意在按驱动模块调试信息可以在Kconfig中设置,学会用depends关联上调试信息的模块,

调试时注意细节,就是在编译是要在相应的文件夹中原来的编译程序删了重新编译,否则没有更新。在调试触摸屏时也可以作为模块加载的 ,在编译的将文件放在相应的文件夹中,更改makefileconfig文件,调用指令make SUBDRI=drivers/input/ modules进行编译。


 

点击(此处)折叠或打开

  1. /***************************************************
  2.     Touch panel driver
  3. ***************************************************/

  4. #include <linux/errno.h>
  5. #include <linux/kernel.h>
  6. #include <linux/module.h>
  7. #include <linux/slab.h>
  8. #include <linux/input.h>
  9. #include <linux/init.h>
  10. #include <linux/serio.h>
  11. #include <linux/delay.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/clk.h>
  14. #include <asm/io.h>
  15. #include <asm/irq.h>

  16. #include <plat/regs-adc.h>
  17. #include <mach/regs-gpio.h>
  18. #undef DEBUG
  19. //#define DEBUG
  20. #ifdef DEBUG
  21. #define dprintk(x...) {printk(x);}
  22. #else
  23. #define dprintk(x...) (void)(0)
  24. #endif

  25. static void __iomem *base_addr;
  26. static struct clk    *adc_clock;
  27. static char *ts_name="SUN ToucnScreen";
  28. #define S3C2410TSVERSION    0x0101

  29. static struct input_dev *dev;//定义输入设备
  30. extern struct semaphore ADC_LOCK;
  31. static int OwnADC = 0;
  32. static long xp;
  33. static long yp;
  34. static int count;//记录按键按下的次数、




  35. #define WAIT4INT(x) (((x)<<8) | \
  36.          S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
  37.          S3C2410_ADCTSC_XY_PST(3))

  38. #define AUTOPST     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
  39.          S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
  40. /*策略是在按下中报告事件的,而不是在ADC完成采集后报告,可以在调试的时候尝试一下*/
  41. static void touch_timer_fire(unsigned long data)
  42. {
  43.     
  44.     unsigned long data0;
  45.     unsigned long data1;
  46.     int updown;
  47.     data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  48.     data1 = ioread32(base_addr+S3C2410_ADCDAT1);            
  49.     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  50.     if(updown)
  51.         {
  52.             //如果是按下状态并且ADC已经完成了转化就报告事件和数据
  53.             if(count!=0)
  54.                 {
  55.                     long tmp;
  56.                     tmp = xp;
  57.                     xp = yp;
  58.                     yp = tmp;
  59.                         xp >>= 2;
  60.                         yp >>= 2;
  61.             #ifdef CONFIG_TOUCHSCREEN_TQ2440_DEBUG
  62.             printk("xp:%d,yp:%d",xp,yp);
  63.             #endif
  64.              input_report_abs(dev, ABS_X, xp);
  65.              input_report_abs(dev, ABS_Y, yp);

  66.              input_report_key(dev, BTN_TOUCH, 1);
  67.              input_report_abs(dev, ABS_PRESSURE, 1);
  68.              input_sync(dev);
  69.             
  70.                 }
  71.             //调试时问题就出在这里,count>0时那么就不会执行else中的语句,这就导致ADC没有被是能工作,count永远无法清零,导致程序直执行一次
  72.             //else
  73.             //    {
  74.                     //这种情况就是按下,但是AD转化没有完成,设置TSC为自动转化模式,启动ADC
  75.                     
  76.                     xp = 0;
  77.                      yp = 0;
  78.                      count = 0;
  79.                     iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  80.                      iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  81.             //    }
  82.         }
  83.     else
  84.         {
  85.             //这种情况就是抬起状态
  86.             //发送抬起事件状态
  87.             count = 0;
  88.              input_report_key(dev, BTN_TOUCH, 0);
  89.              input_report_abs(dev, ABS_PRESSURE, 0);
  90.              input_sync(dev);
  91.             //设置等待中断模式
  92.             //ADTSC的UD_SEN位为0表示等待触摸屏被按下中断
  93.              iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  94.             //按键抬起表示事件的完成,所以释然信号量
  95.             if (OwnADC)
  96.             {
  97.                 OwnADC = 0;
  98.                 up(&ADC_LOCK);
  99.             }
  100.             
  101.         }
  102. }
  103. /***************************************************
  104.          触摸屏中断
  105.  按照正常的程序是判断是否是按下,则启动ADC转化,设置
  106.  进入连续转换模式,如果是松开状态则是设置进入等待中断
  107.  模式
  108. ***************************************************/
  109. static irqreturn_t stylus_updown(int irq, void *dev_id)
  110. {
  111.     unsigned long data0;
  112.     unsigned long data1;
  113.     int updown;

  114.     if(down_trylock(&ADC_LOCK)==0)//表示获得信号量
  115.         {
  116.             OwnADC = 1;
  117.             //为了判断触摸屏的状态读取两个数据寄存器中的值
  118.             data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  119.             data1 = ioread32(base_addr+S3C2410_ADCDAT1);            
  120.             updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  121.             if(updown)
  122.                 {
  123.                     
  124.                     touch_timer_fire(0);//这个函数肯定是用来启动ADC的
  125.                 }
  126.                 else
  127.                 {
  128.                     OwnADC = 0;
  129.                     up(&ADC_LOCK);
  130.                 }
  131.             
  132.         }
  133.     
  134.     
  135.     return IRQ_HANDLED;
  136.         
  137. }

  138. static struct timer_list touch_timer =
  139.         TIMER_INITIALIZER(touch_timer_fire, 0, 0);
  140. /***************************************************
  141.          ADC中断
  142.          在ADC中进行四次坐标采样,取平均值,
  143.          比较稳定,
  144. ***************************************************/
  145. static irqreturn_t stylus_action(int irq, void *dev_id)
  146. {
  147.     unsigned long data0;
  148.     unsigned long data1;

  149.     if (OwnADC)
  150.     {
  151.         data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  152.         data1 = ioread32(base_addr+S3C2410_ADCDAT1);

  153.         xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  154.         yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  155.         count++;

  156.         if (count < (1<<2))
  157.         {
  158.             //如果连续采样次数小于4,就从新启动ADC,启动连续转化,这时就退出等待中断模式,进而没有
  159.             //TS中断产生,OwnADC这个变量就会始终为1
  160.             iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
  161.             iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  162.         }
  163.         //这里是有一个策略,就是延迟,防止屏幕抖动,造成的误判,延迟时间时1个jiffies大概是10ms的样子
  164.         //当ADC转化次数大于4就会停止在一个时间滴答内是停止ADC工作的
  165.         else
  166.         {
  167.             
  168.             mod_timer(&touch_timer, jiffies+1);
  169.             
  170.             //设置等待中断模式
  171.             //ADTSC的UD_SEN位为0表示等待触摸屏释放中断
  172.             iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
  173.         }
  174.     }

  175.     return IRQ_HANDLED;
  176. }

  177. /***************************************************
  178.          设备初始化
  179. ***************************************************/
  180. static int __init ts_init(void)
  181. {
  182.     struct input_dev *input_dev;
  183.     adc_clock = clk_get(NULL, "adc");
  184.     if (!adc_clock)
  185.     {
  186.         printk(KERN_ERR "failed to get adc clock source\n");
  187.         return -ENOENT;
  188.     }
  189.     clk_enable(adc_clock);
  190.     printk("success to get adc clock source\n");
  191.    //ioremap va to kernel
  192.    base_addr=ioremap(S3C2410_PA_ADC,0x20);
  193.      if (base_addr == NULL)
  194.     {
  195.         printk(KERN_ERR "Failed to remap register block\n");
  196.         return -ENOMEM;
  197.     }
  198.    //ADCCON设置 设置预分频使能 分频值为255
  199.    iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
  200.    //iowrite32((1<<14)|(0xff)<<6),base_addr);
  201.    iowrite32(0xffff, base_addr+S3C2410_ADCDLY);
  202.    /*初始化设置ADCTSC为等待中断状态*/
  203.    //iowrite32(0xD3, base_addr+S3C2410_ADCTSC);
  204.     iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
  205.   /*申请中断*/
  206.   //request_irq(IRQ_ADC,stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM,ts_name,dev);
  207.   //request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, ts_name, dev);
  208.   
  209.    /*input设备*/
  210.    input_dev=input_allocate_device();
  211.    
  212.   
  213.     if (!input_dev)
  214.     {
  215.         printk(KERN_ERR "Unable to allocate the input device !!\n");
  216.         return -ENOMEM;
  217.     }
  218.     /*确定分配成功后才开始进行赋值操作*/
  219.          dev = input_dev;
  220.     dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  221.     dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
  222.     input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);
  223.     input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
  224.     input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
  225.    /*这些信息在驱动挂载后会出现在/proc/bus/input/device*/
  226.     dev->name = ts_name;
  227.     dev->id.bustype = BUS_RS232;
  228.     dev->id.vendor = 0xDEAD;
  229.     dev->id.product = 0xBEEF;
  230.     dev->id.version = S3C2410TSVERSION;
  231.     //先有设备再有中断申请,否则中断申请函数中有个参数没有办法确定,导致中断申请失败
  232.     if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, ts_name, dev))
  233.     {
  234.         printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  235.         iounmap(base_addr);
  236.         return -EIO;
  237.     }
  238.     if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, ts_name, dev))
  239.     {
  240.         printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  241.         iounmap(base_addr);
  242.         return -EIO;
  243.     }
  244.     /*将触摸屏设备注册到输入子系统中*/
  245.     input_register_device(dev);
  246.      printk( "%s successfully loaded\n", ts_name);
  247.     return 0;
  248.        
  249. }
  250. /***************************************************
  251.          设备卸载
  252. ***************************************************/

  253. static void __exit ts_exit(void)
  254. {
  255.     disable_irq(IRQ_ADC);
  256.     disable_irq(IRQ_TC);
  257.     free_irq(IRQ_TC,dev);
  258.     free_irq(IRQ_ADC,dev);

  259.     if (adc_clock)
  260.     {
  261.         clk_disable(adc_clock);
  262.         clk_put(adc_clock);
  263.         adc_clock = NULL;
  264.     }

  265.     input_unregister_device(dev);
  266.     iounmap(base_addr);
  267. }

  268. module_init(ts_init);
  269. module_exit(ts_exit);


 

阅读(1883) | 评论(0) | 转发(3) |
0

上一篇:没有了

下一篇:Linux多线程编程简单实用一例

给主人留下些什么吧!~~