==========================================================

开发环境

编译系统 :fedora9

编译器 :arm-linux-4.4.3

主控芯片 :S3C2440

开发板 :mini2440

==========================================================

一、电阻式触摸屏工作原理 
二、 S3C2440 电阻式触摸屏接口、内部ADC结构
三、Linux输入子系统(InputSubsystem)
四、mini2440触摸屏驱动分析

==========================================================

(这部分转自:http://blog.chinaunix.net/uid-22174347-id-1786941.html 文章写得很好)

四、mini2440触摸屏驱动分析

1、硬件原理图分析

S3c2440芯片内部触摸屏接口与ADC接口是集成在一起的,硬件结构原理图请看:S3C2440上ADC驱动实例开发讲解中的图,其中通道7(XP或AIN7)作为触摸屏接口的X坐标输入,通道5(YP或AIN5)作为触摸屏接口的Y坐标输入。在"S3C2440上ADC驱动实例开发讲解"中,AD转换的模拟信号是由开发板上的一个电位器产生并通过通道1(AIN0)输入的,而这里的模拟信号则是由点触触摸屏所产生的X坐标和Y坐标两个模拟信号,并分别通过通道7和通道5输入。S3c2440提供的触摸屏接口有4种处理模式,分别是:正常转换模式、单独的X/Y位置转换模式、自动X/Y位置转换模式和等待中断模式,对于在每种模式下工作的要求,请详细查看数据手册的描述。本驱动实例将采用自动X/Y位置转换模式和等待中断模式。

注意:在每步中,为了让代码逻辑更加有条理和容易理解,就没有考虑代码的顺序,比如函数要先定义后调用。如果要编译此代码,请严格按照C语言的规范来调整代码的顺序。

2、建立触摸屏驱动程序my2440_ts.c,首先实现加载和卸载部分,在驱动加载部分,我们主要做的事情是:启用ADC所需要的时钟、映射IO口、初始化寄存器、申请中断、初始化输入设备、将输入设备注册到输入子系统。代码如下:

[cpp] view plaincopy
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/clk.h>
  4. #include <linux/init.h>
  5. #include <linux/input.h>
  6. #include <linux/serio.h>
  7. #include <plat/regs-adc.h>
  8. #include <asm/irq.h>
  9. #include <asm/io.h>
  10. /*用于保存从平台时钟列表中获取的ADC时钟*/
  11. static struct clk *adc_clk;
  12. /*定义了一个用来保存经过虚拟映射后的内存地址*/
  13. static void __iomem *adc_base;
  14. /*定义一个输入设备来表示我们的触摸屏设备*/
  15. static struct input_dev *ts_dev;
  16. /*设备名称*/
  17. #define DEVICE_NAME    "my2440_TouchScreen"
  18. /*定义一个WAIT4INT宏,该宏将对ADC触摸屏控制寄存器进行操作
  19. S3C2410_ADCTSC_YM_SEN这些宏都定义在regs-adc.h中*/
  20. #define WAIT4INT(x)    (((x)<<8) | S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \
  21. S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_XY_PST(3))
  22. static int __init ts_init(void)
  23. {
  24. int ret;
  25. /*从平台时钟队列中获取ADC的时钟,这里为什么要取得这个时钟,因为ADC的转换频率跟时钟有关。
  26. 系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/
  27. adc_clk = clk_get(NULL, "adc");
  28. if(!adc_clk)
  29. {
  30. /*错误处理*/
  31. printk(KERN_ERR "falied to find adc clock source\n");
  32. return -ENOENT;
  33. }
  34. /*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/
  35. clk_enable(adc_clk);
  36. /*将ADC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
  37. 注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,
  38. S3C2410_PA_ADC是ADC控制器的基地址,定义在mach-s3c2410/include/mach/map.h中,0x20是虚拟地址长度大小*/
  39. adc_base = ioremap(S3C2410_PA_ADC, 0x20);
  40. if(adc_base == NULL)
  41. {
  42. /*错误处理*/
  43. printk(KERN_ERR "failed to remap register block\n");
  44. ret = -EINVAL;
  45. goto err_noclk;
  46. }
  47. /*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/
  48. adc_initialize();
  49. /*申请ADC中断,AD转换完成后触发。这里使用共享中断IRQF_SHARED是因为该中断号在ADC驱动中也使用了,
  50. 最后一个参数1是随便给的一个值,因为如果不给值设为NULL的话,中断就申请不成功*/
  51. ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DEVICE_NAME, 1);
  52. if(ret)
  53. {
  54. printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);
  55. ret = -EINVAL;
  56. goto err_nomap;
  57. }
  58. /*申请触摸屏中断,对触摸屏按下或提笔时触发*/
  59. ret = request_irq(IRQ_TC, tc_irq, IRQF_SAMPLE_RANDOM, DEVICE_NAME, 1);
  60. if(ret)
  61. {
  62. printk(KERN_ERR "IRQ%d error %d\n", IRQ_TC, ret);
  63. ret = -EINVAL;
  64. goto err_noirq;
  65. }
  66. /*给输入设备申请空间,input_allocate_device定义在input.h中*/
  67. ts_dev = input_allocate_device();
  68. /*下面初始化输入设备,即给输入设备结构体input_dev的成员设置值。
  69. evbit字段用于描述支持的事件,这里支持同步事件、按键事件、绝对坐标事件,
  70. BIT宏实际就是对1进行位操作,定义在linux/bitops.h中*/
  71. ts_dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
  72. /*keybit字段用于描述按键的类型,在input.h中定义了很多,这里用BTN_TOUCH类型来表示触摸屏的点击*/
  73. ts_dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
  74. /*对于触摸屏来说,使用的是绝对坐标系统。这里设置该坐标系统中X和Y坐标的最小值和最大值(0-1023范围)
  75. ABS_X和ABS_Y就表示X坐标和Y坐标,ABS_PRESSURE就表示触摸屏是按下还是抬起状态*/
  76. input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0);
  77. input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
  78. input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
  79. /*以下是设置触摸屏输入设备的身份信息,直接在这里写死。
  80. 这些信息可以在驱动挂载后在/proc/bus/input/devices中查看到*/
  81. ts_dev->name          = DEVICE_NAME;   /*设备名称*/
  82. ts_dev->id.bustype    = BUS_RS232;     /*总线类型*/
  83. ts_dev->id.vendor     = 0xDEAD;        /*经销商ID号*/
  84. ts_dev->id.product    = 0xBEEF;        /*产品ID号*/
  85. ts_dev->id.version    = 0x0101;        /*版本ID号*/
  86. /*好了,一些都准备就绪,现在就把ts_dev触摸屏设备注册到输入子系统中*/
  87. input_register_device(ts_dev);
  88. return 0;
  89. /*下面是错误跳转处理*/
  90. err_noclk:
  91. clk_disable(adc_clk);
  92. clk_put(adc_clk);
  93. err_nomap:
  94. iounmap(adc_base);
  95. err_noirq:
  96. free_irq(IRQ_ADC, 1);
  97. return ret;
  98. }
  99. /*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/
  100. static void adc_initialize(void)
  101. {
  102. /*计算结果为(二进制):111111111000000,再根据数据手册得知
  103. 此处是将AD转换预定标器值设为255、AD转换预定标器使能有效*/
  104. writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF), adc_base + S3C2410_ADCCON);
  105. /*对ADC开始延时寄存器进行设置,延时值为0xffff*/
  106. writel(0xffff, adc_base + S3C2410_ADCDLY);
  107. /*WAIT4INT宏计算结果为(二进制):11010011,再根据数据手册得知
  108. 此处是将ADC触摸屏控制寄存器设置成等待中断模式*/
  109. writel(WAIT4INT(0), adc_base + S3C2410_ADCTSC);
  110. }
  111. static void __exit ts_exit(void)
  112. {
  113. /*屏蔽和释放中断*/
  114. disable_irq(IRQ_ADC);
  115. disable_irq(IRQ_TC);
  116. free_irq(IRQ_ADC, 1);
  117. free_irq(IRQ_TC, 1);
  118. /*释放虚拟地址映射空间*/
  119. iounmap(adc_base);
  120. /*屏蔽和销毁时钟*/
  121. if(adc_clk)
  122. {
  123. clk_disable(adc_clk);
  124. clk_put(adc_clk);
  125. adc_clk = NULL;
  126. }
  127. /*将触摸屏设备从输入子系统中注销*/
  128. input_unregister_device(ts_dev);
  129. }
  130. module_init(ts_init);
  131. module_exit(ts_exit);
  132. MODULE_LICENSE("GPL");
  133. MODULE_AUTHOR("s3c2440 tp");
  134. MODULE_DESCRIPTION("My2440 Touch Screen Driver");

