Linux设备驱动开发基础---mini2440触摸屏驱动

一、触摸屏设备工作原理

1、电阻式触摸屏工作原理原理  

触摸屏按其技术原理可分为五类:矢量压力传感式、电阻式、电容式、红外线式、表面声波式,其中电阻式触摸屏在嵌入式系统中用的较多。电阻触摸屏是一块 4 层的透明的复合薄膜屏,最下面是玻璃或有机玻璃构成的基层,最上面是一层外表面经过硬化处理从而光滑防刮的塑料层,中间是两层金属导电层,分别在基层之上和塑料层内表面,在两导电层之间有许多细小的透明隔离点把它们隔开。当手指触摸屏幕时,两导电层在触摸点处接触。

电阻式触摸屏中最常用和普及的触摸屏是四项式触摸屏,其结构由X层和Y层组成,中间由微小的绝缘电阻隔开。当触摸屏没有压力时,X层和Y层处于断开状态。当有压力时,触摸屏X层和Y层导通。通过X层的探针可以侦测Y层接触点的电压,通过电压可以确定触摸点在Y层的位置。同样,通过Y层的探针可以侦测出X层接触点的电压,通过电压可以确定触摸点在X层的位置。这样,就可以得到触摸点在触摸屏上的位置(x,y)。X层和Y层的关系如下图所示:


二、S3C2440触摸屏接口

1、S3C2440触摸屏接口的工作模式

s3c2440芯片具有一个8 通道模拟输入的10 位CMOS ADC(模/数转换器),其转换模拟输入信号为10 位二进制数字编码,最大转换率为2.5MHz A/D转换器时钟下的 500KSPS。A/D 转换器支持片上采样-保持功能和掉电模式的操作。 

触摸屏接口可以控制/选择触摸屏 X、Y 方向的引脚(XP,XM,YP,YM)的变换。触摸屏接口包括触摸屏引脚控制逻辑和带中断发生逻辑的ADC 接口逻辑。 

s3c2440触摸屏接口有4中工作模式、在不同的工作模式下,触摸屏设备完成不通的功能。在某些情况下,几种工作模式需要相互配合,才能够完成一定的功能。

1. 普通转换模式 

普通转换模式(AUTO_PST = 0,XY_PST = 0)用来进行一般的 ADC转换,例如通过ADC测量电池电压等。此模式可以通过设置 ADCCON(ADC 控制寄存器)初始化并且通过读写ADCDAT0(ADC 数据寄存器 0)就能够完成。 

2. 独立的X/Y方向转换模式 

独立 X/Y轴坐标转换模式其实包含了 X 轴模式和 Y轴模式。为获得 X、Y坐标,需首先进行X 轴的坐标转换(AUTO_PST = 0,XY_PST = 1) ,X 轴的转换资料会写到ADCDAT0寄存器的XPDAT中,等待转换完成后,触摸屏控制器会产生 INT_ADC中断。然后,进行Y轴的坐标转换(AUTO_PST = 0,XY_PST = 2) ,Y轴的转换资料会写到 ADCDAT1 寄存器的 YPDAT 中,等待转换完成后,触摸屏控制器也会产生INT_ADC中断。 

3. 自动(顺序)X/Y方向转换模式 

自动(连续)X/Y位置转换模式(AUTO_PST = 1,XY_PST = 0)运行方式是触摸屏控制自动转换 X 位置和 Y 位置。触摸屏控制器在 ADCDAT0 的 XPDATA 位写入 X测定数据,在ADCDAT1的YPADATA位写入Y测定数据。自动(连续)位置转换后,触摸屏控制器产生INT_ADC中断。 

4. 等待中断模式 

当设置触摸屏接口控制器的ADCTSC寄存器为0xD3时,触摸屏就处于等待终端模式。这时触摸屏等待触摸信号的到来。当触摸信号到来时,触摸屏接口控制器通过INT_TC线产生中断信号,表示有触摸动作发生。当中断发生,触摸屏可以转换为其它两种状态来读取触摸点的位置(x,y)。这两种模式是独立的X/Y位置转换模式和自动X/Y位置转换模式。

