arm触摸屏 linux
2016-07-10 18:56:24 liyi850416 阅读数 469

1.   移植触摸屏tslib

1.1.1.    下载tslib-1.4.tar.gz

1.1.2.    安装

$ tar -zxvf tslib-1.4.tar.gz

$ cd tslib

$ ./autogen.sh

$ echo"ac_cv_func_malloc_0_nonnull=yes">arm-linux.cache

$./configure--host=arm-linux --cache-file=arm-linux.cache --enable-inputapi=no-prefix=/usr/local/tslib

$ make

$ make install

红色的“0”是数字0;(2) /usr/local/tslib表示tslib安装后的路径

1.1.3.    修改ts.conf内容

$ vi /usr/local/tslib/etc/ts.conf

添加如下:

module_raw input

module pthres pmin=1

module variance delta=30

module dejitter delta=100

module linear

1.1.4.    下载到开发板

然后将整个tslib文件夹,下载到开发板的对应路径下(/usr/local)

 

1.1.5.    开发板配置环境变量

$ vi /etc/profile

 

export TSLIB_ROOT=/usr/local/tslib

exportTSLIB_TSDEVICE=/dev/input/event0    指定触屏设备

export TSLIB_CALIBFILE=/etc/pointercal    指定触摸屏校准文件 pintercal 的存放位置

exportTSLIB_CONFFILE=$TSLIB_ROOT/etc/ts.conf   指定 TSLIB 配置文件的位置

exportTSLIB_PLUGINDIR=$TSLIB_ROOT/lib/ts    指定触摸屏插件所在路径

export TSLIB_FBDEVICE=/dev/fb0    指定帧缓冲设备

export TSLIB_CONSOLEDEVICE=none    设定控制台设备为 none ,否则默认为/dev/tty ,这样可以避免出现“open consoledevice: No such file or directory KDSETMODE: Bad file descriptor ” 的错误

exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TSLIB_ROOT/lib

1.1.6.    执行测试命令

$ cd/usr/local/tslib/bin

$ ./ts_calibrate

出现tslib的触摸屏五点校准画面,至此,tslib的安装移植成功完成。

$ ./ts_test

2018-12-12 01:47:57 weixin_42462202 阅读数 927

linux触摸屏

linux触摸屏(一)编写触摸屏应用

linux触摸屏(二)使用tslib

 

触摸屏是input设备,对于编写input设备的应用程序,真正的难点在于对读取的input_event中数据的理解

接下先解析触摸屏读取程序,
然后再解析input_event中数据的含义

  1、先打开设备文件,获取文件描述符

int fd;

fd = open(TS_SCREEN_DEV, O_RDONLY); 
if(fd < 0)
{
    fprintf(stderr, "%s can't open!!!\n", TS_SCREEN_DEV);
    return -1;
}

2、使用select函数来实现等待读取

int ret;
fd_set rds;

FD_ZERO(&rds);
FD_SET(fd, &rds); /* 将之前打开的文件和rds绑定 */

while(1)
{
    ret = select( fd + 1, &rds, NULL, NULL, NULL ); /* 等待设备的可读 */

    if(FD_ISSET(fd, &rds)) /* 判断设备读取状态是否满足 */
    {
        /* 在这里read触摸屏数据 */
    }    
}

这里讲解一下select函数

我们通过select函数告诉内核,我们关心哪一个文件的哪个状态,当这个文件的状态满足时,select就会返回

举个例子:当我们在读取某个文件时发生阻塞等待,而现在我们不想阻塞等待,我们想当文件可以读取时我们再去读取

这个时候就可以使用select函数,通过select告诉内核,我们想要这个文件的读取状态,当状态满足时,select函数就会返回,

这是我们就可以去读取文件而不会发生阻塞。

select函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

参数一表示:我们读取的文件集合中,最高的文件描述符 +  1

中间三个参数表示,我们关心哪些文件描述集的什么状态(是否可读,是否可写,是否异常)