2、接下来要做的是,在两个中断服务程序中实现触摸屏状态和坐标的转换。先看代码,如下:

[cpp] view plaincopy
  1. /*定义一个外部的信号量ADC_LOCK,因为ADC_LOCK在ADC驱动程序中已申明
  2. 这样就能保证ADC资源在ADC驱动和触摸屏驱动中进行互斥访问*/
  3. extern struct semaphore ADC_LOCK;
  4. /*做为一个标签,只有对触摸屏操作后才对X和Y坐标进行转换*/
  5. static int OwnADC = 0;
  6. /*用于记录转换后的X坐标值和Y坐标值*/
  7. static long xp;
  8. static long yp;
  9. /*用于计数对触摸屏压下或抬起时模拟输入转换的次数*/
  10. static int count;
  11. /*定义一个AUTOPST宏,将ADC触摸屏控制寄存器设置成自动转换模式*/
  12. #define AUTOPST    (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
  13. S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
  14. /*触摸屏中断服务程序,对触摸屏按下或提笔时触发执行*/
  15. static irqreturn_t tc_irq(int irq, void *dev_id)
  16. {
  17. /*用于记录这一次AD转换后的值*/
  18. unsigned long data0;
  19. unsigned long data1;
  20. /*用于记录触摸屏操作状态是按下还是抬起*/
  21. int updown;
  22. /*ADC资源可以获取,即上锁*/
  23. if (down_trylock(&ADC_LOCK) == 0)
  24. {
  25. /*标识对触摸屏进行了操作*/
  26. OwnADC = 1;
  27. /*读取这一次AD转换后的值,注意这次主要读的是状态*/
  28. data0 = readl(adc_base + S3C2410_ADCDAT0);
  29. data1 = readl(adc_base + S3C2410_ADCDAT1);
  30. /*记录这一次对触摸屏是压下还是抬起,该状态保存在数据寄存器的第15位,所以与上S3C2410_ADCDAT0_UPDOWN*/
  31. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  32. /*判断触摸屏的操作状态*/
  33. if (updown)
  34. {
  35. /*如果是按下状态,则调用touch_timer_fire函数来启动ADC转换,该函数定义后面再讲*/
  36. touch_timer_fire(0);
  37. }
  38. else
  39. {
  40. /*如果是抬起状态,就结束了这一次的操作,所以就释放ADC资源的占有*/
  41. OwnADC = 0;
  42. up(&ADC_LOCK);
  43. }
  44. }
  45. return IRQ_HANDLED;
  46. }
  47. static void touch_timer_fire(unsigned long data)
  48. {
  49. /*用于记录这一次AD转换后的值*/
  50. unsigned long data0;
  51. unsigned long data1;
  52. /*用于记录触摸屏操作状态是按下还是抬起*/
  53. int updown;
  54. /*读取这一次AD转换后的值,注意这次主要读的是状态*/
  55. data0 = readl(adc_base + S3C2410_ADCDAT0);
  56. data1 = readl(adc_base + S3C2410_ADCDAT1);
  57. /*记录这一次对触摸屏是压下还是抬起,该状态保存在数据寄存器的第15位,所以与上S3C2410_ADCDAT0_UPDOWN*/
  58. updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
  59. /*判断触摸屏的操作状态*/
  60. if (updown)
  61. {
  62. /*如果状态是按下,并且ADC已经转换了就报告事件和数据*/
  63. if (count != 0)
  64. {
  65. long tmp;
  66. tmp = xp;
  67. xp = yp;
  68. yp = tmp;
  69. xp >>= 2;
  70. yp >>= 2;
  71. #ifdef CONFIG_TOUCHSCREEN_MY2440_DEBUG
  72. /*触摸屏调试信息,编译内核时选上此项后,点击触摸屏会在终端上打印出坐标信息*/
  73. struct timeval tv;
  74. do_gettimeofday(&tv);
  75. printk(KERN_DEBUG "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, xp, yp);
  76. #endif
  77. /*报告X、Y的绝对坐标值*/
  78. input_report_abs(ts_dev, ABS_X, xp);
  79. input_report_abs(ts_dev, ABS_Y, yp);
  80. /*报告触摸屏的状态,1表明触摸屏被按下*/
  81. input_report_abs(ts_dev, ABS_PRESSURE, 1);
  82. /*报告按键事件,键值为1(代表触摸屏对应的按键被按下)*/
  83. input_report_key(ts_dev, BTN_TOUCH, 1);
  84. /*等待接收方受到数据后回复确认,用于同步*/
  85. input_sync(ts_dev);
  86. }
  87. /*如果状态是按下,并且ADC还没有开始转换就启动ADC进行转换*/
  88. xp = 0;
  89. yp = 0;
  90. count = 0;
  91. /*设置触摸屏的模式为自动转换模式*/
  92. writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, adc_base + S3C2410_ADCTSC);
  93. /*启动ADC转换*/
  94. writel(readl(adc_base + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base + S3C2410_ADCCON);
  95. }
  96. else
  97. {
  98. /*否则是抬起状态*/
  99. count = 0;
  100. /*报告按键事件,键值为0(代表触摸屏对应的按键被释放)*/
  101. input_report_key(ts_dev, BTN_TOUCH, 0);
  102. /*报告触摸屏的状态,0表明触摸屏没被按下*/
  103. input_report_abs(ts_dev, ABS_PRESSURE, 0);
  104. /*等待接收方受到数据后回复确认,用于同步*/
  105. input_sync(ts_dev);
  106. /*将触摸屏重新设置为等待中断状态*/
  107. writel(WAIT4INT(0), adc_base + S3C2410_ADCTSC);
  108. /*如果触摸屏抬起,就意味着这一次的操作结束,所以就释放ADC资源的占有*/
  109. if (OwnADC)
  110. {
  111. OwnADC = 0;
  112. up(&ADC_LOCK);
  113. }
  114. }
  115. }
  116. /*定义并初始化了一个定时器touch_timer,定时器服务程序为touch_timer_fire*/
  117. static struct timer_list touch_timer = TIMER_INITIALIZER(touch_timer_fire, 0, 0);
  118. /*ADC中断服务程序,AD转换完成后触发执行*/
  119. static irqreturn_t adc_irq(int irq, void *dev_id)
  120. {
  121. /*用于记录这一次AD转换后的值*/
  122. unsigned long data0;
  123. unsigned long data1;
  124. if(OwnADC)
  125. {
  126. /*读取这一次AD转换后的值,注意这次主要读的是坐标*/
  127. data0 = readl(adc_base + S3C2410_ADCDAT0);
  128. data1 = readl(adc_base + S3C2410_ADCDAT1);
  129. /*记录这一次通过AD转换后的X坐标值和Y坐标值,根据数据手册可知,X和Y坐标转换数值
  130. 分别保存在数据寄存器0和1的第0-9位,所以这里与上S3C2410_ADCDAT0_XPDATA_MASK就是取0-9位的值*/
  131. xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  132. yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  133. /*计数这一次AD转换的次数*/
  134. count++;
  135. if (count < (1<<2))
  136. {
  137. /*如果转换的次数小于4,则重新启动ADC转换*/
  138. writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, adc_base + S3C2410_ADCTSC);
  139. writel(readl(adc_base + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base + S3C2410_ADCCON);
  140. }
  141. else
  142. {
  143. /*否则,启动1个时间滴答的定时器,这是就会去执行定时器服务程序上报事件和数据*/
  144. mod_timer(&touch_timer, jiffies + 1);
  145. writel(WAIT4INT(1), adc_base + S3C2410_ADCTSC);
  146. }
  147. }
  148. return IRQ_HANDLED;
  149. }

3、我们从整体上描述转换这个的过程:

(1)如果触摸屏感觉到触摸,则触发触摸屏中断即进入tc_irq,获取ADC_LOCK后判断触摸屏状态为按下,则调用touch_timer_fire启动ADC转换;

(2)当ADC转换启动后,触发ADC中断即进入adc_irq,如果这一次转换的次数小于4,则重新启动ADC进行转换,如果4次完毕后,启动1个时间滴答的定时器,停止ADC转换,也就是说在这个时间滴答内,ADC转换是停止的;

(3)这里为什么要在1个时间滴答到来之前停止ADC的转换呢?这是为了防止屏幕抖动。

(4)如果1个时间滴答到来则进入定时器服务程序touch_timer_fire,判断触摸屏仍然处于按下状态则上报事件和转换的数据,并重启ADC转换,重复第(2)步;

(5)如果触摸抬起了,则上报释放事件,并将触摸屏重新设置为等待中断状态。

4、整体流程如下图所示

基于 mini2440 电阻式触摸屏(四):mini2440触摸屏驱动分析相关推荐

  1. 基于 mini2440 电阻式触摸屏:电阻式触摸屏工作原理

    ========================================================== 开发环境 编译系统 :fedora9 编译器 :arm-linux-4.4.3 主 ...

  2. mini2440驱动分析之触摸屏

    mini2440驱动分析之触摸屏 mini2440触摸屏驱动对应的文件为mini2440_ts.c,他是作为输入设备注册到内核的,功能实现是通过输入子系统来完成的,现在分析触摸屏的实现.以后再分析输入 ...

  3. 一个单片机搞定USB电阻式触摸屏,完美解决飞点问题。

    一个单片机搞定USB电阻式触摸屏,完美解决飞点问题. 参考文章: (1)一个单片机搞定USB电阻式触摸屏,完美解决飞点问题. (2)https://www.cnblogs.com/zkp2010/ar ...

  4. STM32F407之电阻式触摸屏

    一,基础知识 1,相关网站: 中国触摸屏网---http://www.51touch.com/ 2,电阻技术触摸屏的最基本的原理 :当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 ...

  5. 电阻式触摸屏的驱动程序和详细注释

    电阻式触摸屏的驱动程序和详细注释 #include <linux/config.h> #include <linux/errno.h> #include <linux/k ...

  6. 电容屏和电磁屏 一:电阻式触摸屏 二:电容式触摸屏以及原理 三:电磁感应触摸屏 PDF电磁屏签名: 签名完成后,点坐转化文件: android 开发 实现网页跳转

    目录 电容屏和电磁屏 一:电阻式触摸屏 二:电容式触摸屏以及原理 三:电磁感应触摸屏

  7. 电阻触摸屏和电容触摸屏的工作原理及优缺点

    随着科技的发展使用需求的增长,触摸屏行业经历了从低档到高档逐步升级的过程,触摸技术已渗透到各行各业中.按照工作原理可以分为电阻触摸屏和电容触摸屏两大类.下面沐渥小编从二者的工作原理和优缺点出发,教大家 ...

  8. 移植基于linux-2.6.26.5内核s3c2410触摸屏驱动移植

    移植基于linux-2.6.26.5内核s3c2410触摸屏驱动移植的过程记录下来: (1)首先打一个补丁:s3c2410_touchscreen.patch, 在内核解压的根目录下 patch -N ...

  9. 基于S7-200 PLC组态王组态和触摸屏的洗衣机控制系统的设计

    基于S7-200 PLC组态王组态和触摸屏的洗衣机控制系统的设计 熟悉工业电气控制系统的组成,熟悉PLC系统的设计. 全自动洗衣机能够根据衣物的质地.数量及脏污程度 来实现标准洗涤.柔顺洗涤等多功能模 ...

  10. 基于S7-200 PLC组态王组态和触摸屏的洗衣机控制系统的设计 熟悉工业电气控制系统的组成,熟悉PLC系统的设计

    基于S7-200 PLC组态王组态和触摸屏的洗衣机控制系统的设计 熟悉工业电气控制系统的组成,熟悉PLC系统的设计. 全自动洗衣机能够根据衣物的质地.数量及脏污程度 来实现标准洗涤.柔顺洗涤等多功能模 ...

最新文章

  1. Scrum工具Leangoo产品白皮书
  2. C#工厂方法模式 -抽象工厂模式
  3. 工控项目开发框架介绍
  4. 手把手教你写Java项目文档
  5. 【滤波器】基于matlab高斯滤波器【含Matlab源码 995期】
  6. PPT图形绘制:端午节PPT模板背景
  7. UE4编辑器扩展踩坑血泪史
  8. 梯形图请用c语言,简单使用C语言写梯形图精简V2.0.pdf
  9. 对于rpx的理解和使用
  10. EasyTest 接口自动化测试平台 搭建手顺
  11. 开车的人和不开车的人思维有什么区别?
  12. 【JZOJ A组省选】词典
  13. Suzy找到实习了吗Day 7 | 哈希表结束啦 454. 四数相加 II,383. 赎金信,15. 三数之和,18. 四数之和
  14. Spine IK 约束
  15. 拼手气红包java_拼手气红包-基于继承实现(Java萌新篇)
  16. PTA天梯赛L1-011 A-B(C++实现)
  17. 支付宝上线直播功能,网友怒赞:YunOS成了
  18. AHOI 2002 黑白瓷砖 题解
  19. Unity 2D 学习手札(六)Sprite 动画
  20. 摘录:三件大事和三件小事

热门文章

  1. 网络投票专家投票计算_安全专家说在线投票是一个坏主意。 这就是为什么。
  2. elementUI_drawer踩坑_抽屉关闭问题
  3. Java icetea_java – OpenJDK 8的IcedTea插件
  4. 易掌管-计件工资系统免费登记派送中
  5. 误差柱状图的三种实现方法
  6. 什么是 Office Open XML 文件格式
  7. 就Linux的一些操作,王里面导入jdk,mysql,tomcat三个包,实现项目在虚拟机上面运行
  8. 联想服务器修改显存,如何调整用于显卡的共享内存
  9. 沉迷机器人操作系统的一个理由和四种修仙秘籍(更新ROS 2.0内容)
  10. 2019强网杯upload