八 s3c2440 linux 触摸屏 驱动代分析

Posted bigPillow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八 s3c2440 linux 触摸屏 驱动代分析相关的知识,希望对你有一定的参考价值。

LCD触摸屏的移植

原文地址:

http://blog.csdn.net/woshidahuaidan2011/article/details/52054949


1、对触摸屏添加设备信息

触摸屏的设备信息的添加跟lcd很像,这里就仿照lcd的platform设备信息给触摸屏添加设备信息。可以在Touchscreen-s3c2410.h(include\\linux\\platform_data)文件中看到,其定义了有关触摸屏信息的结构体s3c2410_ts_mach_info,因此我们只需要填充该结构体就可以。

struct s3c2410_ts_mach_info原型为:

struct s3c2410_ts_mach_info

       int             delay;   //延时时间

       int             presc;  //预分频,adc时钟=PCLK/(presc+1)adc时钟最大为2.5M

       int             oversampling_shift;  //采样次数,2的2次方为4

   void    (*cfg_gpio)(struct platform_device *dev);

;

 

于是在要在mach-smdk2440.c文件中定义中该结构体并且加入设备平台设备就好,在mach-smdk2440.c定义:

#include<linux/platform_data/touchscreen-s3c2410.h> /*ts add*/

(注意这里已经改了,在plat/ts.h目录不存在了)

 

static struct s3c2410_ts_mach_info s3c _touch_data= //跟默认的一样

     .delay=1000,   //延时时间

     .presc=49,  //预分频,

     .oversampling_shift=2,  //采样次数,22次方为4

  

;

然后加入设备平台链表,在

static struct platform_device*smdk2440_devices[] __initdata =

        &s3c_device_ohci,

        &s3c_device_lcd,

        &s3c_device_wdt,

        &s3c_device_i2c0,

        &s3c_device_iis,

        &smdk2440_device_dm9k,

        &s3c_device_rtc,

       &s3c_device_adc, //adc 触摸屏需要

       &s3c_device_ts,   //新加

;

注意:假如没有添加数模转换&s3c_device_adc,则内核会无法启动。

最后一步将设备信息添加到平台设备中,在smdk2440_machine_init函数中写入:

static void  __init smdk2440_machine_init(void)

       s3c24xx_fb_set_platdata(&smdk2440_fb_info);

        s3c24xx_ts_set_platdata(&s3c_touch_data);   //新加

        s3c_i2c0_set_platdata(NULL);

 

        platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));

        smdk_machine_init();

 

至于s3c24xx_ts_set_platdata函数,其定义在Devs.c(arch\\arm\\plat-samsung)文件中,作用跟lcd对应的函数一样就是把s3c_touch_data添加到平台设备s3c_device_ts中。其函数原型为:

 void __inits3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)

      s3c_set_platdata(hard_s3c2410ts_info,

                     sizeof(struct s3c2410_ts_mach_info),&s3c_device_ts);

至于平台设备s3c_device_ts,这个定义在/arch/arm/plat-samsung/devs.c有:

struct platform_device s3c_device_ts =

      .name            = "s3c2410-ts",    //

      .id          = -1,

      .dev.parent    = &s3c_device_adc.dev,   //调用adc的平台设备的dev

      .num_resources   = ARRAY_SIZE(s3c_ts_resource),

      .resource       = s3c_ts_resource,

;

 

2、对触摸屏驱动的测试

假如你够细心的话,那么在内核启动的时候会看到:

samsung-ts s3c2440-ts: driverattached, registering input device

input: S3C24XX TouchScreen as/devices/virtual/input/input0

同时也会看到:

input: gpio-keys as/devices/platform/gpio-keys/input/input1

可以看到作为输入设备,触摸屏被设置为input0,之前被设置的key变成了input0。

 

因此我们可以写测试代码(只需要修改前面所写的按键的测试的代码就可以):

 

 

/*ts.c */

#include<stdint.h>

#include<string.h>

#include<fcntl.h>