那么我们怎么样告诉内核我们关心哪些文件描述集呢?

前面的代码中 FD_SET(fd, &rds); 就是告诉内核我们关心哪些文件。

最后一个参数表示,我们在select函数中最大的等待时间,如果设置为NULL,则表示一直等待

 

当select状态返回之后,调用 FD_ISSET(fd, &rds) 判断是够满足读取状态,如果满足就可以对触摸屏进行读取

3、读取触摸屏数据

struct input_event event; /* input设备上报的数据结构 */

read(fd, &event, sizeof(struct input_event));

input设备上报数据都是以 struct input_event 格式的,对于这个结构体中数据的理解是我们理解 input 应用程序编写的重点

接下来将重点解析这个结构体中数据的含义

首先我们要知道这个结构体从何而来?

input驱动程序上报事件,最终都会以这个结构体形式上报应用层,这个结构体的具体定义在内核源码的 <linux/input.h>

看一下这个结构体的定义

struct input_event {
	struct timeval time; /* 时间,包括秒和微秒 */
	__u16 type; /* 类型 */
	__u16 code; /* 键值 */
	__s32 value; /* 根据前两类型有不同的定义 */
};

struct timeval {
	__kernel_time_t		tv_sec;		/* seconds,32位 */
	__kernel_suseconds_t	tv_usec;	/* microseconds,32位 */
};

由此我们可以得知,读取的input_event结构体的数据包括 :

秒(32位)、微秒(32)、事件类型(16位)、键值(16位)、value(32位)

我们可以在开发板的系统上使用命令 来读取触摸屏(注:触摸屏文件名不一定是event1,可能是其他(eventx)

# hexdump /dev/input/event1 /* hexdump表示以16进制读取显示 */

当我们按键触摸屏时我们会获得一大串数据,根据我们前面的解析,我们可以知道每个数代表什么

32位表示秒,32位表示微秒,16位表示事件类型(type),16位表示键值(code),32位表示value

忽略     秒          微秒       type  code  value     
0000120  2727 0000  4589 0001  0003  0000  021e 0000
0000130  2727 0000  4590 0001  0003  0001  018d 0000
0000140  2727 0000  4593 0001  0001  014a  0001 0000
0000150  2727 0000  4595 0001  0003  0018  0001 0000

好,现在我们想知道有哪些事件类型,哪些键值,value又是表示什么?

我们来查看<linux/input.h>有这么一些宏定义,这些对应的是事件类型(type)

/*
 * Event types
 */

#define EV_SYN			0x00 /* 同步类事件 */
#define EV_KEY			0x01 /* 按键类事件 */
#define EV_REL			0x02 
#define EV_ABS			0x03 /* 绝对位置事件,也就是触摸屏 */
#define EV_MSC			0x04
#define EV_SW			0x05
#define EV_LED			0x11
#define EV_SND			0x12
#define EV_REP			0x14
#define EV_FF			0x15
#define EV_PWR			0x16
#define EV_FF_STATUS		0x17
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)

对比我们之前使用  # hexdump /dev/input/event1 得到数据可知,当按下触摸屏时,触摸屏的type = 3 and type = 1

说明按下触摸屏我们得到了 按键类事件,和绝对位置事件

我们再来看看键值(code)对应哪些宏定义

每种不同的事件有不同的键值,我这里只列出一小部分

/*
 * Absolute axes
 * 绝对位置事件对应的键值
 */
#define ABS_X			0x00
#define ABS_Y			0x01
#define ABS_PRESSURE		0x18

/*
 * 按键类事件的键值
 */
#define BTN_TOUCH		0x14a

对比我们之前使用  # hexdump /dev/input/event1 得到数据可知,第一行的code = 0,所以是ABS_X,表示x轴,同理第二行表示y轴。

接下来我们再来分析下这串数据的含义就焕然大悟了

