6 linux 触摸屏驱动程序

实验目的和内容

实验目的:复习linux系统总线驱动设备模型,了解在该框架下触摸屏驱动程序的实现步骤。

实验内容:以四线电阻触摸屏为例,实现触摸点XY坐标位置的检测。

电阻触摸屏工作原理简介

触摸屏一般由如下三部分组成:两层透明导体层、中间的隔离层、电极。电阻触摸屏选用租型导体材料。当某一层(X层)电极加上电压(X+、X-)后,会在该层形成电压梯度,当手指按压触摸屏时,平时相互绝缘的两层导体层会在接触点有了接触,此时可在未加电的一层(Y层)测得接触点的电压,再根据电压与电极之间的距离关系,即可换算出该接触点加电层(X层)对应的坐标位置。将电压切换至另一层(Y层),得到另一层(Y层)的坐标位置。即可确定接触点当前的坐标位置。电阻屏的关键在于材料,根据引线多少,分为四线、无线、六线等多线电阻触摸屏。

S3C2440内置的ADC和触摸屏接口如图所示,通过4个MOS管分别给X、Y方向施加电压,通过ADC通道采集X、Y方向的电压值。

触摸屏的使用过程中:

  1. 当有触摸动作时,产生触摸屏INT_TC中断;
  2. 在INT_TC中断服务程序中,启动ADC转换X、Y的坐标;
  3. ADC转换结束,产生ADC中断;
  4. 在ADC中断服务程序中,向上层上报ADC转换的结果;
  5. 松开触摸屏。

为了让触摸屏支持连按、滑动等动作,因此在处理中需增加定时器,在4中一次ADC转换结果上报结束后。启动定时器,一旦定时器时间到达,重新启动一次ADC转换。

程序编写

Linux内核中触摸屏驱动是在input输入子系统的框架上编写的。前面章节已经对输入子系统的结构进行了分析,https://www.cnblogs.com/beijiqie1104/p/11418082.html。其中inputcore层由内核提供,不需要修改,EventHandler层中的Evdev.c文件也可支持所有的输入设备。因此,本次实验只需完成input driver层对触摸屏设备的访问,中断的设置,以及将产生的触摸事件上报给inputcore,即实现触摸屏input_dev的分配,设置、注册,以及硬件相关的配置。

入口函数s3c_ts_init编写如下:

  1. 使用input_allocate_device分配s3c_ts_dev结构体空间;
  2. 配置s3c_ts_dev结构体可以产生EV_KEY、EV_ABS事件,细分为EV_KEY中的BTN_TOUCH事件,EV_ABS事件中ABS_X、ABS_Y、ABS_PRESSURE事件。
  3. 使用input_register_device注册s3c_ts_dev设备。
  4. 硬件相关的配置:
  • ADC/TC时钟使能;
  • 设置S3C2440的ADC/TC的寄存器配置;
  • IRQ_TC中断和IRQ_ADC中断的注册;
  • 进入等待按键按下模式。

出口函数s3c_ts_exit编写如下:

  1. IRQ_TC中断和IRQ_ADC中断的注销;
  2. 取消s3c_ts_dev设备挂载;
  3. 释放分配的s3c_ts_dev结构体空间。

按照上述步骤,已基本完成触摸屏驱动程序的编写,当有触摸动作发生时,已经可以检测到触摸信号。为了得到触摸点的坐标值,还需要在IRQ_TC中断服务函数中处理,当检测是按键按下操作时,调用enter_measure_xy_mode()进入到XY坐标测量模式,启动ADC转换start_adc()。

为了提高坐标检测的精度,采取舍弃无效值,多次测量求平均值,软件过滤等方式。

通过增加定时器,实现按键的连按和滑动检测。