#include<unistd.h>

#include<stdio.h>

#include<linux/input.h>

 

int main(void)

  int fd;

  int flag=0;

 structinput_event ev_ts;

  fd=open("/dev/input/event0", O_RDWR);

  if(fd < 0)

   

    perror("Error:open device ts");

     close(fd);

     return -1;

   

  while(1)

   

 

      read(fd,&ev_ts,sizeof(structinput_event));

 

    if(ev_ts.type) //下面的三句的意思是当连续读到的ev_ts.type0,那么只上报一次0

       flag=ev_ts.type;

    if(flag!=0) 

printf("type:%d,code:%d,value:%d,sec:%d,nsec:%d\\n",ev_ts.type,ev_ts.code,ev_ts.value,ev_ts.time.tv_sec,ev_ts.time.tv_usec);

 

      flag=ev_ts.type;

 

   

 close(fd);

 return 0;

 

对应的Makefile 文件为:

CC=arm-linux-2440-gcc

ts:ts.c

        $CC -o $@  $<

        cp ts /work/root/work/  #这里是我的跟文件系统的路径

clean:

        rm -rf ts

 

测试结果如下,下面给出一个触笔从按下到抬起过程测试所得到的数据:

 

type:3,code:0,value:660,sec:3138,nsec:430227

type:3,code:1,value:473,sec:3138,nsec:430227

type:1,code:330,value:1,sec:3138,nsec:430227

type:0,code:0,value:0,sec:3138,nsec:430227

type:3,code:0,value:656,sec:3138,nsec:435283

type:0,code:0,value:0,sec:3138,nsec:435283

type:3,code:0,value:668,sec:3138,nsec:440267

type:3,code:1,value:476,sec:3138,nsec:440267

type:0,code:0,value:0,sec:3138,nsec:440267

type:1,code:330,value:0,sec:3138,nsec:450234

type:0,code:0,value:0,sec:3138,nsec:45023

type类型为3表示本次上报的是绝对坐标类型,可以看到本次我随机按下的坐标x=660 ,y=473

随后上传的类型为1表示上传的是按键的类型,通过编码 code:330可以判断该按键类型为触摸屏被按下。

然后上传了一次上报事件结束标志,其type类型为0

此时一次完整的触摸屏按下的动作上传完毕。

但是通过返回的数字可以看到有上传了一次X的坐标信息和同步信息:

type:3,code:0,value:656,sec:3138,nsec:435283

type:0,code:0,value:0,sec:3138,nsec:435283

这是由于在上传同步的信息的时候,实际上传的是:

static inlinevoid input_sync(struct input_dev *dev)

      input_event(dev, EV_SYN, SYN_REPORT,0);

可以看到实习上传了编码为SYN_REPORT的信号,该信号的作用在Documentation\\input\\ event-codes.txt有介绍:

当多个输入数据在同一时间发生变化时,SYN_REPORT用于把这些数据进行打包和包同步。例如,一次鼠标的移动可以上报REL_X和REL_Y两个数值,然后上报出同步信号,在下次动作的时候再次发出REL_X和REL_Y两个数值和同步信号。

此外,这个信号比较特殊,每当系统检测到x或y的其中一个坐标或者x、y两个坐标都有变化的时候,在发射完x或y或(x.y)坐标之后都会产生SYN_REPORT信号,同时在触笔接触或者离开屏幕的时候也会发射出SYN_REPORT信号其值为0。用图示意就是:

检测到触笔按下:

ABS_X=坐标值

ABS_Y=坐标值

BTN_TOUCH=1

SYN_REPORT=0

检测到X坐标有变化Y坐标无变化:

ABS_X=新的坐标值

SYN_REPORT=0

检测到X坐标Y坐标都变化:

ABS_X=新的坐标值

ABS_Y=新的坐标值

SYN_REPORT=0

。。。。。。。

检测到触笔离开:

BTN_TOUCH=0

SYN_REPORT=0

  所以,最后上传的

