触摸屏(TS)驱动

一、实验平台:开发板fs2410,采用三星s3c2410的CPU,linux操作系统。

二、实现功能:获取点击触摸屏的坐标,并在移动的过程中也能动态的显示出触点坐标。同时通过一些测试程序来校准触摸屏等(测试程序非自己撰写)。

三、实验原理:

       fs2410采用电阻触摸屏,用ADC的通道5、7分别获取触点的x、y绝对坐标值。

       当点击触摸屏时,会产生TS中断,在TS的中断处理程序中需要开启AD转换,获取坐标值。AD转换需要一定的时间,为了节省CPU的资源,转换完成后采用中断的方式,通知CPU AD转换已完成。

       为了能在移动过程中动态的显示出坐标,需要不断的进行AD转换,本例中采用定时器机制,在定时器超时后若还处于点击状态,则继续进行AD转换,获取坐标值,若已松开,则不转换。如此循环,就能获取动态坐标值了,这样就可以在屏幕上划线了。

四、实验现象:

       1、加载模块后,点击触摸屏,可以显示出坐标值;

       2、运行测试程序ts_callibrate,LCD屏幕会出现十字光标,分表点击LCD屏幕四个角和中间就可以讲触摸屏和LCD屏进行校准;

       3、运行测试程序ts_print_raw,会显示出坐标原始数据,现象与1类似;

       4、运行测试程序ts_test,然后点击屏幕上的draw,就可以在屏幕上划线了。

 

 

 

 

 

五、实验总结:

       由于AD转换存在一定的误差,所有在驱动代码中需要通过软件的方法对转换结果进行优化处理,如:

       1、点击后延时一会,让电压值稳定后再进行AD转换;

       2、在进行AD转换时,若这时触摸笔已松开,则丢弃此次AD转换结果;

       3、多次测量,再去平均值;

       4、过过一些软件过滤方法。

六、示例代码:

/*驱动代码s3c_ts.c*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

struct s3c_adc_regs {
	unsigned long adccon;
	unsigned long adctsc;
	unsigned long adcdly;
	unsigned long adcdat0;
	unsigned long adcdat1;
};

static struct input_dev *s3c_ts;
static struct s3c_adc_regs *s3c_adc_regs;
static volatile unsigned long *clkcon;
static volatile unsigned long *gpgcon;
static struct timer_list s3c_ts_timer;

static void enter_wait_for_pen_down_mode(void)
{
	s3c_adc_regs->adctsc = (1<<7) | (1<<6) | (0<<5) | (1<<4) | (0<<3) | (3);
}

static void enter_wait_for_pen_up_mode(void)
{
	s3c_adc_regs->adctsc = (1<<8) | (1<<7) | (1<<6) | (0<<5) | (1<<4) | (0<<3) | (3);
}

static void enter_auto_xy_mode(void)
{
	s3c_adc_regs->adctsc = (1<<7) | (1<<6) | (1<<4) | (1<<3) | (1<<2);
}

static void start_adc(void)
{
	s3c_adc_regs->adccon |= 1;
}

static void s3c_ts_timer_function(unsigned long data)
{
	if (s3c_adc_regs->adcdat0 & (1<<15))
	{
		input_report_key(s3c_ts, BTN_TOUCH, 0);
	        input_report_abs(s3c_ts, ABS_PRESSURE, 0);
	        input_sync(s3c_ts);
		
		enter_wait_for_pen_down_mode();
		return;
	}
	else
	{
		enter_auto_xy_mode();
		start_adc();
	}
}

static irqreturn_t s3c_ts_pen_down_up_isr(int irq, void *dev_id)
{
	if (s3c_adc_regs->adcdat0 & (1<<15))
	{
		/* 松开 */ 
		input_report_key(s3c_ts, BTN_TOUCH, 0);
	        input_report_abs(s3c_ts, ABS_PRESSURE, 0);
	        input_sync(s3c_ts);
		
		enter_wait_for_pen_down_mode();
	}
	else
	{
		enter_auto_xy_mode();
		start_adc();
	}
	return IRQ_HANDLED;
}


static int s3c_ts_filtering(int *px, int *py)
{
	int avr_x, avr_y;
	int dx, dy;

#define FILTER_LIMIT 10

	avr_x = (px[0] + px[1]) / 2;
	avr_y = (py[0] + py[1]) / 2;

	dx = (px[2] > avr_x) ? (px[2] - avr_x) : (avr_x - px[2]);
	dy = (py[2] > avr_y) ? (py[2] - avr_y) : (avr_y - py[2]);

	if ((dx > FILTER_LIMIT) || (dy > FILTER_LIMIT))
		return 0;

	avr_x = (px[1] + px[2]) / 2;
	avr_y = (py[1] + py[2]) / 2;

	dx = (px[3] > avr_x) ? (px[3] - avr_x) : (avr_x - px[3]);
	dy = (py[3] > avr_y) ? (py[3] - avr_y) : (avr_y - py[3]);

	if ((dx > FILTER_LIMIT) || (dy > FILTER_LIMIT))
		return 0;

	return 1;
}

