1. 基本概念

常用的触摸屏类型有两种:阻性触摸屏和容性触摸屏。阻性触摸屏是一种传感器,它将矩形区域中触摸点(X, Y)的物理位置转换为代表X坐标和Y坐标的电压。触摸屏包含上下叠合的两个透明层阻性材料,中间由一种弹性材料隔开。当触摸屏表面受到压力时,顶层和底层之间会产生触碰。所用的电阻式触摸屏都采用分压器原理来产生代表X坐标和Y坐标的电压。如下图所示,分压器是通过将两个电阻进行串联来实现的。上面的电阻R1连接正参考电压Vref,下面的电阻R2接地。连个电阻连接点处的电压测量值与下面那个电阻的阻值成正比。

四线触摸屏示意图如下:

四线触摸屏包含两个阻性层。其中一层在屏幕的左右边各有一条垂直总线,另一层在屏幕的底部和顶部各有一条水平总线,见上图。为了X轴方向进行测量,将左侧总线偏置为0V,右侧总线偏置为Vref。将顶部或底部总线连接到ADC,当顶层和底层相接触时即可做一次测量。

2. 分析设备

2.1 引脚连接

本人使用的是JZ2440v3开发板,该开发板CPU使用的是S3C2440A,使用的是4线触摸屏,该4线连接在2440AIN4~AIN7引脚上,如图:

2.2 s3c2440触摸屏接口

查看s3c2440手册ADC & TOUCH SCREEN INTERFACE触摸屏章节触摸屏A/D转换器和触摸屏接口框图如下:


    值得注意的是两个中断产生的时间,INT_TC在点击触摸屏时发生,INT_ADC在数模转换完成时发生。手册中还提到了A/D的转换时间,如下公式:

    ADC的工作频率最大为2.5MHZ,正确使用需要设置寄存器ADCCON->PRSCVL更改分频系数,对于JZ2440这里要设置ADC的工作频率为1MHZ(即设置ADCCON的bit6为49)
s3c2440手册提到了触摸屏接口的4种模式:
    1. 正常转换模式
    2. 分离式X/Y位置转换方式
    3. 自动X/Y位置转换模式
    4. 等待中断模式
从数据手册中我们可以得到设置模式相关信息:
    1. 设置为自动X/Y位置转换模式
        设置寄存器ADCTSC=0x0c,系统会把X坐标存入寄存器ADCDAT0,把Y坐标存入ADCDAT1。
    2. 设置为等待中断模式
        设置寄存器ADCTSC=0xd3(设置等待按下中断信号到来)
        设置寄存器ADCTSC=0x1d3(设置等待松开中断信号到来)

触摸屏工作流程:
    1. 选择XY坐标转换模式(独立转换/连续转换)
    2. 设置触摸屏到等待状态
    3. 如果中断发生,启动ADC转换(转换获得的坐标值会存入相应的寄存器中)
    4. 获得XY坐标,返回到步骤2

3. 编写代码

对于触摸屏驱动,也是使用输入子系统框架进行编写,输入子系统相关内容在九、Linux驱动之输入子系统分析里详细分析了,内核已经写好了驱动事件处理部分(触摸屏即对应tsdev.c),所以我们只需要写具体的驱动设备部分,然后内核会将两部分自动连接。先整理代码整体流程。

3.1 代码流程

3.1.1 在init入口函数中

    1. 分配一个input_dev结构体
    2. 设置input_dev的成员
        2.1 设置input_dev->evbit,即设置支持按键事件,绝对位移事件
        2.2 设置input_dev-> keybit,支持按键事件的触摸屏笔尖按下事件
        2.3 设置input_dev-> absbit,支持ABS_X、ABS_Y、 ABS_PRESSURE
            2.3.1 input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);  //ADC是10位,所以第四个参数设置为3FF
            2.3.2 input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);  //ADC是10位,所以第四个参数设置为3FF
            2.3.3 input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);  //压力最多就是1
   