type:1,code:330,value:0,sec:3138,nsec:450234

type:0,code:0,value:0,sec:3138,nsec:45023

3、对触摸屏驱动分析

触摸屏的驱动代码定义在S3c2410_ts.c (drivers\\input\\touchscreen)  文件中,接下来仍然从probe开始介绍:

static ints3c2410ts_probe(struct platform_device *pdev)

      struct s3c2410_ts_mach_info*info;

      struct device *dev = &pdev->dev; //这里得到的是平台设备的device成员

      struct input_dev *input_dev;//这里跟之前的按键的驱动一样,输入设备。

      struct resource *res;

      int ret = -EINVAL;

 

      /* Initialise input stuff */

      memset(&ts, 0, sizeof(structs3c2410ts));

/*********************************************************************************

 这里是将ts初始化为0。对于tsstructs3c2410ts的实例。我们之前了解到,对于一个输入设备,我们是用struct input_dev来描述一个输入设备;同样的道理,对于一个触摸屏设备,内核用struct s3c2410ts来描述它。下面来看一下structs3c2410ts结构体。

struct s3c2410ts

      structs3c_adc_client *client;注册一个adc设备的客户记录

/*********************************************************************************

struct s3c_adc_client

      structplatform_device       *pdev;

      structlist_head    pend; //客户请求列表

      wait_queue_head_t    *wait; //等待队列,用来睡眠

 

      unsigned int         nr_samples; //记录需要采样的个数

      int                 result;

      unsigned char             is_ts;   //是否支持触摸屏

      unsigned char             channel; //通道数

 

      void       (*select_cb)(struct s3c_adc_client *c,unsigned selected);触摸屏选择回调函数

      void       (*convert_cb)(struct s3c_adc_client*c,  触摸屏转换回调函数

                          unsigned val1, unsigned val2,

                          unsigned *samples_left);

;

***********************************************************************************/

 

      struct device*dev;  触摸屏对应的设备

      structinput_dev *input;  指向要注册的输入子系统

      struct clk*clock;  ADC的时钟

      void __iomem*io;  对应io引脚的基地址

      unsigned longxp;   X的坐标

      unsigned longyp;   Y的坐标

      int irq_tc;          按下触摸屏和抬笔对应的中断号

      int count;          每次采样的adc的个数

      int shift;           记录每次采样的adc的最大个数  

      int features;        标记着ADC对应引脚用在触摸屏模式时所拥有的特性

;

***********************************************************************************/

      ts.dev = dev; 设置触摸屏的设备为定义的平台所定义设备

 

      info =dev_get_platdata(&pdev->dev);得到平台设备定义的s3c2410_ts_mach_info

      if (!info)

             dev_err(dev, "no platformdata, cannot attach\\n");

             return -EINVAL;

     

 

      dev_dbg(dev, "initialisingtouchscreen\\n");

 

      ts.clock = clk_get(dev, "adc") ;得到内核所定义的时钟

      if (IS_ERR(ts.clock))

             dev_err(dev, "cannot get adcclock source\\n");

             return -ENOENT;

     

 

      clk_enable(ts.clock);使能时钟,上面所获得的时钟有效

      dev_dbg(dev, "got and enabledclocks\\n");

 

      ts.irq_tc = ret = platform_get_irq(pdev,0);得到中断号

      if (ret < 0)

             dev_err(dev, "no resource forinterrupt\\n");

             goto err_clk;

     

 

      res = platform_get_resource(pdev,IORESOURCE_MEM, 0);//得到内存资源

      if (!res)

             dev_err(dev, "no resource forregisters\\n");

             ret = -ENOENT;

             goto err_clk;

     

 

      ts.io = ioremap(res->start,resource_size(res));

//将物理地址转化为虚拟地址,并将虚拟地址的首地址赋值

      if (ts.io == NULL)

             dev_err(dev, "cannot mapregisters\\n");

             ret = -ENOMEM;

             goto err_clk;

     

 

      /* inititalise the gpio */

      if (info->cfg_gpio)//假如有定义cfg_gpio,就通过container_of函数获得platform_device

             info->cfg_gpio(to_platform_device(ts.dev));

     