触摸屏控制器产生中断信号(INT_TC)后,必须清除等待中断模式。(XY_PST 设置到无操作模式) 。

编程笔记 
1. A/D 转换的数据可以通过中断或查询方式访问。中断方式的总体转换时间为从 A/D 转换器开始到转换数据的读取,可能由于中断服务程序的返回时间和数据访问时间而延迟。查询方式是通过检查转换结束标志位的 ADCCON[15],可以确定读取 ADCDAT 寄存器的时间。 

2. 还提供了其它启动 A/D 转换的方法。在转换的读启动模式 ADCCON[1]设置为 1 后,A/D 转换启动同时读取数据。 

5.待机模式(Standby Mode) 

当ADCCON 寄存器的 STDBM位置1时,待机模式被激活。在这种模式下,A/D转换动作被禁止,ADCDAT0 的 XPDATA 位和 ADXDATA1 的 YPDAT 保留以前被转换的数据。 

2、s3c2440数模屏设备寄存器

(1)、 ADCCON(ADC CONTROL REGISTER),ADC控制寄存器,用于控制AD转换、是否使用分频、设置分频系数、读取AD转换器的状态等。

(2)、ADCTSC (ADC TOUCH SCREEN CONTROL REGISTER),ADC触摸屏控制寄存器。用于存储触摸屏的YMON、nYPON、XMON、nXPON等的状态。

(3)、ADCDLY (ADC START DELAY REGISTER),ADC延时寄存器,用于正常模式下和等待终端模式下的延时操作。

(4)、ADCDAT0 (ADC CONVERSION DATA REGISTER),ADC转换寄存器0,用于存储触摸屏的点击装态、工作模式、X坐标等。

(5)、ADCDAT1 (ADC CONVERSION DATA REGISTER),ADC数据寄存器1,用于存储触摸屏的点击装态、工作模式、Y坐标等。

使用ADCDAT0和ADCDAT1寄存器是,需要注意一下问题:

ADCDAT0和ADCDAT1寄存器的第15位,表示X和Y方向上检测到的触摸屏是否被按下。只有当ADCDAT0和ADCDAT1寄存器的第15位,即两个寄存器的UPDOWN都等于0时,才表示触摸屏被按下,或者有触笔点击触摸屏。

三、S3C2440触摸屏驱动的实现

1、在内核中添加触摸屏驱动程序

linux-src/drivers/input/touchscreen 目录下,增加一个s3c2410_ts.c 文件

#include <linux/serio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/io.h>

#include <plat/adc.h>
#include <plat/regs-adc.h>

#include <mach/regs-gpio.h>  

//#define CONFIG_TOUCHSCREEN_MINI2440_DEBUG   1

/*设备名称*/ 
#define DEVICE_NAME    "mini2440_TouchScreen" 

/*定义一个外部的信号量 ADC_LOCK,保证ADC资源在ADC驱动和触摸屏驱动中进行互斥访问*/ 
extern struct semaphore ADC_LOCK; 
 
/*做为一个标签,只有对触摸屏操作后才对X和Y 坐标进行转换*/ 
static int OwnADC = 0; 

#define INT_DOWN (0)
#define INT_UP (1 << 8)

/*把触摸屏设备转换为等待中断模式*/
#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
                         S3C2410_ADCTSC_YP_SEN | \
                         S3C2410_ADCTSC_XP_SEN | \
                         S3C2410_ADCTSC_XY_PST(3))    

/*将ADC设置为自动XY坐标转换模式*/
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
S3C2410_ADCTSC_YP_SEN | \
S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_AUTO_PST | \
S3C2410_ADCTSC_XY_PST(0))