3. 注册input_dev驱动设备到内核中
    4. 设置触摸屏相关的硬件
        4.1 开启ADC时钟,使用clk_get ()和clk_enable()函数
        4.2 ioremap获取寄存器地址,设置寄存器ADCCON =(1<<14)|(49<<6),设置时钟分频
        4.3 设置寄存器ADCDLY=0xffff,ADC启动延时时间设为最大值,使触摸电压稳定后再发出IRQ_TC中断
        4.4 开启IRQ_TC中断、开启IRQ_ADC中断
        4.5 初始化定时器,增加触摸滑动功能
        4.6 最后设置寄存器ADCTSC=0x0d3,开启IRQ_TC中断,即设置触摸屏进入等待按下中断模式(当触摸屏被按下,进入IRQ_TC中断函数)

3.1.2 在出口函数中

    1. 注销内核里的input_dev
   
2. 释放input_dev
   
3. 释放中断、删除定时器、iounmap注销地址

3.1.3 在IRQ_TC中断函数中

    1. 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0XD3,即设置触摸屏为等待按下模式
   
2. 若判断触摸屏当前为按下状态,设置为XY自动转换模式,启动一次ADC转换(ADC转换成功后,会进入IRQ_ADC中断函数)

3.1.4 在IRQ_ADC中断函数中

    1. 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0XD3,即设置触摸屏为等待按下模式
    2. 若判断触摸屏当前为按下状态,获取ADCDAT0/1的低10位,计算出X/Y方向坐标值。多次测量(多次设置为XY自动转换模式并启动ADC转换,转换完成就会再次进入IRQ_ADC中断函数),测量4次后判断精度,满足则上报数据,否则设置成等待松开模式,10ms后启动定时器超时函数

3.1.5 在定时器超时函数中

  1. 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0XD3,即设置触摸屏为等待按下模式
   
2. 若判断触摸屏当前为按下状态,设置为XY自动转换模式,启动一次ADC转换(ADC转换成功后,会进入IRQ_ADC中断函数)

3.2 编写代码