ts.client =s3c_adc_register(pdev, s3c24xx_ts_select,

                                s3c24xx_ts_conversion, 1); //最后的参数数字1表示支持触摸屏

/*********************************************************************************

s3c_adc_register用来注册一个ADC设备,主要的工作就是:

client->is_ts = 1;

client->select_cb =s3c24xx_ts_select;

client->convert_cb =s3c24xx_ts_conversion;

其参数比较特殊,看一下这些参数的含义:

对于s3c24xx_ts_select,这是选择回调函数,看一下定义。

static void s3c24xx_ts_select(structs3c_adc_client *client, unsigned select)

       if(select)

              writel(S3C2410_ADCTSC_PULL_UP_DISABLE| AUTOPST,

                     ts.io + S3C2410_ADCTSC);

       else

              mod_timer(&touch_timer,jiffies+1); //介绍完probe函数后,将介绍定时器中断函数。

              writel(WAIT4INT| INT_UP, ts.io + S3C2410_ADCTSC);

      

这里假如select=1,那么就写入寄存器ADCTSC1101 1100,也就是中自动转换模式

假如select=0,就设定定时器中断的中断时间.然后写入寄存器ADCTSC1 1101 0011把模式变为等待中断模式,并设置触笔抬起触发中断

之前我们有介绍过定时器的使用步骤如下;

定义一个定时器timer_list mytimer

初始化定时器并赋值成员 setup_timer(mytimer, timer_func, data);//datatimer_func形参

增加定时器add_timer(mytimer)

该修改定时器的定时时间mod_timer(mytimer,expire)

取消定时器,有两个可选择

del_timer(mytimer)  直接删除定时器

del_timer_sync(mytimer) 等待本次定时器处理完毕再取消(不适用中断上下文)

不过在S3c2410_ts.c(drivers\\input\\touchscreen)     文件中并没有看到有关定时器的定义,但是有一个宏:

static DEFINE_TIMER(touch_timer,touch_timer_fire, 0, 0);此时定时器计时通过这个宏来定义并初始化的。

事实上,初始化定时器内核留下了很多接口:

init_timer(struct timer_list* timer);
TIMER_INITIALIZER(_function, _expires, _data);
DEFINE_TIMER(_name, _function, _expires, _data);  //
定义并初始化。

 

对于s3c24xx_ts_conversion,本来是比较简单,就是读取一次采样值,并设置采样标记数+1

*******************************************************************************/

 

      if (IS_ERR(ts.client))

             dev_err(dev, "failed toregister adc client\\n");

             ret = PTR_ERR(ts.client);

             goto err_iomap;

     

 

      /* Initialise registers */

      if ((info->delay & 0xffff) > 0) //设置延时时间

             writel(info->delay & 0xffff,ts.io + S3C2410_ADCDLY);

 

      writel(WAIT4INT | INT_DOWN, ts.io +S3C2410_ADCTSC);

//写入寄存器ADCTSC01101 0011把模式变为等待中断模式,并设置触笔按下时触发中断

 

      input_dev = input_allocate_device(); //跟按键一样,申请一个输入设备。

      if (!input_dev)

             dev_err(dev, "Unable toallocate the input device !!\\n");

             ret = -ENOMEM;

             goto err_iomap;

     

 

      ts.input = input_dev;

      ts.input->evbit[0] = BIT_MASK(EV_KEY) |BIT_MASK(EV_ABS); //支持按键和绝坐标事件

      ts.input->keybit[BIT_WORD(BTN_TOUCH)] =BIT_MASK(BTN_TOUCH);

//支持触摸屏事件,更多解释可以看Documentationinputevent-codes.txt文档,或者查看博客:

http://blog.csdn.net/droidphone/article/details/8432055

      input_set_abs_params(ts.input, ABS_X, 0,0x3FF, 0, 0);

      input_set_abs_params(ts.input, ABS_Y, 0,0x3FF, 0, 0);