忽略     秒          微秒       type  code  value     
0000120  2727 0000  4589 0001  0003  0000  021e 0000 /* type:EV_ABS, code:ABS_X */
0000130  2727 0000  4590 0001  0003  0001  018d 0000 /* type:EV_ABS, code:ABS_Y */
0000140  2727 0000  4593 0001  0001  014a  0001 0000 /* type:EV_KEY, code:BTN_TOUCH */
0000150  2727 0000  4595 0001  0003  0018  0001 0000 /* type:EV_ABS, code:ABS_PRESSURE*/

第一行:绝对坐标事件,也是触摸屏事件,X轴,此时value为 0x0000_021e (542),表示触摸点的x坐标为542

第二行:绝对坐标事件,Y轴,此时value为0x0000_018d  (397),表示触摸点的y坐标为397

综上两行,通过两个input_event得到了触摸坐标为(542,397)

第三行:按键类事件,触摸按键,1表示按下

第四行:绝对坐标事件,触摸压力,1表示按下

到这里已经对我们read得到的input结构体数据的含义很清楚了

我们在写应用程序的时候,通过read读取一个一个的input结构体,然后根据每个数据的含义进行解析

 

下面是源码

void ts_read(void)
{
	int ret = -1;
    int fd = -1;
    fd_set rds;
    struct input_event event;
    
    fd = open(TS_SCREEN_DEV, O_RDONLY); 
    if(fd < 0)
    {
        fprintf(stderr, "%s can't open!!!\n", TS_SCREEN_DEV);
        return -1;
    }

    FD_ZERO(&rds);
    FD_SET(fd, &rds);

    while(1)
    {
        /* 在fd + 1 个设备中查找,当fd发生时间时唤醒 */
        ret = select( fd + 1, &rds, NULL, NULL, NULL );

        if(ret < 0)
        {
            perror("select");
            close(fd);
            return -1;
        }

        if(FD_ISSET(fd, &rds)) /* 是否是我们想要的状态 */
        {
            ret = read(fd, &event, sizeof(struct input_event)); /* 读取input_event数据 */
            if(ret < 0)
            {
                perror("read");
                memset(&m_ts_info, 0, sizeof(m_ts_info));
            }

            /* 做我们的事 */
        }
    }

    close(fd);
}

 

 

 

2012-12-18 22:39:06 wuye110 阅读数 664

触摸屏驱动,挂在平台驱动下,input 子系统中。

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
 * iPAQ H1940 touchscreen support
 *
 * ChangeLog
 *
 * 2004-09-05: Herbert P枚tzl <herbert@13thfloor.at>
 *	- added clock (de-)allocation code
 *
 * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
 *      - h1940_ -> s3c2410 (this driver is now also used on the n30
 *        machines :P)
 *      - Debug messages are now enabled with the config option
 *        TOUCHSCREEN_S3C2410_DEBUG
 *      - Changed the way the value are read
 *      - Input subsystem should now work
 *      - Use ioremap and readl/writel
 *
 */  

#include <linux/autoconf.h>
#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 <asm/io.h>
#include <asm/irq.h>

#include <linux/platform_device.h>


#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/ts.h>
#include <asm/hardware/clock.h>

/* For ts.dev.id.version */
#define S3C2410TSVERSION	0x0101

#define WAIT4INT(x)  (((x)<<8) | \
		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
		     S3C2410_ADCTSC_XY_PST(3))

#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
		     S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))

#define DEBUG_LVL    KERN_DEBUG

MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("s3c2440 touchscreen driver");
MODULE_LICENSE("GPL");

/*
 * Definitions & global arrays.
 */


static char *s3c2410ts_name = "s3c2440 TouchScreen";

/*
 * Per-touchscreen data.
 */

struct s3c2410ts {
	struct input_dev *dev;
	long xp;
	long yp;
	int count;
	int shift;
	char phys[32];
};

static struct s3c2410ts ts;
static void __iomem *base_addr;