代码如下:

  1. #include <linux/errno.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/module.h>  
  4. #include <linux/slab.h>  
  5. #include <linux/input.h>  
  6. #include <linux/init.h>  
  7. #include <linux/serio.h>  
  8. #include <linux/delay.h>  
  9. #include <linux/platform_device.h>  
  10. #include <linux/clk.h>  
  11. #include <asm/io.h>  
  12. #include <asm/irq.h>  
  13.     
  14. #include <asm/plat-s3c24xx/ts.h>  
  15.     
  16. #include <asm/arch/regs-adc.h>  
  17. #include <asm/arch/regs-gpio.h>  
  18.     
  19. static struct input_dev *s3c_ts_dev;  
  20.     
  21. struct s3c_ts_reg  
  22. {  
  23.     unsigned long  adccon;  
  24.     unsigned long  adctsc;  
  25.     unsigned long  adcdly;  
  26.     unsigned long  adcdat0;  
  27.     unsigned long  adcdat1;  
  28.     unsigned long  adcupdn;  
  29. };  
  30.     
  31. static volatile struct s3c_ts_reg *s3c_ts_regs;  
  32. static struct timer_list s3c_ts_timer;  
  33.     
  34. static void enter_wait_pen_down_mode(void)  
  35. {  
  36.     s3c_ts_regs->adctsc = 0xd3;  
  37. }  
  38.     
  39. static void enter_wait_pen_up_mode(void)  
  40. {  
  41.     s3c_ts_regs->adctsc = 0x1d3;  
  42. }  
  43.     
  44. static void enter_measure_xy_mode(void)  
  45. {  
  46.     s3c_ts_regs->adctsc = (1<<3) | (1<<2);  
  47. }  
  48.     
  49. static void start_adc(void)  
  50. {  
  51.     s3c_ts_regs->adccon |= (1<<0);  
  52. }  
  53.     
  54. static int s3c_filter_ts(int x[], int y[])  
  55. {  
  56. #define ERR_LIMIT 10  
  57.     int avr_x, avr_y;  
  58.     int det_x, det_y;  
  59.     
  60.     avr_x = (x[0] + x[1])/2;  
  61.     avr_y = (y[0] + y[1])/2;  
  62.     
  63.     det_x =(x[2] > avr_x)?(x[2] - avr_x):(avr_x - x[2]);  
  64.     det_y =(y[2] > avr_y)?(y[2] - avr_y):(avr_y - y[2]);  
  65.     
  66.     if((det_x > ERR_LIMIT)||(det_y > ERR_LIMIT))  
  67.         return 0;  
  68.     
  69.     avr_x = (x[1] + x[2])/2;  
  70.     avr_y = (y[1] + y[2])/2;  
  71.     
  72.     det_x =(x[3] > avr_x)?(x[3] - avr_x):(avr_x - x[3]);  
  73.     det_y =(y[3] > avr_y)?(y[3] - avr_y):(avr_y - y[3]);  
  74.     
  75.     if((det_x > ERR_LIMIT)||(det_y > ERR_LIMIT))  
  76.         return 0;  
  77.     
  78.     return 1;  
  79. }  
  80.     
  81.     
  82. static irqreturn_t pen_down_up_irq(int irq, void *dev_id)  
  83. {  
  84.     if(s3c_ts_regs->adcdat0 & (1<<15))  
  85.     {  
  86.         //printk("pen up ");  
  87.         enter_wait_pen_down_mode();  
  88.         input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);  
  89.         input_report_key(s3c_ts_dev, BTN_TOUCH, 0);  
  90.         input_sync(s3c_ts_dev);  
  91.     }  
  92.     else  
  93.     {  
  94.         //printk("pen down ");  
  95.         //enter_wait_pen_up_mode();  
  96.         enter_measure_xy_mode();  
  97.         start_adc();  
  98.     }  
  99.     return IRQ_HANDLED;  
  100. }  
  101.     
  102.     
  103. static irqreturn_t adc_irq(int irq, void *dev_id)  
  104. {  
  105.     static int cnt = 0;  
  106.     static int x[4],y[4];  
  107.     int adcdat0, adcdat1;  
  108.     /* 优化措施2: ADC转换完成时,触摸笔弹起,则扔掉此次转换的数据 */  
  109.         
  110.     adcdat0 = s3c_ts_regs->adcdat0;  
  111.     adcdat1 = s3c_ts_regs->adcdat1;  
  112.     if(adcdat0 & (1<<15))  
  113.     {  
  114.         cnt = 0;  
  115.         enter_wait_pen_down_mode();  
  116.         input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);  
  117.         input_report_key(s3c_ts_dev, BTN_TOUCH, 0);  
  118.         input_sync(s3c_ts_dev);  
  119.     }  
  120.     else  
  121.     {  
  122.         //printk("adc irq  cnt = %d, x = %d, y = %d ", ++cnt, s3c_ts_regs->adcdat0 & 0x3ff, s3c_ts_regs->adcdat1 & 0x3ff);  
  123.         /* 优化措施3 多次测量求平均值 */  
  124.         x[cnt] = adcdat0 & 0x3ff;  
  125.         y[cnt] = adcdat1 & 0x3ff;  
  126.         cnt++;  
  127.         if(cnt == 4)  
  128.         {  
  129.             /* 优化措施4: 软件过滤 */  
  130.             if(s3c_filter_ts(x, y))  
  131.             {  
  132.                 //printk("x = %d, y = %d ",(x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);  
  133.     
  134.                 input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);  
  135.                 input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);  
  136.                 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);  
  137.                 input_report_key(s3c_ts_dev, BTN_TOUCH, 1);  
  138.                 input_sync(s3c_ts_dev);               
  139.             }  
  140.             cnt = 0;  
  141.             enter_wait_pen_up_mode();  
  142.             mod_timer(&s3c_ts_timer, jiffies + HZ/100);  
  143.         }  
  144.         else  
  145.         {  
  146.             enter_measure_xy_mode();  
  147.             start_adc();  
  148.         }             
  149.             
  150.     }  
  151.     return IRQ_HANDLED;  
  152. }  
  153.     
  154. static void s3c_ts_timer_func(unsigned long data)  
  155. {  
  156.     if(s3c_ts_regs->adcdat0 & (1<<15))  
  157.     {  
  158.         /* 弹起 */  
  159.         enter_wait_pen_down_mode();  
  160.     }  
  161.     else  
  162.     {  
  163.         /* 按下 */  
  164.         enter_measure_xy_mode();  
  165.         start_adc();  
  166.     }  
  167. }  
  168.     
  169. static int s3c_ts_init(void)  
  170. {  
  171.     struct clk  *adc_clk ;  
  172.     
  173.     /* 1 分配 */  
  174.     s3c_ts_dev = input_allocate_device();  
  175.         
  176.     /* 2 配置 */  
  177.     /* 2.1、可以产生哪类事件 */  
  178.     set_bit(EV_KEY, s3c_ts_dev->evbit);  
  179.     set_bit(EV_ABS, s3c_ts_dev->evbit);  
  180.         
  181.     /* 2.2 能产生事件中的哪一类子事件 */  
  182.     set_bit(BTN_TOUCH,         s3c_ts_dev->keybit);  
  183.     
  184.     input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);  
  185.     input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);  
  186.     input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);  
  187.         
  188.     /* 3 注册 */  
  189.     input_register_device(s3c_ts_dev);  
  190.         
  191.     /* 4 硬件相关配置 */  
  192.     /* 4.1 使能时钟*/  
  193.     adc_clk = clk_get(NULL, "adc");  
  194.     if(!adc_clk)  
  195.     {  
  196.         printk(KERN_ERR "failed to get adc clock source ");  
  197.         return -ENOENT;  
  198.     }  
  199.     clk_enable(adc_clk);  
  200.     
  201.     /* 4.2 设置s3c2440ADC/TS寄存器 */  
  202.     s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_reg));  
  203.     
  204.     /* ADCCON 
  205.      * ADCCON[14]  : 1 A/D converter prescaler enable 
  206.      * ADCCON[13:6]:   A/D converter prescaler value 
  207.      *              49, ADCCLK = 50MHz/(49+1) = 1 MHz 
  208.      * ADCCON[5:3]: Analog input channel select 
  209.                     000 = AIN 0 
  210.                     001 = AIN 1 
  211.                     010 = AIN 2 
  212.                     011 = AIN 3 
  213.                     100 = YM 
  214.                     101 = YP 
  215.                     110 = XM 
  216.                     111 = XP                     
  217.      * ADCCON[2]:  Standby mode select 
  218.                     0 = Normal operation mode 
  219.      * ADCCON[0]:  A/D conversion starts by enable 
  220.      *              0 = No operation 
  221.                     1 = A/D conversion starts and this bit is cleared after the startup 
  222.      */  
  223.     s3c_ts_regs->adccon = (1<<14) |(49<<6);  
  224.     
  225.     request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM,  
  226.             "s3c_ts_pen", NULL);  
  227.     
  228.     request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM,  
  229.             "adc", NULL);  
  230.     
  231.     /* 优化措施1 : 
  232.      * 设置ADCDLY为最大,确保电压稳定后再触发IRQ_TC中断 
  233.      */  
  234.     s3c_ts_regs->adcdly = 0xffff;  
  235.     
  236.     /* 优化措施2: 
  237.      *启动定时器,处理长按/滑动的情况  
  238.      */  
  239.     init_timer(&s3c_ts_timer);  
  240.     s3c_ts_timer.function = s3c_ts_timer_func;  
  241.     add_timer(&s3c_ts_timer);  
  242.         
  243.     enter_wait_pen_down_mode();  
  244.         
  245.     return 0;  
  246. }  
  247.     
  248. static void s3c_ts_exit(void)  
  249. {  
  250.     free_irq(IRQ_TC, NULL);  
  251.     free_irq(IRQ_ADC, NULL);  
  252.     iounmap(s3c_ts_regs);  
  253.     input_unregister_device(s3c_ts_dev);  
  254.     input_free_device(s3c_ts_dev);  
  255.     del_timer(&s3c_ts_timer);  
  256. }  
  257.     
  258. module_init(s3c_ts_init);  
  259. module_exit(s3c_ts_exit);  
  260. MODULE_LICENSE("GPL");  
  261. 测试