/*********************************************************************************

这里是设置有关坐标的一些参数:

void input_set_abs_params(struct input_dev *dev, unsignedint axis,

                      int min, int max, int fuzz,intflat)

unsigned int axis,表示坐标轴,如: ABS_X ABS_Y ABS_Z等等

int min, int max 表示对应坐标轴的最大值和最小值。

int fuzz 表示滤波器用来消除噪声的制定毛刺值

int flat 对于像joydev这样的驱动,flat可看做阈值,当小于该值时,上报的都是0

********************************************************************************/

      ts.input->name = "S3C24XXTouchScreen"; // 执行cat /proc/bus/input/devices打印的信息

      ts.input->id.bustype = BUS_HOST;

      ts.input->id.vendor = 0xDEAD;

      ts.input->id.product = 0xBEEF;

      ts.input->id.version = 0x0102;

 

      ts.shift = info->oversampling_shift;//每次采样的个数,其个数等于=2^ oversampling_shift

      ts.features =platform_get_device_id(pdev)->driver_data;

/*********************************************************************************

得到数值0 .其定义为S3c2410_ts.c(drivers\\input\\touchscreen)

staticstruct platform_device_id s3cts_driver_ids[] =

      "s3c2410-ts", 0 ,

      "s3c2440-ts", 0 ,

      "s3c64xx-ts", FEAT_PEN_IRQ ,

     

;********************************************************************************/

 

      ret = request_irq(ts.irq_tc, stylus_irq,0, //申请中断,中断函数为stylus_irq

                      "s3c2410_ts_pen", ts.input);

      if (ret)

             dev_err(dev, "cannot get TCinterrupt\\n");

             goto err_inputdev;

     

 

      dev_info(dev, "driver attached,registering input device\\n");

 

      /* All went ok, so register to the inputsystem */

      ret = input_register_device(ts.input);//注册一个输入设备。

      if (ret < 0)

             dev_err(dev, "failed toregister input device\\n");

             ret = -EIO;

             goto err_tcirq;

     

 

      return 0;

 

 err_tcirq:

      free_irq(ts.irq_tc, ts.input);

 err_inputdev:

      input_free_device(ts.input);

 err_iomap:

      iounmap(ts.io);

 err_clk:

      del_timer_sync(&touch_timer);

      clk_put(ts.clock);

      return ret;

前面提到s3c24xx_ts_select函数的时候,提到,一个定时器中断函数,接下来看一下该函数:

static voidtouch_timer_fire(unsigned long data)

      unsigned long data0;

      unsigned long data1;

      bool down;

      data0 = readl(ts.io + S3C2410_ADCDAT0);

      data1 = readl(ts.io + S3C2410_ADCDAT1);

      down = get_down(data0,data1);//检测是否触笔按下。假如按下返回true,否则返回fause

 

      if (down) //假如有触笔按下

             if (ts.count == (1 <<ts.shift)) //假如到达采样个数预定值,这里设定是2^2=4

                    ts.xp >>= ts.shift; //这是在s3c24xx_ts_conversion累加的,这里求均值

                    ts.yp >>= ts.shift;

                    dev_dbg(ts.dev, "%s:X=%lu, Y=%lu, count=%d\\n",

                           __func__, ts.xp,ts.yp, ts.count);

                    input_report_abs(ts.input,ABS_X, ts.xp);//上报x坐标

                    input_report_abs(ts.input,ABS_Y, ts.yp); //上报y坐标

 

                    input_report_key(ts.input,BTN_TOUCH, 1);//上报触摸屏被按下事件

                    input_sync(ts.input);//上报完毕

                    ts.xp = 0;//清0

                    ts.yp = 0;

                    ts.count = 0;

            

 

             s3c_adc_start(ts.client, 0, 1<< ts.shift);