static inline void s3c2410_ts_connect(void)
{
	s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
	s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
	s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
	s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}

static void touch_timer_fire(unsigned long data)
{
  	unsigned long data0;
  	unsigned long data1;
	int updown;
	int tmp;	//add by lili 2006-12-12
  	data0 = readl(base_addr+S3C2410_ADCDAT0);
  	data1 = readl(base_addr+S3C2410_ADCDAT1);

 	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

 	if (updown) {
 		if (ts.count != 0) {
 			ts.xp >>= ts.shift;
 			ts.yp >>= ts.shift;
//reset coordinate system add by lili 2006-12-12
#ifdef CONFIG_RESET_COORDINATE_SYSTEM
			tmp = ts.xp;
			ts.xp = ts.yp;
			ts.yp = tmp;
#endif
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
 			{
 				struct timeval tv;
 				do_gettimeofday(&tv);
				printk("Touch screen info:");		//add by lili 2006-12-9
 				printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
 			}
#endif

 			input_report_abs(ts.dev, ABS_X, ts.xp);
 			input_report_abs(ts.dev, ABS_Y, ts.yp);

 			input_report_key(ts.dev, BTN_TOUCH, 1);
 			input_report_abs(ts.dev, ABS_PRESSURE, 1);
 			input_sync(ts.dev);
 		}

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

 		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
 		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
 	} else {
 		ts.count = 0;

 		input_report_key(ts.dev, BTN_TOUCH, 0);
 		input_report_abs(ts.dev, ABS_PRESSURE, 0);
 		input_sync(ts.dev);

 		writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
 	}
}

static struct timer_list touch_timer =
		TIMER_INITIALIZER(touch_timer_fire, 0, 0);

static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned long data0;
	unsigned long data1;
	int updown;
	disable_irq(IRQ_TC);	//add by lili 2007-6-19
	data0 = readl(base_addr+S3C2410_ADCDAT0);
	data1 = readl(base_addr+S3C2410_ADCDAT1);

	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

	/* TODO we should never get an interrupt with updown set while
	 * the timer is running, but maybe we ought to verify that the
	 * timer isn't running anyways. */

	if (updown)
		touch_timer_fire(0);
	enable_irq(IRQ_TC);		//add by lili 2007-6-19
	return IRQ_HANDLED;
}


static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned long data0;
	unsigned long data1;
	disable_irq(IRQ_ADC);		//add by lili  2007-06-19
	data0 = readl(base_addr+S3C2410_ADCDAT0);
	data1 = readl(base_addr+S3C2410_ADCDAT1);

	ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
	ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
	ts.count++;

        if (ts.count < (1<<ts.shift)) {
		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
	} else {
		mod_timer(&touch_timer, jiffies+1);
		writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
	}
	enable_irq(IRQ_ADC);		//add by lili 2007-6-19
	return IRQ_HANDLED;
}

static struct clk	*adc_clock;

/*
 * The functions for inserting/removing us as a module.
 */