static irqreturn_t s3c_ts_adc_isr(int irq, void *dev_id)
{
	static int x[4] = {0, 0, 0, 0}, y[4] = {0, 0, 0, 0}, cnt = 0;
	int data0, data1;

	data0 = s3c_adc_regs->adcdat0;
	data1 = s3c_adc_regs->adcdat1;

	/* 优化措施2: 如果这个时候触摸笔已经松开,此次结果丢弃 */
	if (data0 & (1<<15))
	{
		enter_wait_for_pen_down_mode();
		cnt = 0;
		x[0] = x[1] = x[2] = x[3] = 0;
		y[0] = y[1] = y[2] = y[3] = 0;

	        input_report_key(s3c_ts, BTN_TOUCH, 0);
	        input_report_abs(s3c_ts, ABS_PRESSURE, 0);
	        input_sync(s3c_ts);

		return IRQ_HANDLED;
	}
	else
	{		
		/* 优化措施3: 多次测量求平均值 */
		x[cnt] = data0 & 0x3ff;
		y[cnt] = data1 & 0x3ff;
		cnt++;
		if (cnt == 4)
		{
			/* 优化措施4: 软件过滤 */
			if (s3c_ts_filtering(x, y))
			{
				printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);		
		                input_report_abs(s3c_ts, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
		                input_report_abs(s3c_ts, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
		                input_report_key(s3c_ts, BTN_TOUCH, 1);
		                input_report_abs(s3c_ts, ABS_PRESSURE, 1);
		                input_sync(s3c_ts);
			}

			enter_wait_for_pen_up_mode();
			cnt = 0;
			x[0] = x[1] = x[2] = x[3] = 0;
			y[0] = y[1] = y[2] = y[3] = 0;
			
			/* 启动定时器 */
			mod_timer(&s3c_ts_timer, jiffies + HZ/100);				
		}
		else
		{
			enter_auto_xy_mode();
			start_adc();
		}
	}
	
	return IRQ_HANDLED;
}

static int s3c_ts_init(void)
{
	s3c_ts = input_allocate_device();

	__set_bit(EV_KEY, s3c_ts->evbit);
	__set_bit(EV_ABS, s3c_ts->evbit);
	__set_bit(BTN_TOUCH, s3c_ts->keybit);

	input_set_abs_params(s3c_ts, ABS_X, 0, 0x3FF, 0, 0);
	input_set_abs_params(s3c_ts, ABS_Y, 0, 0x3FF, 0, 0);
	input_set_abs_params(s3c_ts, ABS_PRESSURE, 0, 1, 0, 0);

	input_register_device(s3c_ts);

	clkcon = ioremap(0x4C00000C, 4);
	*clkcon |= (1<<15);
	iounmap(clkcon);
	
	gpgcon = ioremap(0x56000060, 4);
	*gpgcon |= 0xff000000;
	iounmap(gpgcon);
	
	s3c_adc_regs = ioremap(0x58000000, sizeof(struct s3c_adc_regs));
	
	request_irq(IRQ_TC, s3c_ts_pen_down_up_isr, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
	request_irq(IRQ_ADC, s3c_ts_adc_isr, IRQF_SAMPLE_RANDOM, "ts_adc", NULL);

	s3c_adc_regs->adccon = (1<<14) | (49 << 6);

	/* 优化措施1: 按下触摸屏后稍等一会再发出中断,为了让电压稳定 */
	s3c_adc_regs->adcdly = 0xffff;

	init_timer(&s3c_ts_timer);
	s3c_ts_timer.function = s3c_ts_timer_function;	
	add_timer(&s3c_ts_timer);

	/* 进入"Waiting for Interrupt Mode" */
	enter_wait_for_pen_down_mode();
	
	return 0;
}

static void s3c_ts_exit(void)
{
	free_irq(IRQ_TC, NULL);
	free_irq(IRQ_ADC, NULL);
	iounmap(s3c_adc_regs);
	input_unregister_device(s3c_ts);
	input_free_device(s3c_ts);
}

module_init(s3c_ts_init);
module_exit(s3c_ts_exit);

MODULE_LICENSE("GPL");

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值