/*********************************************************************************

假如有触摸屏被按下但是还没有达到采样的次数,或者没有开启adc转换,

;********************************************************************************/

 

      else //假如触笔抬起

             ts.xp = 0;

             ts.yp = 0;

             ts.count = 0;

 

             input_report_key(ts.input,BTN_TOUCH, 0);//上报抬起触笔

             input_sync(ts.input);

 

             writel(WAIT4INT | INT_DOWN, ts.io +S3C2410_ADCTSC);//进入等待中断模式

     

家下来看一下中断服务函数,也就是当触笔按下屏幕的时候,会进入此中断函数中:

 

staticirqreturn_t stylus_irq(int irq, void *dev_id)

      unsigned long data0;

      unsigned long data1;

      bool down;

 

      data0 = readl(ts.io + S3C2410_ADCDAT0);//读取ADCDAT0的值

      data1 = readl(ts.io + S3C2410_ADCDAT1);

 

      down = get_down(data0, data1);//是否屏幕被按下

 

      /* TODO we should never get an interruptwith down set while

       *the timer is running, but maybe we ought to verify that the

       *timer isn't running anyways. */

 

      if (down)//假如被按下则启动adc

             s3c_adc_start(ts.client, 0, 1<< ts.shift);

      else

             dev_dbg(ts.dev, "%s:count=%d\\n", __func__, ts.count);

 

      if (ts.features & FEAT_PEN_IRQ) // 对于s3c64xx-ts,if才可判定成功

             /* Clear pen down/up interrupt */

             writel(0x0, ts.io +S3C64XX_ADCCLRINTPNDNUP);

     

 

      return IRQ_HANDLED;

有关触摸屏的代码比较简单,这里就介绍到这里,现在总结一下其工作的流程:

首先在驱动初始化的时候回调用probe函数,在该函数中

调用ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,    s3c24xx_ts_conversion, 1);注册一个adc设备;然后调用input_dev =input_allocate_device();申请一个输入设备,并且调用ret = input_register_device(ts.input);注册成为一个输入设备;再此之间又调用ret=request_irq(ts.irq_tc, stylus_irq, 0,

"s3c2410_ts_pen",ts.input);函数注册一个中断,并且有函数writel(WAIT4INT| INT_DOWN, ts.io + S3C2410_ADCTSC);设置寄存器,使其处于等待中断模式。等到中断到来的时候(触摸屏被按下的时候),则调用中断函数stylus_irq,在该中断函数中,则调用s3c_adc_start(ts.client, 0, 1 << ts.shift);开始进行adc转换;函数s3c_adc_start调用s3c_adc_start函数,s3c_adc_start函数则调用s3c_adc_select和s3c_adc_convert函数;s3c_adc_select就会调用s3c24xx_ts_select函数用来将模式转换为自动转换模式,然后s3c_adc_convert会读取转换的数字。于此同时,在adc每次进入中断adc中断的时候,s3c_adc_irq会通过判断client->nr_samples的数值(即触摸屏需要采样的次数),来判定是否通过s3c24xx_ts_select函数将自动转换模式转化为等待中断模式。

一般情况下,触摸屏将处于等待中断模式下,一旦触摸屏被按下,则触发stylus_irq中断函数,该中断函数将调用s3c_adc_start函数,进而调用s3c_adc_try函数,继而调用s3c_adc_select函数,最后调用s3c24xx_ts_select(client,1),s3c24xx_ts_select函数将模式设置为自动转化模式。此时,cpu则开始转化ADC,等到转化完毕,则会进入ADC中断函数s3c_adc_irq中,在该函数中,则读取转化后AD的数值,并设置client->nr_samples--;来判断是否达到每次取值求平均的个数,然后调用s3c24xx_ts_conversion,将每次读取的AD值进行累加(以便后面计算平均值),假如没有到达需要每次取值求平均的个数,则重新调用s3c24xx_ts_select(client,1),仍设置为自动转化模式,然后等到AD转换完毕,则重新进去AD中断函数s3c_adc_irq就这么周而复始,等到达需要每次取值求平均的个数s3c_adc_irq则会调用(client->select_cb)(client,0);和s3c_adc_try(adc)函数,(client->select_cb)(client, 0)将会把模式设置为等待中断模式并社会下次触发是抬笔触发,且设置定时器为下一个jiffies时间,在定时器服务函数touch_timer_fire里,在touch_timer_fire函数里,假如判定触笔按下,在会上报坐标值,假如判定触笔抬起,则会上报触笔抬起信号并把下次触发中断设置为触笔按下触发中断。就这样周而复始的轮训这工作。

 

