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

全部博文(11)

文章存档

2013年(11)

我的朋友

分类: LINUX

2013-09-07 23:50:17

1. 电阻式触摸屏的原理    

    触摸屏已经在现实生活中大量使用,比如银行的自动存/取款机等。触摸屏的种类有很多,比如超声波触摸屏、红外触摸屏、电容触摸屏、电阻触摸屏等。
    电阻触摸屏由于造价低廉,在电气上可以直接接入用户的系统中而得到大量使用。电阻触摸屏有几种类型,比如“四线”,“五线”和“八线”。线越多,精度就越高,温度漂移也就越少,但基本的操作是一样的。它本质上是个电阻分压器,将矩形区域中触摸点(x,y)的物理位置转换为代表x坐标和y坐标的电压。


2. S3C2440触摸屏接口    

    S3C2440的触摸屏接口可以驱动四线电阻触摸屏,其接口结构如下:


    其接四线电阻触摸屏的等效电路如下:



3. 等效原理图分析

    触摸屏首先需要进入“等待中断模式”,去检测触摸屏被按下或被释放。
    检测按下操作:此时只有S5 和S4 是被接通的,Y_ADC处为高电平,当触摸屏被按下时,X轴和Y轴就被接通,这样Y_ADC就会检测到高电压到低电压的转变,从而触发INC_TC中断。
    检测触摸屏弹起是同样的道理:此时依然只有S5 和S4 是接通的,在按下后到释放前,Y_ADC处都为低电平,当释放后,X轴和Y轴分离,这样Y_ADC就会检测到低电平到高电平的转变,从而触发INC_TC中断。
    那么怎么知道INC_TC中断是由触摸屏被按下触发的还是被释放触发的呢?这个在2440中非常简单,因为可以通过ADCDATX寄存器中UPDOWN位得到相应信息。同样可以通过设置ADCTSC寄存器的UD_SEN位来设定“等待中断模式”是去检测按下还是释放。

    当触摸屏被按下后,我们就需要去得到相应的坐标信息。
    检测X坐标:此时只有S1 和S3接地,这样X轴就有电压分布,因为按下的状态,X轴和Y轴是相通的,所以X_ADC就被接在触摸点处,自然就可以得到相应的分压,自然也就得到了相应的坐标(需要上层做转换)。
    检测Y坐标:此时只有S2 和S4接地,这样Y轴就有电压分布,因为按下的状态,X轴和Y轴是相通的,所以Y_ADC就被接在触摸点处,自然就可以得到相应的分压。
    S3C2440有“分离的X/Y轴坐标转换模式”和“自动X/Y轴坐标转换模式”两种转换模式,我们一般使用后者,这样就设置相应的寄存器后,硬件会自动完成相应的转换工作,并将结果写入ADCDATX寄存器中的前10位中(2440的ADC分辨率为10位),然后触发INC_ADC中断。


4. 源程序分析