static int __init s3c2410ts_probe(struct device *dev)
{
	struct s3c2410_ts_mach_info *info;

	info = ( struct s3c2410_ts_mach_info *)dev->platform_data;

	if (!info)
	{
		printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
		return -EINVAL;
	}

#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
	printk(DEBUG_LVL "Entering s3c2410ts_init\n");
#endif

	adc_clock = clk_get(NULL, "adc");
	if (!adc_clock) {
		printk(KERN_ERR "failed to get adc clock source\n");
		return -ENOENT;
	}
	//clk_use(adc_clock);
	clk_enable(adc_clock);

#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
	printk(DEBUG_LVL "got and enabled clock\n");
#endif

	base_addr=ioremap(S3C2410_PA_ADC,0x20);
	if (base_addr == NULL) {
		printk(KERN_ERR "Failed to remap register block\n");
		return -ENOMEM;
	}


	/* Configure GPIOs */
	s3c2410_ts_connect();

	if ((info->presc&0xff) > 0)
		writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
			     base_addr+S3C2410_ADCCON);
	else
		writel(0,base_addr+S3C2410_ADCCON);


	/* Initialise registers */
	if ((info->delay&0xffff) > 0)
		writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);

	writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);

	/* Initialise input stuff */
	memset(&ts, 0, sizeof(struct s3c2410ts));
	//init_input_dev(&ts.dev);
	ts.dev = input_allocate_device();
	ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
	ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT(BTN_TOUCH);
	input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
	input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
	input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);

	sprintf(ts.phys, "ts0");

	ts.dev->private = &ts;
	ts.dev->name = s3c2410ts_name;
	ts.dev->phys = ts.phys;
	ts.dev->id.bustype = BUS_RS232;
	ts.dev->id.vendor = 0xDEAD;
	ts.dev->id.product = 0xBEEF;
	ts.dev->id.version = S3C2410TSVERSION;

	ts.shift = info->oversampling_shift;

	/* Get irqs */
	if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM,
		"s3c2410_action", ts.dev)) {
		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
		iounmap(base_addr);
		return -EIO;
	}
	if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
			"s3c2410_action", ts.dev)) {
		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
		iounmap(base_addr);
		return -EIO;
	}
/*
 *	for debug regs
 */
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
	unsigned long debug_regs;
	debug_regs = 0;
	debug_regs = readl(base_addr+S3C2410_ADCCON);
	printk("S3C2410_ADCCON:0x%08lx\n",debug_regs);
	debug_regs = readl(base_addr+S3C2410_ADCTSC);
	printk("S3C2410_ADCTSC:0x%08lx\n",debug_regs);
	debug_regs = readl(base_addr+S3C2410_ADCDLY);
	printk("S3C2410_ADCDLY:0x%08lx\n",debug_regs);
#endif

	printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);

	/* All went ok, so register to the input system */
	input_register_device(ts.dev);

	return 0;
}

static int s3c2410ts_remove(struct device *dev)
{
	disable_irq(IRQ_ADC);
	disable_irq(IRQ_TC);
	free_irq(IRQ_TC,ts.dev);
	free_irq(IRQ_ADC,ts.dev);

	if (adc_clock) {
		clk_disable(adc_clock);
		//clk_unuse(adc_clock);
		clk_put(adc_clock);
		adc_clock = NULL;
	}

	input_unregister_device(ts.dev);
	iounmap(base_addr);

	return 0;
}

static struct device_driver s3c2410ts_driver = {
       .name           = "s3c2410-ts",
       .bus            = &platform_bus_type,
       .probe          = s3c2410ts_probe,
       .remove         = s3c2410ts_remove,
};


int __init s3c2410ts_init(void)
{
	return driver_register(&s3c2410ts_driver);
}

void __exit s3c2410ts_exit(void)
{
	driver_unregister(&s3c2410ts_driver);
}

module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);

/*
    Local variables:
        compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
        c-basic-offset: 8
    End:
*/

分析一下这个驱动。

1.数据结构分析。

    触摸屏设备:1.继承输入子系统设备;2.坐标位置;3.ADC采集次数;4.采样移位;5设备名称。

    定时器

    device_driver

    platform_device

     s3c2410_ts_mach_info:触摸屏的配置信息

2.驱动分析

