zoukankan      html  css  js  c++  java
  • 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命令进行测试。

  • 相关阅读:
    python调用c++动态库报错
    Python自动化测试常用库整理
    Linux kswapd0 进程CPU占用过高快速处理方法
    Linux安装jdk8及环境变量配置
    免费的网站安全检查和恶意软件扫描程序
    【Java】对象的创建、初始化和使用
    【Java】数组的创建、遍历和复制
    【Java】Java 语言中流程控制语句(分支语句、循环语句、转移语句)的一些新特性
    【Java】Java 语言中操作符的一些新特性
    【Java】常量与变量(局部变量、成员变量)
  • 原文地址:https://www.cnblogs.com/beijiqie1104/p/11543619.html
Copyright © 2011-2022 走看看