十三、Linux驱动之触摸屏驱动
1. 基本概念
常用的触摸屏类型有两种:阻性触摸屏和容性触摸屏。阻性触摸屏是一种传感器,它将矩形区域中触摸点(X, Y)的物理位置转换为代表X坐标和Y坐标的电压。触摸屏包含上下叠合的两个透明层阻性材料,中间由一种弹性材料隔开。当触摸屏表面受到压力时,顶层和底层之间会产生触碰。所用的电阻式触摸屏都采用分压器原理来产生代表X坐标和Y坐标的电压。如下图所示,分压器是通过将两个电阻进行串联来实现的。上面的电阻R1连接正参考电压Vref,下面的电阻R2接地。连个电阻连接点处的电压测量值与下面那个电阻的阻值成正比。
四线触摸屏示意图如下:
四线触摸屏包含两个阻性层。其中一层在屏幕的左右边各有一条垂直总线,另一层在屏幕的底部和顶部各有一条水平总线,见上图。为了X轴方向进行测量,将左侧总线偏置为0V,右侧总线偏置为Vref。将顶部或底部总线连接到ADC,当顶层和底层相接触时即可做一次测量。
2. 分析设备
2.1 引脚连接
本人使用的是JZ2440v3开发板,该开发板CPU使用的是S3C2440A,使用的是4线触摸屏,该4线连接在2440的AIN4~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驱动之触摸屏驱动相关推荐
- linux下GT911触摸屏驱动优化记录
linux下GT911触摸屏驱动优化记录 背景 由于最近要做linux内核启动速度优化,所以就对着驱动一点一点优化,加上QT应用程序的初始化,总共的启动时间要做到4S以内.目前先调试GT911驱动程序 ...
- Linux 驱动 | hy46xx触摸屏驱动
hy46xx Touch IC hy46xx是HYCON科技一款触摸IC. 上电时序: 通信接口: 使用IIC通信 中断方式: 当INT引脚产生下降沿的时候,触摸数据就绪,这样就可以在中断中读取 如果 ...
- linux内核关闭触摸屏校准,linux内核usb触摸屏驱动bug调试- selected device is not a touchscreen I understand...
近期给客户调试一块数控板,今天客户带过来一个屏,并且有一个usb的触摸屏芯片接在屏上.屏很快就弄好正常显示. 触摸屏在内核下找到usb 触摸屏驱动,内核启动后这个usb转的触摸屏也正常找到,注册为ev ...
- linux rs232触摸屏驱动程序,Linux下的触摸屏驱动
一.触摸屏理论概述 对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码.下面讲的是基于Mini2440的触摸屏驱动,现在的驱动我们都将设备和驱动分离,挂在平台设备总线上,让设备和驱动去匹配 ...
- Linux下的触摸屏驱动
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.触摸屏理论概述 对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码.下面讲的是基于Mini ...
- FL2440移植LINUX-3.4.2 -- 按键驱动和触摸屏驱动移植
(一)先移植按键输入子系统驱动: 拿过去编译,改错,然后insmod: (二)触摸屏驱动拿过去编译,改错,然后insmod: 触摸屏驱动的使用: 编译: tar xzf tslib-1.4.tar.g ...
- Linux/Android——usb触摸屏驱动 - usbtouchscreen (一)
版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入.屏幕显示的机械.光电性能,并不代表本人局部或全部同意.支持或者反对观点.如需要详查 ...
- Linux内核---30.触摸屏驱动分析
查看input系统 root@OK6410:~# cat /proc/bus/input/devices I: Bus=0013 Vendor=dead Product=beef Version=01 ...
- linux触摸屏代码解析,Linux触摸屏驱动解析
Linux下开发触摸屏驱动,最好的范例莫过于mc68328digi.c的实现.在没有看到原文之前,我把其中用到的结构解析一下. 1,struct ts_pen_info 该结构是触摸屏的核心数据结构. ...
- 【正点原子Linux连载】第六十四章 Linux 多点电容触摸屏实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
最新文章
- 表格排序——tablesorter.js使用(支持中文排序)
- 深度学习Deep learning:四十九(RNN-RBM简单理解)
- SpringSecurity分布式整合之分布式认证流程说明
- 11.reindex操作
- 深度学习在美图个性化推荐的应用实践
- 框架使用SpringBoot + Spring Security Oauth2 +PostMan
- 专属设计师的专业领域导航网站
- 如何在Mac电脑上更改地区或国家位置设定?
- AlphaGo 引发的中国象棋之路
- esxi能直通的显卡型号_显卡参数看不懂?手把手教你选独立显卡
- 舵机和舵机控制版、步进电机、伺服电机
- JPA中Specification方法
- 与Windows更新的抗争-取消Windows系统自动更新
- JavaScript级联链表
- 蘑菇云「行空板Python入门教程」第七课:舒尔特方格小游戏
- 2015物联网安全年报
- php中下载xls某个文件,php下载excel文件
- spring boot 整合JSP(详解)
- 遗传算法求解无人机路径多目标规划问题(python实现)
- 一把王者的时间带你拿捏计算机原码、反码、补码的计算原理