a.入口,出口。module_init(s3c2410ts_init);module_exit(s3c2410ts_exit);

      入口和出口只做了一件事:将device_driver结构向系统中注册和注销。

    在device_driver 中关联了设备,总线,以及相关操作(个人理解为驱动操作)。
 b.关于平台总线设备驱动

    1.在平台资源中通过s3c24xx_ts_set_platdata(&utu2440_ts_cfg);将触摸屏的配置信息加到平台资源中去,其中utu2440_ts_cfg为配置的平台资源,在探测函数中,通过传入 的平台设备,利用dev->platform_data取出数据。

    2.探测函数主要做的事情

       1.取出平台设备中关于触摸屏的配置信息

       2.取出adc 采样的时钟,并使能时钟

       3.映射ADC的物理地址

       4.配置2440的引脚,使之工作在触摸屏模式

       5.根据配置信息,配置相关的寄存器

      6.为触摸屏设备分配内存,初始化输入子系统,并向输入子系统模型设备中填充相应内容

      7.请求中断,包括ADC中断和TS中断

      8.输入子系统注册

   3.移除函数主要做的事情

      1.禁止中断,释放中断

       2.禁止时钟,释放时钟

       3. 在输入子系统中注销

       4.取消地址映射

c.中断响应

   1.ADC中断响应:stylus_action

    2.触摸屏的中断响应:stylus_updown

d.触摸屏的事件报告:touch_timer_fire

2014-07-01 21:14:00 weixin_30855099 阅读数 4

触摸屏驱动程序框架与上一片文章的输入子系统类似,只是底层驱动由按键变成了触摸屏。

S3C2440的ADC相关寄存器:

struct s3c_ts_regs {
  unsigned long adccon;
  unsigned long adctsc;
  unsigned long adcdly;
  unsigned long adcdat0;
  unsigned long adcdat1;
  unsigned long adcupdn;
};

1.分配input_dev结构体

struct 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);
//表示支持绝对值x坐标,并设置它在坐标系中的最大值和最小值,以及干扰值和平焊位置等
input_set_abs_params(s3c_ts_dev, ABS_X,              0, 0x3FF, 0, 0);
input_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.注册
inpute_register_device(s3c_ts_dev);
4.硬件相关操作
4.1使能ADC时钟
struct clk *clk = clk_get(NULL, "adc");
clk_enable(clk);
4.2设置ADC相应寄存器
s3c_ts_regs = ioremap((0x58000000, sizeof(struct s3c_ts_regs));
/* converter prescaler enable , ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz */
s3c_ts_regs->adccon = (1<<14)|(49<<6);
s3c_ts_regs->adcdly = 0xffff;//为了确保ADC转换的精度,将这个寄存器设置成最大值
4.3申请中断,ADC转换完成中断和触摸屏按下或抬起中断
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);
4.4初始化定时器,用来处理在触摸屏上滑动的情况
init_timer(&ts_timer);
ts_timer.function = s3c_ts_timer_function;
add_timer(&ts_timer);
4.5让触摸屏进入等待笔尖按下状态
enter_wait_pen_dowm_mode();
5.触摸屏模式设置
void enter_wait_pen_down_mode(void)
{
     s3c_ts_regs->adctsc = 0xd3;
}
void enter_wait_pen_up_mode(void)
{
     s3c_ts_regs->adctsc = 0x1d3;
}
void enter_measure_xy_mode(void)
{
     s3c_ts_regs->adctsc = (1<<3) | (1<<2);
}
void start_adc(void)
{
     s3c_ts_regs->adccon |= (1<<0);
}
6.定时器处理函数,上报事件
void s3c_ts_timer_function(void)
{
     if (s3c_ts_regs->adcdat0 & (1<<15))
     {
          /* 松开 */
          input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
          input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
          input_sync(s3c_ts_dev);
          enter_wait_pen_down_mode();
     } else {
          /* 按下 */
          enter_measure_xy_mode();
          start_adc();
     }
}
7.中断处理函数
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);
          input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
          input_sync(s3c_ts_dev);
          enter_wait_pen_down_mode();
     } else {
          /* 按下 */
          enter_measure_xy_mode();
          start_adc();
     }
     return IRQ_HANDLED;
}
irqreturn_t adc_irq(int irq, void *dev_id)
{
     static int cnt = 0;
     static int x[4], y[4];
     int adcdat0, adcdat1;
     
    adcdat0 = s3c_ts_regs->adcdat0;
    adcdat1 = s3c_ts_regs->adcdat1;
    if (s3c_ts_regs->adcdat0 & (1<<15)) {
          /* 松开 */
         cnt = 0;
         input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
         input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
          input_sync(s3c_ts_dev);
         enter_wait_pen_down_mode();
     } else {
         x[cnt] = adcdat0 & 0x3FFF;
         y[cnt] = adcdat1 & 0x3FFF;
         ++cnt;
         if (cnt == 4) {
               if (s3c_filter_ts(x, y)) {
                   input_report_abs(s3c_ts_dev, ABS_X, (x[1]+x[2]+x[3]+x[4])/4);
                   input_report_abs(s3c_ts_dev, ABS_Y, (y[1]+y[2]+y[3]+y[4])/4);
                   input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
                   input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
                   input_sync(s3c_ts_dev);
               }
              cnt = 0;
              enter_wait_pen_up_mode();
              mod_timer(&ts_timer, jiffies + HZ / 100);
          } else {
              enter_measure_xy_mode();
              start_adc();
          }
     }
    return IRQ_HANDLED;
}
8.过滤ADC转化的数据
static int s3c_filter_ts(int x[], int y[])
{
#define ERR_LIMIT 10

     int 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;
}
 9.移植tslib,测试触摸屏驱动程序