4、安装tslib校正触摸屏

本次安装的版本是网络上最常见的tslib1.4.

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

首先将下载来的tslib1.4拷贝到linux系统下,解压:

也可以使用git下载:

git clonehttps://github.com/kergoth/tslib

(假如没有安装git先安装:sudo apt-get install get

下载完毕后解压:

$tar -xf tslib-1.4.tar.gz

进入talib加压后的目录:

执行:

./autogen.sh

然后执行配置命令:

./configure  --host=arm-inux\\

ac_cv_func_malloc_0_nonnull=yes  --cache-file=arm-linux-2440.cache  --prefix=/work/tools/tslib

其中--prefix是指定安装路径,把tslib安装到/work/tools/tslib目录

  --host=arm-inux的具体是交叉编译器的名字(假如没有的话建立软连接就可以)

完毕后,执行:

make

makeinstall

安装完成后进入安装的tslib目录会生成

可以看到在lib里面就是生成的一些库文件,我们把lib/生成的*so*拷贝到根文件系统目录下:

在tslab目录下执行:

cp ./lib/*so* /work/root/lib/

cp ./l     ib/ts  /work/root/lib/  -r

tslib/bin文件下所有文件复制到根文件系统的/bin目录下:

cp bin/* /work/root/bin/

/tslib/etc下的ts.conf文件复制到根文件系统的/etc下

cp etc/ts.conf  /work/root/etc/

然后修改刚拷贝的ts.conf文件:

去掉# module_raw input前面的注释以及空格(注意修改完是前面是没有空格的)

 

 

然后设置一下环境变量,可以在根文件系统的etc/profile (假如没有,新建文件即可)加入:

进入根文件系统目录,执行:

sudovi etc/profile 

 写入下列语句:

export TSLIB_TSDEVICE=/dev/input/event0

export TSLIB_CONFFILE=/etc/ts.conf

export TSLIB_PLUGINDIR=/lib/ts

export TSLIB_CALIBFILE=/tmp/pointercal

export TSLIB_CONSOLEDEVICE=none

export TSLIB_FBDEVICE=/dev/fb0

TSLIB_TSDEVICE——触摸屏设备文件,因人而异;

TSLIB_CONFFILE——配置文件名,就是前面复制并修改的ts.conf文件;

TSLIB_PLUGINDIR——插件目录

TSLIB_CALIBFILE——执行ts_calibrate会得到一个校准文件,指定的目录就是这个校正文件存储的目录,当需要判断触屏输入的位置时候,后调用本文件。

TSLIB_CONSOLEDEVICE——控制台设备文件名;

TSLIB_FBDEVICE——LCD设备节点,也要根据自己开发板的情况填写。

一切就绪就可以让开发板挂载上文件系统了,然后就可以校验:

输入:ts_calibrate

提示:

xres = 480, yres= 272

selecteddevice is not atouchscreen I understand

这是因为在tslib/plugins/input-raw.c的文件中有:

static intcheck_fd(struct tslib_input *i)

    

    structtsdev *ts =i->module.dev;

    intversion;

    u_int32_tbit;

    u_int64_tabsbit;

 

    if (!((ioctl(ts->fd,EVIOCGVERSION, &version) >= 0) &&

        (version== EV_VERSION) &&

        (ioctl(ts->fd,EVIOCGBIT(0, sizeof(bit) *8), &bit) >= 0) &&

        (bit& (1 << EV_ABS)) &&

        (ioctl(ts->fd,EVIOCGBIT(EV_ABS,sizeof(absbit) * 8), &absbit) >= 0) &&

        (absbit& (1 << ABS_X)) &&

        (absbit& (1 << ABS_Y)) &&(absbit & (1 << ABS_PRESSURE))))

        fprintf(stderr,"selecteddevice is not a touchscreen I understand ");

        return-1;

    

 

    if(bit & (1 <<EV_SYN))

        i->using_syn=1;

 

    return0;

这个函数,主要是个if语句。这里if中的判定的这八个条件只要有一个不满足(有取反符号)就会打印:

"selected device is not a touchscreen Iunderstand "

先看这八个条件:

(ioctl(ts->fd, EVIOCGVERSION, &version) >=0)  //读取驱动版本号

(version == EV_VERSION)                      //比较驱动版本号

 (ioctl(ts->fd, EVIOCGBIT(0, sizeof(bit) *8),&bit) >= 0)  //获取事件

 (bit & (1 << EV_ABS))                            //比较EV_ABS事件

 (ioctl(ts->fd,EVIOCGBIT(EV_ABS,sizeof(absbit) * 8), &absbit) >= 0)   //获取EV_ABS事件相关位

   (absbit & (1 <<ABS_X))                                     //比较ABS_X

  (absbit & (1 << ABS_Y))                                     //比较ABS_Y

(absbit & (1 << ABS_PRESSURE)))                             //比较ABS_PRESSURE

首先这里的驱动版本号,就是安装交叉编译工具链的时候用的内核版本是否跟现在内核版本是否相同,由于很多人下载别人的交叉编译工具链,所以这里的版本号很多时候不相同的。

我的交叉编译工具链的版本号在:

crosstool/arm-jason-linux-gnueabi/sysroot/usr/include/linux/input.h

我的内核的触摸屏驱动版本号在

/include/uapi/linux/input.h

 

不一致的话要修改一致即可

还有一个就是在linux3.14中并没有上报ABS_PRESSURE事件,这里需要添加到内核使之可以支持ABS_PRESSURE(压力值)事件。

这要修改三个地方:在drivers/input/touchscreen/s3c2410_ts.c文件中:

touch_timer_fire函数中:

  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_report_abs(ts.input, ABS_PRESSURE,  1);   //新加第一句

  input_sync(ts.input);

 

 

  input_report_key(ts.input, BTN_TOUCH, 0);

  input_report_abs(ts.input, ABS_PRESSURE,  0);  //新加第二句

  input_sync(ts.input);

在s3c2410ts_probe函数里:

  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.input->name = "S3C24XX TouchScreen";

注意第一句跟第二句的参数不一样,最后一个参数一个是1一个是0。

假如设置错误的话,运行tslib的测试程序会提示:

random: nonblocking pool is initialized

修改完毕后就可以编译下载内核到开发板,假如需要修改环境变量的话还需要修改环境变量。

运行ts_calibrate

屏幕出现十字符,点击校正。

校正后会在TSLIB_CALIBFILE=/tmp/pointercal

定义的目录里面生成pointercal校正文件。

然后运行ts_test

此时可以画点或者画线等等,如此表示校验成功。

可以进行测试

参考:

http://hi.baidu.com/396954504/item/45d171f027327014cf9f3226

http://www.latelee.org/embedded-linux/porting-linux-tslib.html

http://blog.csdn.net/zhaocj/article/details/37522115

以上是关于八 s3c2440 linux 触摸屏 驱动代分析的主要内容,如果未能解决你的问题,请参考以下文章

RK3399驱动开发 | 07 - GT911触摸屏驱动调试及问题分析解决(基于RK SDK Linux 4.4.194内核)

学习Linux下s3c2440的USB鼠标驱动笔记

基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读

树莓派3代3.5寸触摸屏驱动的安装(通过ssh安装)

s3c2440液晶屏驱动 (非内核自带) linux-4.1.24

六 linux UART串口驱动代分析