/**
 * struct s3c2410ts - driver touchscreen state.
 * @client: The ADC client we registered with the core driver.
 * @dev: The device we are bound to.
 * @input: The input device we registered with the input subsystem.
 * @clk: The clock for the adc.
 * @io: Pointer to the IO base.
 * @xp: The accumulated X position data.
 * @yp: The accumulated Y position data.
 * @irq_tc: The interrupt number for pen up/down interrupt
 * @count: The number of samples collected.
 * @shift: The log2 of the maximum count to read in one go.
 */
struct s3c2410ts {
    struct input_dev *input;    /*定义一个输入设备来表示我们的触摸屏设备*/ 
struct clk *clk;              /*用于保存从平台时钟列表中获取的 ADC 时钟*/ 
    void __iomem *io;              /*定义了一个用来保存经过虚拟映射后的内存地址*/ 
unsigned long xp;              /*用于记录转换后的 X 坐标值和 Y 坐标值*/ 
unsigned long yp;
int count;                     /*用于计数对触摸屏压下或抬起时模拟输入转换的次数*/ 
int shift;                     /*1<<shift ADC转换次数*/
};

static struct s3c2410ts ts;

/**
 * s3c2410_ts_connect - configure gpio for s3c2410 systems
 *
 * Configure the GPIO for the S3C2410 system, where we have external FETs
 * connected to the device (later systems such as the S3C2440 integrate
 * these into the device).
*/
static inline void s3c2410_ts_initialize(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);


/* Initialise registers */
/*将 AD 转换预定标器值设为 255、AD 转换预定标器使能有效*/ 
writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF), ts.io+ S3C2410_ADCCON); 
/*延时值为0xffff,设置中断发出间隔,当触摸屏按下时,不断发出INT_TC中断,这个就是设置中断发出的间隔*/ 
writel(0xffff, ts.io + S3C2410_ADCDLY); 
/*将ADC触摸屏控制寄存器设置成等待中断模式,并且是按下中断*/ 
writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
}

/**
 * get_down - return the down state of the pen
 * @data0: The data read from ADCDAT0 register.
 * @data1: The data read from ADCDAT1 register.
 *
 * Return non-zero if both readings show that the pen is down.
 */
static inline bool get_down(unsigned long data0, unsigned long data1)
{
/*该状态保存在数据寄存器的第15位,所以与上 S3C2410_ADCDAT0_UPDOWN */
return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&!(data1 & S3C2410_ADCDAT0_UPDOWN));
}

static void touch_timer_fire(unsigned long data) 

    unsigned long data0; 
    unsigned long data1; 
    bool updown; 
 
    data0 = readl(ts.io + S3C2410_ADCDAT0); 
    data1 = readl(ts.io + S3C2410_ADCDAT1); 
    updown = get_down(data0, data1);
/*判断触摸屏是否被按下*/
if (updown) {
            //读取四次
  if (ts.count == (1 << ts.shift)) {
ts.xp >>= ts.shift;   //读取平均值
ts.yp >>= ts.shift;

#ifdef CONFIG_TOUCHSCREEN_MINI2440_DEBUG
//触摸屏调试信息,编译内核时选上此项后,点击触摸屏会在终端上打印出坐标信息
struct timeval tv;
do_gettimeofday(&tv);
printk(KERN_DEBUG "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
            printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
#endif
      //报告事件和数据 
input_report_abs(ts.input, ABS_X, ts.xp);
input_report_abs(ts.input, ABS_Y, ts.yp);

input_report_key(ts.input, BTN_TOUCH, 1);
//等待接收方受到数据后回复确认,用于同步
input_sync(ts.input);

ts.xp = 0;
ts.yp = 0;
ts.count = 0;
}

//设置触摸屏的模式为自动转换模式 
         writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts.io + S3C2410_ADCTSC); 
         //启动ADC转换 
         writel(readl(ts.io + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,ts.io + S3C2410_ADCCON);  
 } 
else {
 ts.count = 0;
         //报告按键事件,键值为 0(代表触摸屏对应的按键被释放)
 input_report_key(ts.input, BTN_TOUCH, 0);
 input_sync(ts.input);
         //将触摸屏重新设置为等待中断状态
              writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
if (OwnADC){ 
              OwnADC = 0; 
              up(&ADC_LOCK); 
       
 
 }
}

/*定义并初始化了一个定时器 touch_timer,定时器服务程序为 touch_timer_fire*/ 
static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);