9.1解压、配置、编译tslib-1.4.tar.gz
  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
9.2装载驱动
  装载s3c_ts.ko驱动和lcd.ko驱动
9.3修改/etc/ts.conf第1行
  将"# module_raw input"改为"module_raw input",去掉#号和第一个空格
9.4添加环境变量
  export TSLIB_TSDEVICE=/dev/event0          指定触摸设备
     export TSLIB_CALIBFILE=/etc/pointercal       指定触摸屏校准文件pointercal的存放位置
     export TSLIB_CONFFILE=/etc/ts.conf            指定TSLIB配置文件的位置
     export TSLIB_PLUGINDIR=/lib/ts                  指定触摸屏插件所在目录
     export TSLIB_CONSOLEDEVICE=none           设置控制台设备为none,否则默认为/dev/tty,这样可以避免出现“open consoledevice: No such file or directory KDSETMODE: Bad file descriptor”的错误
     export TSLIB_FBDEVICE=/dev/fb0                 指定帧缓存设备
一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原来脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。
9.5触摸屏校准
  执行程序ts_calibrate
9.6触摸屏测试
  执行程序ts_test

转载于:https://www.cnblogs.com/zpehome/p/3819051.html

2007-10-01 10:51:00 besthyq 阅读数 4734

本文抛开技术层次上来说说LINUX触摸屏设备驱动原理

     触摸屏,就是用一块AD转换芯片来将屏幕上的触摸信号转成数字信号。触摸屏常用的是四线电阻,AD转换芯片

常用的是7843,7846,TS2003等。当触摸时候,这些芯片会将模拟信号转换成数字信号,就是通常说的AD转换。

在LINUX中,通常是通过中断来读取这些数字的。触摸屏幕的时候,中断发生,LINUX通过串口或者I2C,SPI等去

读取数字,然后把数值传给INPUT层。这就是一般驱动的原理。

  触摸屏校验,因为你从驱动里活动的数值大多都是8位,或者12位的。可以表示的坐标范围是(0.0)--(256,256)或者

(0,0)--(4096,4096),你需要将次数字与屏幕的分辨率相对应。这个时候,你就需要校验程序来一一对应

这些。比较常用的校验程序是TSLIB。

  通过TSLIB校验后,应用程序读取TSLIB里的数值,这个时候就能准确定位了

下面具体说说我坐的一个驱动

  触摸屏------TI2007-----------I2C----------INPUT-------TSLIB-------应用程序。

具体代码分析等下篇再描述

 

 

 

arm linux

阅读数 4

arm linux

阅读数 4

arm linux

阅读数 205

Arm & Linux

阅读数 598

没有更多推荐了,返回首页