首先,使用menuconfig去掉linux内核中的触摸屏驱动,然后make uImage生成新的内核文件。使用新的不带触摸屏驱动的内核文件启动。

方法一:

安装触摸屏驱动insmod s3c_ts.ko,使用hexdump查看触摸屏设备上报的事件数据。

方法二:

使用Tslib进行测试。Tslib是一个开源的程序,能够为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。

(1)Tslib交叉编译安装

    步骤如下:

tar xzf tslib-1.4.tar.gz         //解压

cd tslib                    //进入到tslib目录

./autogen.sh                //执行autogen.sh

mkdir tmp                //创建tmp目录,保存编译产生的文件

echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache

./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp     /配置/

Make                    //编译

make install                //编译安装

按照上述步骤操作,编译安装好的Tslib工具全部保存到tmp文件夹下。注意在执行./autogen.sh命令时,可能回出现如下错误:

错误原因:没有安装automake工具。

解决方法:使用以下命令安装automake工具。

sudo apt-get install autoconf

sudo apt-get install automake

sudo apt-get install libtool

(2)移植Tslib

  • 将tmp目录下的Tslib工具拷贝到文件系统的根目录下;
  • 安装s3c_ts.ko, lcd.ko驱动程序;
  • 修改/etc/ts.conf文件的第1行内容(去掉#号和第一个空格);
  • 设置环境变量:

export TSLIB_TSDEVICE=/dev/event0

export TSLIB_CALIBFILE=/etc/pointercal

export TSLIB_CONFFILE=/etc/ts.conf

export TSLIB_PLUGINDIR=/lib/ts

export TSLIB_CONSOLEDEVICE=none

export TSLIB_FBDEVICE=/dev/fb0

  • 使用ts_calibrate命令进行屏幕校准;
  • 使用ts_test命令进行测试。

原文地址:https://www.cnblogs.com/beijiqie1104/p/11543619.html