/*触摸屏中断服务程序,对触摸屏按下或提笔时触发执行*/ 
static irqreturn_t stylus_updown(int irq, void *dev_id) 

    /*用于记录这一次 AD 转换后的值*/ 
    unsigned long data0; 
    unsigned long data1; 
 
    /*用于记录触摸屏操作状态是按下还是抬起*/ 
    bool updown;

/*ADC 资源可以获取,即上锁*/ 
    if (down_trylock(&ADC_LOCK) == 0){ 
        /*标识对触摸屏进行了操作*/ 
        OwnADC = 1; 
 
        /*读取这一次 AD 转换后的值,注意这次主要读的是状态*/ 
        data0 = readl(ts.io + S3C2410_ADCDAT0); 
        data1 = readl(ts.io + S3C2410_ADCDAT1); 


/*记录这一次对触摸屏是压下还是抬起*/ 
updown = get_down(data0, data1); 
 
/*判断触摸屏的操作状态*/ 
if (updown){ 
/*如果是按下状态,则调用 touch_timer_fire 函数来启动 ADC 转换*/ 
touch_timer_fire(0); 

        else{ 
            /*如果是抬起状态,就结束了这一次的操作,所以就释放 ADC 资源的占有*/ 
            OwnADC = 0; 
            up(&ADC_LOCK); 
       
    }


    return IRQ_HANDLED; 
}
 
/*ADC 中断服务程序,AD 转换完成后触发执行*/ 
static irqreturn_t stylus_action(int irq, void *dev_id) 

    /*用于记录这一次 AD 转换后的值*/ 
    unsigned long data0; 
    unsigned long data1; 
 
    if(OwnADC) 
    { 
        /*读取这一次 AD 转换后的值,注意这次主要读的是坐标*/ 
data0 = readl(ts.io + S3C2410_ADCDAT0); 
data1 = readl(ts.io + S3C2410_ADCDAT1); 
 
/*记录这一次通过 AD 转换后的 X 坐标值和 Y 坐标值,根据数据手册可知,X 和 Y
坐标转换数值分别保存在数据寄存器0和1的第0-9位,所以这里与上
S3C2410_ADCDAT0_XPDATA_MASK 就是取 0-9 位的值*/ 
ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; 
ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; 
 
/*计数这一次 AD 转换的次数*/ 
ts.count++; 
 
if (ts.count < (1<<ts.shift)){ 
/*如果转换的次数小于 4,则重新启动 ADC 转换*/ 
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts.io + S3C2410_ADCTSC); 
writel(readl(ts.io + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, ts.io + S3C2410_ADCCON); 



        else{ 
            /*如果已经转换了四次,那么重新设置定时器,定时一个时钟滴答,当一个时钟滴答到是又会判断是否按下,
            如果这时松开的话,那么这次按下就是不确定按下,也就是抖动  */
            mod_timer(&touch_timer, jiffies + 1); 
//设置触摸屏为等待中断模式,但是等待的是松开中断
            writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); 
        } 
    } 
 
    return IRQ_HANDLED; 


static int __init mini2440ts_init(void) 
 {  
     //struct s3c2410_ts_mach_info *info;                 /*触摸屏接口相关硬件配置信息结构体指针*/
     struct input_dev *input_dev;
int ret = -EINVAL; 


/* Initialise input stuff */
memset(&ts, 0, sizeof(struct s3c2410ts));


/*获取ADC时钟*/
ts.clk = clk_get(NULL, "adc"); 
if(!ts.clk){  
       printk(KERN_ERR "falied to find adc clock source\n"); 
  return -ENOENT; 
}
clk_enable(ts.clk);/*使能时钟,内核默认是禁止的*/   


ts.io = ioremap(S3C2410_PA_ADC, 0x20); 
if(ts.io == NULL) { 
  printk(KERN_ERR "failed to remap register block\n"); 
  ret = -EINVAL; 
  goto err_noclk;


/*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/ 
s3c2410_ts_initialize();


input_dev = input_allocate_device();
if (!input_dev) {
dev_err(input_dev, "Unable to allocate the input device !!\n");
ret = -ENOMEM;
goto err_iomap;
}

/*初始化输入设备*/
ts.input = input_dev;
/*evbit字段用于描述支持的事件,这里支持同步事件、按键事件、绝对坐标事件*/
ts.input->evbit[0] = BIT(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
/*keybit 字段用于描述按键的类型,这里表示触摸屏的点击*/
ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 

/*对于触摸屏来说,使用的是绝对坐标系统。这里设置该坐标系统中X和Y坐标的
*最小值和最大值(0-1023 范围)ABS_X 和 ABS_Y 就表示 X 坐标和 Y 坐标,
*ABS_PRESSURE 就表示触摸屏是按下还是抬起状态*/ 
input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.input, ABS_PRESSURE, 0, 1, 0, 0);


/*初始化ts的成员变量*/
ts.input->name = DEVICE_NAME;               /*设备名称*/ 
ts.input->id.bustype = BUS_HOST;            /*总线类型*/ 
ts.input->id.vendor = 0xDEAD;              /*经销商 ID 号*/
ts.input->id.product = 0xBEEF;             /*产品 ID 号*/ 
ts.input->id.version = 0x0001;             /*版本 ID 号*/ 

ts.shift = 2;
//注册两个中断处理例程,一个是AD完成中断处理stylus_action,一个是触摸屏按下中断处理stylus_updown


/*申请ADC中断,AD转换完成后触发。这里使用共享中断 IRQF_SHARED 是因为该中断号在ADC驱动中也使用了*/ 
ret = request_irq(IRQ_ADC, stylus_action, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DEVICE_NAME, ts.input); 
if(ret){ 
    printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret); 
    ret = -EINVAL; 
    goto err_iomap; 
}


/*申请触摸屏中断,对触摸屏按下或提笔时触发*/ 
ret = request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, DEVICE_NAME, ts.input); 
if(ret){ 
printk(KERN_ERR "IRQ%d error %d\n", IRQ_TC, ret); 
ret = -EINVAL; 
goto err_noirq; 


/* All went ok, so register to the input system */
ret = input_register_device(ts.input);
if (ret < 0) {
dev_err(input_dev, "failed to register input device\n");
ret = -EIO;
goto err_noirq;
}

return 0; 

   /*跳转处的错误处理*/
err_noclk: 
clk_disable(ts.clk); 
clk_put(ts.clk); 
err_iomap:
iounmap(ts.io); 
err_noirq:
free_irq(IRQ_ADC, ts.input); 


return ret; 
  }


 static void __exit mini2440ts_exit(void) 
 {

    /*屏蔽和释放中断*/ 
    disable_irq(IRQ_ADC); 
    disable_irq(IRQ_TC); 
    free_irq(IRQ_ADC, 1); 
    free_irq(IRQ_TC, 1); 
 
    /*释放虚拟地址映射空间*/ 
    iounmap(ts.io); 
 
    /*屏蔽和销毁时钟*/ 
    if(ts.clk){ 
        clk_disable(ts.clk); 
        clk_put(ts.clk);
   ts.clk = NULL; 
    }
 
/*将触摸屏设备从输入子系统中注销*/ 
input_unregister_device(ts.input); 
 
 
module_init(mini2440ts_init); 
module_exit(mini2440ts_exit); 

MODULE_AUTHOR("DreamCatcher");
MODULE_DESCRIPTION("MINI2440 Touchscreen driver");
MODULE_LICENSE("GPL v2");

触摸屏驱动的工作流程:

1. 用户按下触摸屏进入中断处理程序stylus_updown,这个函数处理如下
    (1)判断是否按下,如果是调用touch_timer_fire,如果不是那么说明现在松开了
    (2)touch_timer_fire处理如下:判断是否按下
        <1>确实按下了,那先判断count是否为4,如果为4那么说明ad转换完成了,进行x/y坐标的事件报告,并报告触摸屏事件。这个才是一次正确的按下

        <2>确实按下了,判断的count不是4,说明还没有完成4次AD转换,启动ad转换,转换完成进入中断处理程序stylus_action 
        <3>触摸屏这时松开了,那么只报告触摸屏事件,并设置触摸屏为等待按下中断的模式
 2.  stylus_action 函数处理过程,首先读取转换数据,然后判断count是否小于4
    (1)count小于4 那么再次启动转换
    (2)count不小于4,然后重新设置定时器,延时时间为一个时钟滴答,设置触摸屏为等待中断模式,等待松开中断
 3 . 在定时器事件到达之前松开触摸屏,会进入stylus_updown,这个函数判断触摸屏松开了,释放信号量不会报告任何事件
   如果定时器时间到执行touch_timer_fire,就会判断真正按下,而这时count为4,报告触摸屏事件,报告x/y坐标
 4. 这里的定时器是为了防止抖动而设置的,所以按下操作至少要保持一个时钟滴答加四次ad转换的时间。

打开linux-2.6.32.2/drivers/input/touchscreen/Makefile,定位到文件末尾,添加该源代码的目标模块,如下红色部分:

obj-$(CONFIG_TOUCHSCREEN_W90X900) +=w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_MINI2440) += s3c2410_ts.o

再打开linux-2.6.32.2/drivers/input/touchscreen/Kconfig,定位到14行附近,加入如下红色部分,这样就在内核配置中添加了mini2440的触摸屏驱动选项:

if INPUT_TOUCHSCREEN

config TOUCHSCREEN_MINI2440
tristate "Samsung S3C2440 touchscreeninput driver"
depends on MACH_MINI2440 && INPUT&& INPUT_TOUCHSCREEN && MINI2440_ADC
default y
help
Say Y here if you have the s3c2440 touchscreen.
If unsure, say N.
To compile this driver as a module, choose Mhere: the
module will be called s3c2410_ts.

config TOUCHSCREEN_MY2440_DEBUG
boolean "S3C2440 touchscreens input driverdebug messages"
depends on TOUCHSCREEN_MINI2440
help
Select this if you want debug messages

config TOUCHSCREEN_ADS7846
tristate "ADS7846/TSC2046 and ADS7843 based touchscreens"
depends on SPI_MASTER
depends on HWMON = n || HWMON
help

至此,我们就已经在内核中添加完了触摸屏驱动。

2、配置编译并启动内核

在命令行执行: make menuconfig ,然后依次选择如下子菜单,找到刚刚添加的触摸屏驱动选项:
Device Drivers --->
Input device support --->
[*] Touchscreens --->
按空格键选中 “S3C2440 touchscreens input driver debug messages (NEW)” 

3、测试

1、如果定义了程序中的测试宏,点击触摸屏将会显示对应的坐标值

2、用网上现有的测试程序进行测试

测试程序代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
struct sample{
char position[15];
int x;
int y;
};
struct sample sample_array[4]=
{
{"topleft",0,0},
{"topright",0,0},
{"bottonleft",0,0},
{"bottonright",0,0},
};
int X1,X2,Y1,Y2;
getsample(int fd,int position)

struct input_event ev[128];
int rb,sample_cnt,cntx=0,cnty=0;
rb=read(fd,ev,sizeof(struct input_event)*128);
if (rb < (int) sizeof(struct input_event)) {
perror("evtest: short read");
exit (1);
}
for (sample_cnt = 0;
sample_cnt< (int) (rb / sizeof(struct input_event));
sample_cnt++)
{
if (EV_ABS== ev[sample_cnt].type){
if( sample_cnt%20==0){
printf("%ld.%06ld ",
ev[sample_cnt].time.tv_sec,
ev[sample_cnt].time.tv_usec);
printf("type %d code %d value %d\n",
ev[sample_cnt].type,
ev[sample_cnt].code, ev[sample_cnt].value);
}
if(ABS_X==ev[sample_cnt].code){
sample_array[position].x+= ev[sample_cnt].value;
cntx++;
}
if(ABS_Y==ev[sample_cnt].code){
sample_array[position].y+= ev[sample_cnt].value;
cnty++;
}
}
}
sample_array[position].x/=cntx;
sample_array[position].y/=cnty;
}
int ts_coordinate(int value,int axes)
{
int tempX,ret;
if(ABS_X==axes)ret=240-(240*(value-X2)/(X1-X2));
if(ABS_Y==axes)ret=320-(320*(value-Y2)/(Y1-Y2));
return ret;


int main(int argc, char **argv)
{
struct pollfd pfd;
int n,fd,i=0;
if ((fd = open("/dev/input/event0",O_RDONLY) )< 0) {
printf("open error! \n"); 
exit(1);
}

for(i=0;i<4;i++){
printf("Please touch the %s for 6 second ! \n",sample_array[i].position);
sleep(6);
printf("Time is up Please release\n");
getsample(fd,i);
sleep(1);
}
for(i=0;i<4;i++){
printf("%12s x=%4d,y=%4d\n",sample_array[i].position,
sample_array[i].x,
sample_array[i].y);
}
X1=(sample_array[0].x+ sample_array[2].x )/2; 
X2=(sample_array[1].x+ sample_array[3].x )/2; 
Y1=(sample_array[0].y+ sample_array[1].y )/2; 
Y2=(sample_array[2].y+ sample_array[3].y )/2; 
printf("Coordinate complete,test it now\n"); 
for(i=0;i<4;i++){
printf("Please touch the %s for 6 second ! \n",sample_array[i].position);
sleep(6);
printf("Time is up Please release\n");
getsample(fd,i);
sample_array[i].x=ts_coordinate(sample_array[i].x,ABS_X);
sample_array[i].y=ts_coordinate(sample_array[i].y,ABS_Y);
sleep(1);
}
printf("the data after coordinate \n"); 
for(i=0;i<4;i++){
printf("%12s x=%4d,y=%4d\n",sample_array[i].position,
sample_array[i].x,
sample_array[i].y);
}
close(fd);
exit(0);
}

查看设备信息:

root@MINI2440:/# cat proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 89 i2c
 90 mtd
可以得知输入类设备的主设备号为13,又按如下操作:
root@MINI2440:/# cat proc/bus/input/devices
I: Bus=0019 Vendor=dead Product=beef Version=0001
N: Name="mini2440_TouchScreen"
P: Phys=
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=mouse0 event0 
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=1000003
根据这些信息我们建立好设备节点:

root@MINI2440:/#mkdir /dev/input
root@MINI2440:/#mknod /dev/input/event0 c 13 64
root@MINI2440:/#echo 8 > /proc/sys/kernel/printk
root@MINI2440:/#cat /dev/input/event0(点击触摸屏将会看到一些乱码)

注:可以将这些信息加入etc/profile系统启动时自动创建

clx@Think:/rootfs/etc$ sudo gedit profile

#建立触摸屏设备节点
mkdir /dev/input
mknod /dev/input/event0 c 13 64
echo 8 > /proc/sys/kernel/printk

运行测试程序将看到如下信息:

root@MINI2440:/opt# ./ts_test
Please touch the topleft for 6 second ! 
Time is up Please release
123.800012 type 3 code 0 value 90
Please touch the topright for 6 second ! 
Time is up Please release
130.590014 type 3 code 1 value 903
130.725012 type 3 code 0 value 922
130.980012 type 3 code 0 value 918
Please touch the bottonleft for 6 second ! 


注:此驱动tslib触摸屏矫正无法使用,不知道哪里有问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值