点击(此处)折叠或打开

  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 <asm/io.h>
  7. #include <asm/delay.h>
  8. #include <asm/arch/regs-adc.h>
  9. #include <asm/irq.h>
  10. #include <asm/arch/regs-irq.h>


  11. //#define DEBUG_TS

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

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

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

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

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

  53. //定义每个触摸点采样的次数
  54. #define CVR_CNT    10

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

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


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

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

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

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

  89.     return tmp_val;
  90. }

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

  95.     #ifdef DEBUG_TS
  96.         printk("[do_adc_convert] start!\n");
  97.     #endif
  98.         
  99.     if (get_down_status()) {    //按下状态,读取X轴和Y轴数据。    
  100.         absy = (readl(S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK);     //发现对应的X和Y轴颠倒,故修正
  101.         absx = (readl(S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK);

  102.         ts_devp->px[ts_devp->cnt] = absx; //累加相应坐标的数据,并累加次数
  103.         ts_devp->py[ts_devp->cnt] = absy;
  104.         ts_devp->cnt++;
  105.         
  106.         if (ts_devp->cnt >= CVR_CNT) { //当转换的次数达到预设的值,则调用get_truth_value()求均值
  107.             
  108.             absx = get_truth_value(ts_devp->px, ts_devp->cnt);
  109.             absy = get_truth_value(ts_devp->py, ts_devp->cnt);
  110.             ts_devp->cnt = 0;
  111.         
  112.             input_report_key(ts_devp->dev, BTN_TOUCH, 1); //向输入核心层提交相关数据
  113.             input_report_abs(ts_devp->dev, ABS_X, absx);
  114.             input_report_abs(ts_devp->dev, ABS_Y, absy);
  115.             input_sync(ts_devp->dev);

  116.             #ifdef DEBUG_TS
  117.                 printk("absx is %3d, absy is %3d \n\n", absx, absy);
  118.             #endif

  119.             WAIT_UP_INT(); //进入等待释放模式
  120.             schedule_delayed_work(&ts_work, 10); //进行延时调用,如果是长按或是在滑动,还是会进入自动转换模式,这个比较关键,否则无法得到滑动的坐标点
  121.             #ifdef DEBUG_TS
  122.                 printk("WAIT_UP_INT ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));            
  123.             #endif    
  124.         } else { //如果转换次数不够,则继续采样
  125.             AUTO_PST_INT(); //必须再次设置自动转换并使能ADC,否则S3C2410_ADCDAT0的值有问题
  126.         }
  127.     }
  128. }

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

  133.     reg_tmp = S3C2410_ADCCON_PRSCEN |         // prescaler enable
  134.              S3C2410_ADCCON_PRSCVL(19);    // PCLK=50MHz, AD_CLK<=2.5MHz, AD_CLK = PCLK / (PRSCVL + 1)
  135.     writel(reg_tmp, S3C2410_ADCCON);
  136.     #ifdef DEBUG_TS
  137.         printk("the ADCCON is 0x%x \n", readl(S3C2410_ADCCON));            
  138.     #endif

  139.     writel(5000, S3C2410_ADCDLY);

  140.     WAIT_DOWN_INT(); //自动等待模式(按下)
  141.     #ifdef DEBUG_TS
  142.         printk("the ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));            
  143.     #endif    

  144. }

  145. //发生INC_TC中断时,会进入此函数
  146. static irqreturn_t ts_touch_interrupt(int irq, void *dev_id)
  147. {    
  148.     
  149.     #ifdef DEBUG_TS
  150.         printk("[ts_touch_interrupt] start!\n");
  151.     #endif

  152.     #if 0
  153.     if (get_down_status() && !ts_devp->oflag) {
  154.     #else    
  155.     if (get_down_status()) {
  156.     #endif
  157. //        disable_irq_nosync(irq);
  158. //        ts_devp->oflag = 1;
  159.         AUTO_PST_INT();    
  160.         
  161.         #ifdef DEBUG_TS
  162.             printk("touch down \n\n");
  163.         #endif
  164.     } else {
  165.         input_report_key(ts_devp->dev, BTN_TOUCH, 0);
  166.         input_sync(ts_devp->dev);

  167.         #ifdef DEBUG_TS
  168.             printk("[ts_touch_interrupt] touch up \n\n\n\n");
  169.         #endif

  170.         WAIT_DOWN_INT();
  171. //        ts_devp->oflag = 0;
  172.         #ifdef DEBUG_TS
  173.             printk("WAIT_DOWN_INT ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));            
  174.         #endif    
  175.     }
  176.     return IRQ_HANDLED;    
  177. }

  178. //发生INC_ADC中断时,会进入此函数
  179. static irqreturn_t ts_adc_interrupt(int irq, void *dev_id)
  180. {
  181.     #ifdef DEBUG_TS
  182.         printk("[ts_adc_interrupt] start!\n");
  183.     #endif
  184.     
  185.     schedule_delayed_work(&ts_work, 0); //立即启动ts_work队列,调用do_adc_convert()
  186.     
  187.     return IRQ_HANDLED;
  188. }


  189. /*
  190.  * The functions for inserting/removing us as a module.
  191.  */

  192. static int __init s3c24xx_ts_init(void)
  193. {
  194.     int err;

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

  201.     init_s3c24xx_adcreg();

  202.     ts_devp->dev = input_allocate_device();
  203.     if (!ts_devp->dev)
  204.         return -ENOMEM;

  205.     ts_devp->dev->name = "s3c24xx_touchscreen";
  206.     ts_devp->dev->phys = "s3c24xx/input0";
  207.     ts_devp->dev->id.bustype = BUS_RS232;
  208.     ts_devp->dev->id.vendor = 0xdead;
  209.     ts_devp->dev->id.product = 0xbeef;
  210.     ts_devp->dev->id.version = 0x0101;
  211.     
  212.     ts_devp->dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
  213.     ts_devp->dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);

  214.     input_set_abs_params(ts_devp->dev, ABS_X,
  215.         S3C2440_TS_ABS_X_MIN, S3C2440_TS_ABS_X_MAX, 0, 0);
  216.     input_set_abs_params(ts_devp->dev, ABS_Y,
  217.         S3C2440_TS_ABS_Y_MIN, S3C2440_TS_ABS_Y_MAX, 0, 0);
  218.     //input_set_abs_params(s3c24xx_ts_dev, ABS_PRESSURE,
  219.     //    0, 1, 0, 0);

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

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

  240. fail3:
  241.     free_irq(IRQ_ADC, NULL);
  242.     
  243. fail2:
  244.     free_irq(IRQ_TC, NULL);
  245.     cancel_delayed_work(&ts_work);
  246.     flush_scheduled_work();

  247. fail1:
  248.     input_free_device(ts_devp->dev);
  249.     return err;
  250. }

  251. static void __exit s3c24xx_ts_exit(void)
  252. {
  253.     input_unregister_device(ts_devp->dev);
  254.     
  255.     free_irq(IRQ_ADC, NULL);
  256.     free_irq(IRQ_TC, NULL);
  257.     
  258.     cancel_delayed_work(&ts_work);
  259.     flush_scheduled_work();
  260.     
  261.     input_free_device(ts_devp->dev);
  262.     
  263.     kfree(ts_devp);
  264. }

  265. module_init(s3c24xx_ts_init);
  266. module_exit(s3c24xx_ts_exit);


5. 触摸的方式思考

    触摸的状态有几种? 两种,按下和释放?? 这应该是大部分人的第一反应。我在初期移植的时候也是这种思维,所以走了很多弯路。
    其实应该分为:“短按”,“长按”,“滑动”,“释放”,这样才能更好的完成人际互动,实现例如绘图、手写等操作。
     那么怎么样才能正确检测这些操作呢?最好的办法就是当触摸按下后,在没有弹起来之前就按照一定的频率去采样X/Y轴的坐标,并提交上去,这样上层应用就可以等到这些信息,然后判断是什么样的操作。

    


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