触摸屏驱动程序s3c_ts.c完整代码如下:

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/plat-s3c24xx/ts.h>
#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h>static struct input_dev *s3c_ts_dev;struct s3c_ts_regs{unsigned long adccon;unsigned long adctsc;unsigned long adcdly;unsigned long adcdat0;unsigned long adcdat1;unsigned long adcupdn;
};
static  struct s3c_ts_regs * s3c_ts_regs;
static struct timer_list ts_timer;/*进入等待按下模式*/
static void enter_wait_pen_down_mode(void)
{s3c_ts_regs->adctsc=0xd3;
}/*进入等待松开模式*/
static void enter_wait_pen_up_mode(void)
{s3c_ts_regs->adctsc = 0x1d3;
}/*进入XY自动转换模式*/
static void enter_measure_xy_mode(void)
{s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}/*启动一次ADC转换*/
static void start_adc(void)
{s3c_ts_regs->adccon |=(1<<0);
}static void s3c_ts_timer_function(unsigned long data)
{if (s3c_ts_regs->adcdat0 & (1<<15))   //如果触摸屏当前为松开状态{input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);//上报压力值为0input_report_key(s3c_ts_dev, BTN_TOUCH, 0);//上报BTN_TOUCH按键值松开input_sync(s3c_ts_dev);//上报同步事件,通知系统有事件上报enter_wait_pen_down_mode();///*进入等待按下模式*/}else{/* 进入测量X/Y坐标模式并启动一次ADC */enter_measure_xy_mode();start_adc();}
}
static int s3c_filter_ts(int x[], int y[])
{
#define ERR_LIMIT 10int avr_x, avr_y;int det_x, det_y;avr_x = (x[0] + x[1])/2;avr_y = (y[0] + y[1])/2;det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))return 0;avr_x = (x[1] + x[2])/2;avr_y = (y[1] + y[2])/2;det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))return 0;return 1;
}
static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{if (s3c_ts_regs->adcdat0 & (1<<15))       //如果触摸屏当前为松开状态{input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);//上报压力值为0input_report_key(s3c_ts_dev, BTN_TOUCH, 0);//上报BTN_TOUCH按键值松开input_sync(s3c_ts_dev);//上报同步事件,通知系统有事件上报enter_wait_pen_down_mode();///*进入等待按下模式*/}else{/* 进入测量X/Y坐标模式并启动一次ADC */enter_measure_xy_mode();start_adc();}return IRQ_HANDLED;
}static irqreturn_t adc_irq(int irq, void *dev_id)
{static int cnt=0;static int x[4],y[4];if (s3c_ts_regs->adcdat0 & (1<<15))    //如果触摸屏当前为松开状态{input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);//上报压力值为0input_report_key(s3c_ts_dev, BTN_TOUCH, 0);//上报BTN_TOUCH按键值松开input_sync(s3c_ts_dev);//上报同步事件,通知系统有事件上报enter_wait_pen_down_mode();///*进入等待按下模式*/cnt=0;}else{x[cnt]=s3c_ts_regs->adcdat0 & 0x3ff;y[cnt]=s3c_ts_regs->adcdat1 & 0x3ff;cnt++;if(cnt==4){if (s3c_filter_ts(x, y)){input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);//上报X方向值input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);//上报Y方向值input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);//上报压力方向值input_report_key(s3c_ts_dev, BTN_TOUCH, 1);//上报BTN_TOUCH按键值按下input_sync(s3c_ts_dev);//上报同步事件,通知系统有事件上报}cnt=0;enter_wait_pen_up_mode();/* 启动定时器处理长按/滑动的情况 */mod_timer(&ts_timer, jiffies + HZ/100);}else{/* 进入测量X/Y坐标模式并启动一次ADC */enter_measure_xy_mode();start_adc();}}return IRQ_HANDLED;
}static int s3c_ts_init(void)
{struct clk* clk;/*1.       分配一个input_dev结构体*/s3c_ts_dev=input_allocate_device();/*2.      设置*//*2.1.  能产生哪类事件*/set_bit(EV_KEY, s3c_ts_dev->evbit);set_bit(EV_ABS, s3c_ts_dev->evbit);/*2.2. 能产生这类事件的哪些事件*/set_bit(BTN_TOUCH, s3c_ts_dev->keybit);input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);//s3c2410手册ADC是10位,所以第四个参数设置为3FFinput_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);/*3.      注册*/input_register_device(s3c_ts_dev);/*4.      硬件相关操作*//*4.1.  使能时钟(设置CLKCON[15])*/clk=clk_get(NULL,"adc");clk_enable(clk);/*4.2.   设置s3c2440的ADC/ts寄存器*/s3c_ts_regs=ioremap(0x58000000, sizeof(struct s3c_ts_regs));s3c_ts_regs->adccon = (1<<14)|(49<<6);/**必须得进入等待按下中断或者等待松*开中断模式"ts_pen"这个中断才能在触摸*屏被按下或松开时被触发。*/request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);s3c_ts_regs->adcdly = 0xffff;    //设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断init_timer(&ts_timer);ts_timer.function = s3c_ts_timer_function;add_timer(&ts_timer);enter_wait_pen_down_mode();return 0;
}static void s3c_ts_exit(void)
{input_unregister_device(s3c_ts_dev);input_free_device(s3c_ts_dev);free_irq(IRQ_TC,NULL);free_irq(IRQ_ADC,NULL);iounmap(s3c_ts_regs);del_timer(&ts_timer);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");

Makefile完整代码如下:

KERN_DIR = /work/system/linux-2.6.22.6    //内核目录all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m  += s3c_ts.o

4. 测试

内核:linux-2.6.22.6
编译器:arm-linux-gcc-3.4.5
环境:ubuntu9.10

4.1 配置内核

1. 重新配置内核,将内核自带的触摸屏驱动配置为模块。在linux-2.6.22.6内核目录下执行:
      make menuconfig
    2. 配置步骤如下:
      Device Drivers  --->
          Input device support  --->
              [*]   Touchscreens  --->
                  < >   S3C2410/S3C2440 touchscreens 
(将内核自带的触摸屏驱动去掉)

4.2 重烧内核

1. 编译内核与模块
      make uImage
   
2. 将linux-2.6.22.6/arch/arm/boot下的uImage烧写到开发板,网络文件系统启动。

4.3 测试

     首先安装新驱动。在开发板目录上执行:
      insmod s3c_ts.ko
      ls /dev/event*

     
      此时新出现了/dev/event0这个设备,就是我们写的触摸屏驱动。继续执行:
      hexdump /dev/event0
      此时再点一下触摸屏,串口端输入如下:
     
    第1列表示hexdump序列号(如0000000)
    第2、3列表示秒(如013c 0000)
    第4、5列表示微妙(如c7c6 0000)
    第6列表示type(如0003表示ABS绝对位移类型,0001表示按键类型)
    第7列表示code(如0000表示x方向ABS_X,0001表示y方向ABS_Y,0018表示ABS_PRESSURE,014a表示BTN_TOUCH)
    第8、9列表示对应type的对应code值(如020b 0000)

4.4 安装tslib

    1.tslib-1.4.tar.gz放到服务器上,在ubuntu执行以下命令:
      tar xzf tslib-1.4.tar.gz  (解压文件)
      cd tslib
      ./autogen.sh
      mkdir 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  (安装到tmp)
      cp tmp /work/nfs_root/first_fs/ts_dir -rfd  (将tmp目录复制到网络文件系统目录)

    2. 网络文件系统启动开发板,进入刚才安装好的tslib目录,执行:
      cd /ts_dir/tmp
      cp * / -rfd 
(再将该目录里所有内容拷贝到开发板的根目录里)

    3. 安装lcd相关驱动,lcd相关内容参考十二、Linux驱动之LCD驱动开发板上执行命令如下:
      insmod cfbcopyarea.ko
      insmod cfbfillrect.ko
      insmod cfbimgblt.ko
      insmod lcd.ko 
(安装lcd相关驱动)
      insmod s3c_ts.ko  (安装触摸屏驱动)

    4. 修改 /etc/ts.conf文件,开发板上执行命令如下:
      vi /etc/ts.conf
     

    改为(去掉第二行的#号和第一个空格):
     

    5. 设置触摸屏的环境变量。开发板上执行命令如下:
      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   (lcd设备)

6. 校验。开发板上执行如下:
      ts_calibrate  (开发板上此时会出现校验触摸点)
     

7. 测试 。开发板上执行如下:
      ts_test  (就可以在lcd上绘画了!)
     
PS:在第1点中将tmp目录复制到网络文件系统目录时,将一些测试程序如ts_calibrate 、ts_test 等拷贝到了网络文件系统bin目录下。

十三、Linux驱动之触摸屏驱动相关推荐

  1. linux下GT911触摸屏驱动优化记录

    linux下GT911触摸屏驱动优化记录 背景 由于最近要做linux内核启动速度优化,所以就对着驱动一点一点优化,加上QT应用程序的初始化,总共的启动时间要做到4S以内.目前先调试GT911驱动程序 ...

  2. Linux 驱动 | hy46xx触摸屏驱动

    hy46xx Touch IC hy46xx是HYCON科技一款触摸IC. 上电时序: 通信接口: 使用IIC通信 中断方式: 当INT引脚产生下降沿的时候,触摸数据就绪,这样就可以在中断中读取 如果 ...

  3. linux内核关闭触摸屏校准,linux内核usb触摸屏驱动bug调试- selected device is not a touchscreen I understand...

    近期给客户调试一块数控板,今天客户带过来一个屏,并且有一个usb的触摸屏芯片接在屏上.屏很快就弄好正常显示. 触摸屏在内核下找到usb 触摸屏驱动,内核启动后这个usb转的触摸屏也正常找到,注册为ev ...

  4. linux rs232触摸屏驱动程序,Linux下的触摸屏驱动

    一.触摸屏理论概述 对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码.下面讲的是基于Mini2440的触摸屏驱动,现在的驱动我们都将设备和驱动分离,挂在平台设备总线上,让设备和驱动去匹配 ...

  5. Linux下的触摸屏驱动

    版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.触摸屏理论概述 对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码.下面讲的是基于Mini ...

  6. FL2440移植LINUX-3.4.2 -- 按键驱动和触摸屏驱动移植

    (一)先移植按键输入子系统驱动: 拿过去编译,改错,然后insmod: (二)触摸屏驱动拿过去编译,改错,然后insmod: 触摸屏驱动的使用: 编译: tar xzf tslib-1.4.tar.g ...

  7. Linux/Android——usb触摸屏驱动 - usbtouchscreen (一)

    版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入.屏幕显示的机械.光电性能,并不代表本人局部或全部同意.支持或者反对观点.如需要详查 ...

  8. Linux内核---30.触摸屏驱动分析

    查看input系统 root@OK6410:~# cat /proc/bus/input/devices I: Bus=0013 Vendor=dead Product=beef Version=01 ...

  9. linux触摸屏代码解析,Linux触摸屏驱动解析

    Linux下开发触摸屏驱动,最好的范例莫过于mc68328digi.c的实现.在没有看到原文之前,我把其中用到的结构解析一下. 1,struct ts_pen_info 该结构是触摸屏的核心数据结构. ...

  10. 【正点原子Linux连载】第六十四章 Linux 多点电容触摸屏实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

最新文章

  1. 表格排序——tablesorter.js使用(支持中文排序)
  2. 深度学习Deep learning:四十九(RNN-RBM简单理解)
  3. SpringSecurity分布式整合之分布式认证流程说明
  4. 11.reindex操作
  5. 深度学习在美图个性化推荐的应用实践
  6. 框架使用SpringBoot + Spring Security Oauth2 +PostMan
  7. 专属设计师的专业领域导航网站
  8. 如何在Mac电脑上更改地区或国家位置设定?
  9. AlphaGo 引发的中国象棋之路
  10. esxi能直通的显卡型号_显卡参数看不懂?手把手教你选独立显卡
  11. 舵机和舵机控制版、步进电机、伺服电机
  12. JPA中Specification方法
  13. 与Windows更新的抗争-取消Windows系统自动更新
  14. JavaScript级联链表
  15. 蘑菇云「行空板Python入门教程」第七课:舒尔特方格小游戏
  16. 2015物联网安全年报
  17. php中下载xls某个文件,php下载excel文件
  18. spring boot 整合JSP(详解)
  19. 遗传算法求解无人机路径多目标规划问题(python实现)
  20. 一把王者的时间带你拿捏计算机原码、反码、补码的计算原理

热门文章

  1. 计算机图形学基础(opengl版) pdf,计算机图形学基础(OpenGL版)
  2. 现代优化算法 (一):模拟退火算法 及应用举例
  3. Anylogic学习—银行排队模型
  4. 局域网 以太网 令牌环网(一)
  5. 电脑坏掉之后,Oracle数据恢复
  6. 通过PyMuPDF编写增值税发票多PDF文件合并工具
  7. pc电脑上浏览手机网站在线wap浏览器或模拟器软件
  8. 总结oninput、onchange与onpropertychange事件的用法和区别
  9. 节点数对5层网络迭代次数的影响
  10. windows 网卡驱动安装