Chinaunix首页 | 论坛 | 博客
  • 博客访问: 50105
  • 博文数量: 11
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 164
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-05 23:14
文章分类

全部博文(11)

文章存档

2013年(11)

我的朋友

分类: LINUX

2013-09-14 23:38:40

直接上源代码:

点击(此处)折叠或打开

  1. #include <linux/input.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <linux/interrupt.h>
  5. #include <linux/clk.h>
  6. #include <linux/jiffies.h>

  7. #include <asm/io.h>
  8. #include <asm/delay.h>
  9. #include <asm/arch/regs-adc.h>
  10. #include <asm/irq.h>
  11. #include <asm/arch/regs-irq.h>

  12. //#define DEBUG_TS

  13. #define TCMODNAME     "ts_touch"
  14. #define ADCMODNAME    "ts_adc"

  15. #define S3C2440_TS_ABS_X_MIN    0
  16. #define S3C2440_TS_ABS_X_MAX    0x3ff
  17. #define S3C2440_TS_ABS_Y_MIN    0
  18. #define S3C2440_TS_ABS_Y_MAX    0x3ff

  19. #define PEN_DOWN (0 << 8)
  20. #define PEN_UP (1 << 8)

  21. //进入等待中断模式,等待触摸屏被按下
  22. #define WAIT_DOWN_INT() { \
  23.                             writel(PEN_DOWN | \
  24.                             S3C2410_ADCTSC_YM_SEN | \
  25.                             S3C2410_ADCTSC_YP_SEN | \
  26.                             S3C2410_ADCTSC_XP_SEN | \
  27.                             S3C2410_ADCTSC_XY_PST(3), S3C2410_ADCTSC); \
  28.                         }
  29.                         
  30. //进入等待中断模式,等待触摸屏被按下
  31. #define WAIT_UP_INT() { \
  32.                             writel(PEN_UP | \
  33.                             S3C2410_ADCTSC_YM_SEN | \
  34.                             S3C2410_ADCTSC_YP_SEN | \
  35.                             S3C2410_ADCTSC_XP_SEN | \
  36.                             S3C2410_ADCTSC_XY_PST(3), S3C2410_ADCTSC); \
  37.                         }
  38.                         
  39. //使能ADC转换
  40. #define ENABLE_START()    { \
  41.                             writel( \
  42.                             readl(S3C2410_ADCCON) | \
  43.                             S3C2410_ADCCON_ENABLE_START , \
  44.                             S3C2410_ADCCON); \
  45.                         }

  46. //进入自动X/Y坐标转换模式
  47. #define AUTO_PST_INT() { \
  48.                             writel( \
  49.                             S3C2410_ADCTSC_PULL_UP_DISABLE | \
  50.                             S3C2410_ADCTSC_AUTO_PST | \
  51.                             S3C2410_ADCTSC_XY_PST(0), S3C2410_ADCTSC); \
  52.                             ENABLE_START(); \
  53.                         }

  54. //定义每个触摸点采样的次数
  55. #define CVR_CNT    6 //必须大于3,因为会去掉最大和最小值

  56. struct s3c24xx_ts_dev {
  57.     struct input_dev *dev;
  58.     struct clk *clk;
  59.     unsigned int cnt; //for adc
  60.     int px[CVR_CNT];
  61.     int py[CVR_CNT];
  62.     int old_px;
  63.     int old_py;
  64.     unsigned int oflag; //for ts
  65. };
  66. static struct s3c24xx_ts_dev *ts_devp;

  67. //定义一个延时队列,将坐标相关工作移到中断下半部完成
  68. static void do_adc_convert(struct work_struct *work);
  69. static DECLARE_DELAYED_WORK(ts_work, do_adc_convert);


  70. //检测是否是按下模式
  71. static unsigned int get_down_status(void)
  72. {
  73.     return !((readl(S3C2410_ADCDAT0) & S3C2410_ADCDAT0_UPDOWN) >> 15) &&
  74.                 !((readl(S3C2410_ADCDAT1) & S3C2410_ADCDAT1_UPDOWN) >> 15); //通过读取ADCDATX的第15位,等到相关信息
  75. }

  76. //计算平均值,并将最小和最大的两个数据丢弃
  77. static int get_truth_value(int *dat, unsigned int cnt)
  78. {
  79.     int tmp_min, tmp_max, tmp_val, tmp_sum = 0;
  80.     unsigned int i;

  81.     tmp_min = tmp_max = tmp_sum = dat[0];
  82.     
  83.     for (i = 1; i < cnt-1; i++) {
  84.         if(tmp_min > dat[i])
  85.             tmp_min = dat[i];
  86.         if(tmp_max < dat[i])
  87.             tmp_max = dat[i];
  88.         tmp_sum += dat[i];
  89.     }

  90.     tmp_val = (tmp_sum - tmp_min - tmp_max) / (cnt - 2);

  91.     return tmp_val;
  92. }

  93. static int is_big_deviation(int new_x, int new_y, int old_x, int old_y)
  94. {
  95.     int tmp_x = new_x - old_x;
  96.     int tmp_y = new_y - old_y;
  97.     
  98.     if ((tmp_x < -3 || tmp_x > 3) ||
  99.             (tmp_y < -3 || tmp_y > 3))
  100.         return 1;

  101.     else
  102.         return 0;
  103. }

  104. //获取ADC转换数据,进行累加,平均,之后提交输入事件。
  105. static void do_adc_convert(struct work_struct *work)
  106. {
  107.     int absx = 0, absy = 0;

  108.     if (get_down_status()) {    //按下状态,读取X轴和Y轴数据。    
  109.         absy = (readl(S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK);     //发现对应的X和Y轴颠倒,故修正
  110.         absx = (readl(S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK);

  111.         if (1 == ts_devp->oflag) {
  112.             if (!is_big_deviation(absx, absy, ts_devp->old_px, ts_devp->old_py)) {
  113.                 ts_devp->oflag = 0; //清除两次采样标记    
  114.                 
  115.                 ts_devp->px[ts_devp->cnt] = absx; //累加相应坐标的数据,并累加次数
  116.                 ts_devp->py[ts_devp->cnt] = absy;
  117.                 ts_devp->cnt++;

  118.                 if (ts_devp->cnt >= CVR_CNT) {                
  119.                     WAIT_UP_INT(); //进入等待释放模式
  120.                     
  121.                     absx = get_truth_value(ts_devp->px, ts_devp->cnt);
  122.                     absy = get_truth_value(ts_devp->py, ts_devp->cnt);
  123.                     ts_devp->cnt = 0;
  124.                     
  125.                     msleep(6); //必须等待,保证松开的中断被触发,此值需要根据ADCDLY寄存器进行调整

  126.                     if (get_down_status()) {                                        
  127.                         input_report_key(ts_devp->dev, BTN_TOUCH, 1); //向输入核心层提交相关数据
  128.                         input_report_abs(ts_devp->dev, ABS_X, absx);
  129.                         input_report_abs(ts_devp->dev, ABS_Y, absy);
  130.                         input_report_abs(ts_devp->dev, ABS_PRESSURE, 1);
  131.                         input_sync(ts_devp->dev);        

  132.                         schedule_delayed_work(&ts_work, 0); //在前面调用了msleep(),所以再此处直接运行。
  133.                     }
  134.                 } else {
  135.                     AUTO_PST_INT(); //必须再次设置自动转换并使能ADC,否则S3C2410_ADCDAT0的值有问题
  136.                 }
  137.                 
  138.             } else {
  139.                 ts_devp->oflag = 0;
  140.                 AUTO_PST_INT(); //必须再次设置自动转换并使能ADC,否则S3C2410_ADCDAT0的值有问题                
  141.             }
  142.         } else {
  143.             ts_devp->oflag = 1;
  144.             ts_devp->old_px = absx;
  145.             ts_devp->old_py = absy;
  146.             
  147.             AUTO_PST_INT(); //必须再次设置自动转换并使能ADC,否则S3C2410_ADCDAT0的值有问题
  148.         }
  149.     } else {
  150.         input_report_key(ts_devp->dev, BTN_TOUCH, 0);
  151.         input_report_abs(ts_devp->dev, ABS_PRESSURE, 0);
  152.         input_sync(ts_devp->dev);
  153.         
  154.         ts_devp->cnt = 0;
  155.         ts_devp->oflag = 0;        
  156.         
  157.         WAIT_DOWN_INT();
  158.     }
  159. }

  160. //初始化相应的ADC寄存器,设置分频,并进入等待按下模式
  161. static int init_s3c24xx_adcreg(void)
  162. {
  163.     unsigned long reg_tmp;

  164.     reg_tmp = S3C2410_ADCCON_PRSCEN |         // prescaler enable
  165.              S3C2410_ADCCON_PRSCVL(24);        // PCLK=50MHz, AD_CLK<=2.5MHz, AD_CLK = PCLK / (PRSCVL + 1)
  166.     writel(reg_tmp, S3C2410_ADCCON);

  167.     writel(6000, S3C2410_ADCDLY); //在触摸按下和ADC转换之间,需要延时,否则极易出现不稳定的情况。

  168.     WAIT_DOWN_INT(); //自动等待模式(按下)
  169. }

  170. //发生INC_TC中断时,会进入此函数
  171. static irqreturn_t ts_touch_interrupt(int irq, void *dev_id)
  172. {    
  173.     if (get_down_status()) {
  174.         AUTO_PST_INT();    

  175.     } else {
  176.         input_report_key(ts_devp->dev, BTN_TOUCH, 0);
  177.         input_report_abs(ts_devp->dev, ABS_PRESSURE, 0);
  178.         input_sync(ts_devp->dev);

  179.         ts_devp->cnt = 0;
  180.         ts_devp->oflag = 0;    

  181.         WAIT_DOWN_INT();
  182.     }
  183.     return IRQ_HANDLED;    
  184. }

  185. //发生INC_ADC中断时,会进入此函数
  186. static irqreturn_t ts_adc_interrupt(int irq, void *dev_id)
  187. {
  188.     schedule_delayed_work(&ts_work, 0); //立即启动ts_work队列,调用do_adc_convert()
  189.     
  190.     return IRQ_HANDLED;
  191. }


  192. /*
  193.  * The functions for inserting/removing us as a module.
  194.  */

  195. static int __init s3c24xx_ts_init(void)
  196. {
  197.     int err;

  198.     ts_devp = kzalloc(sizeof(struct s3c24xx_ts_dev), GFP_KERNEL);
  199.     if (!ts_devp)
  200.         return -ENOMEM;
  201.     
  202.     ts_devp->clk = clk_get(NULL, "adc");
  203.     clk_enable(ts_devp->clk);

  204.     ts_devp->dev = input_allocate_device();
  205.     if (!ts_devp->dev)
  206.         return -ENOMEM;

  207.     ts_devp->dev->name = "s3c24xx_touchscreen";
  208.     ts_devp->dev->phys = "s3c24xx/input0";
  209.     ts_devp->dev->id.bustype = BUS_RS232;
  210.     ts_devp->dev->id.vendor = 0xdead;
  211.     ts_devp->dev->id.product = 0xbeef;
  212.     ts_devp->dev->id.version = 0x0101;

  213.     ts_devp->cnt = 0;
  214.     ts_devp->oflag = 0;
  215.     
  216.     ts_devp->dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
  217.     ts_devp->dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);

  218.     input_set_abs_params(ts_devp->dev, ABS_X,
  219.         S3C2440_TS_ABS_X_MIN, S3C2440_TS_ABS_X_MAX, 0, 0);
  220.     input_set_abs_params(ts_devp->dev, ABS_Y,
  221.         S3C2440_TS_ABS_Y_MIN, S3C2440_TS_ABS_Y_MAX, 0, 0);
  222.     input_set_abs_params(ts_devp->dev, ABS_PRESSURE,
  223.         0, 1, 0, 0);

  224.     if (request_irq(IRQ_TC, ts_touch_interrupt ,
  225.             IRQF_DISABLED, TCMODNAME, NULL) < 0) {
  226.         printk(KERN_ERR "s3c24xx_ts_input.c: Can't allocate irq %d\n",
  227.              IRQ_TC);
  228.         err = -EBUSY;
  229.         goto fail1;
  230.     }

  231.     if (request_irq(IRQ_ADC, ts_adc_interrupt,
  232.             IRQF_DISABLED, ADCMODNAME, NULL) < 0) {
  233.         printk(KERN_ERR "s3c24xx_ts_input.c: Can't allocate irq %d\n",
  234.              IRQ_ADC);
  235.         err = -EBUSY;
  236.         goto fail2;
  237.     }
  238.         
  239.     err = input_register_device(ts_devp->dev);
  240.     if (err)
  241.         goto fail3;

  242.     init_s3c24xx_adcreg();
  243.     
  244.     return 0;

  245. fail3:
  246.     free_irq(IRQ_ADC, NULL);
  247.     
  248. fail2:
  249.     free_irq(IRQ_TC, NULL);
  250.     cancel_delayed_work(&ts_work);
  251.     flush_scheduled_work();

  252. fail1:
  253.     input_free_device(ts_devp->dev);
  254.     return err;
  255. }

  256. static void __exit s3c24xx_ts_exit(void)
  257. {
  258.     input_unregister_device(ts_devp->dev);
  259.     
  260.     free_irq(IRQ_ADC, NULL);
  261.     free_irq(IRQ_TC, NULL);
  262.     
  263.     cancel_delayed_work(&ts_work);
  264.     flush_scheduled_work();
  265.     
  266.     input_free_device(ts_devp->dev);
  267.     
  268.     kfree(ts_devp);
  269. }

  270. module_init(s3c24xx_ts_init);
  271. module_exit(s3c24xx_ts_exit);

软件在处理跳点时加入了一些处理方法,与上一版有些不同,软件简单测试,工作良好,不过应该还有改进的空间。

这个软件调试花了我2天的时间,周末基本都在弄这个,开发的难点如下:
1、如何去除跳点,于是就采用了采样两点后进行比较,如果误差较大,则废弃这两个坐标。总共采样6次,在6个样本中去掉最大和最小值后取平均值。
2、最后在最后松开时,发生跳点,而之前的所有坐标都OK。我就想是不是松开后相应的中断无法触发(因为一直在自动转换模式中),于是就进行了相应的处理。 --- 想明白这个问题花了近一天的时间,效率低啊~~~
3、有时候底层去处理一些数据还是比较麻烦的,比如在松开时跳点问题,在上层软件中可以直接进行滤除操作。

关于松开时发生跳点的问题,我想在这里再说下。
1、处于自动转换模式时,松开的动作是无法触发中断的。
2、因为已经松开,所以ADC采样的是悬空状态的电压(未知电压)。
3、必须在提交坐标信息到输入子系统之前明确是否是松开状态。

最后有图有真相~~     这是基于TSLIB,优化触摸屏驱动后的~~

阅读(1695) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~