触摸屏驱动

输入子系统体系
• 核心层: Linux_Dir/drivers/input/input.c(提供最核心函数)
• 设备事件层: Linux_Dir/drivers/input/evdev.c(提供handler)
提供输入设备产生的原始数据并上报给应用程序,这适用于
所有输入设备, 该触摸屏也不例外
编写基于输入子系统的驱动时只需:
1.分配input_dev
2.设置能产生什么事件和这类事件的哪些事件
3. input_register_device注册
4. 硬件操作

触摸屏硬件原理

在这里插入图片描述
在这里插入图片描述

1.分配input_dev
2.设置能产生什么事件和这类事件的哪些事件
3. input_register_device注册

/* 1. 分配一个input结构体 */
	 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);
	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.注册 */
	 input_register_device(s3c_ts_dev);

现在已将input_dev结构体设置好了,接下来就是对触摸屏硬件的设置了,而在设置硬件之前要先向大家介绍一下触摸屏的使用过程:
触摸屏的使用过程分析
1.按下触摸屏产生中断
2.在触摸屏中断处理程序中,将通过ADC采集的电压值转化为XY方向的坐标值,然后启动ADC。
3.AD转化结束产生ADC中断。
4.在ADC中断处理函数中上报事件,并启动定时器(用于处理长按和滑动事件)
5.定时器时间到,跳到步骤2继续往下执行
直到松开触摸屏
下面是硬件寄存器和中断的设置,为上面触摸屏的操作做准备,程序为:

	 /* 4.硬件相关操作 */
	 /* 4.1 使能时钟 */
	 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 = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
	 /*bit[14]	PRSCEN :A/D converter 预分频使能
	  * 				1 = Enable
	  *bit[13:6] PRSCVL : A/D converter prescaler value 预分频系数
	  * 				49 ADCLK = PCLK/(49+1) = 50MHZ / (49+1) = 1MHZ
	  *bit[0] ENABLE_START ADC开始转化使能 这里先设为0,后面还会进行设置
	  *
	  *
	  */
	 s3c_ts_regs->adccon = (1<<14) | (49<<6) | (0<<0);


	 /* 4.3 注册中断,当按下触摸屏时会产生中断 */
	request_irq(IRQ_TC,pen_down_up_irq,IRQF_SAMPLE_RANDOM,"ts_pen",NULL);

	 /* 4.5 当ADC转化结束后,会产生ADC转化结束中断 */
	request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM,"adc",NULL);
	enter_wait_pen_down_mode();

上面程序中的s3c_ts_regs是触摸屏寄存器的一个总的集合,并将其放入一个结构体中,代码为:

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

上面是准备代码,下面我们将模仿触摸屏按下的过程来分析代码:
假设我们程序已经写好,并且在开发版上运行正常,当你拿起笔按在触摸屏上的一点,因为在之前我们已经注册了IRQ_TC中断:

 /* 4.3 注册中断,当按下触摸屏时会产生中断 */
	request_irq(IRQ_TC,pen_down_up_irq,IRQF_SAMPLE_RANDOM,"ts_pen",NULL);

所以当你按下屏幕时触发中断,从而处理相应的中断处理函数:pen_down_up_irq,详细的代码为:

static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{
	if(s3c_ts_regs->adcdat0 & (1<<15))//判断屏幕是否还是处于按下状态
	{
		//printk("pen up\n");//处于抬起状态,上报抬起事件
		enter_wait_pen_down_mode();  //进入等待按下模式
		input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
		input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
		input_sync(s3c_ts_dev);
	}
	else
	{
		//此时屏幕还处于按下状态
		/* 4.4 在中断中启动ADC,转化XY坐标 */
		//printk("pen down\n");
		//enter_wait_open_up_mode();
		enter_measure_xy_mode();
		start_adc();
	}
	return IRQ_HANDLED;
}

如上面程序所示,此时如果你抬起了你的笔,程序将上报事件,而如果你还处于按下状态,我们将继续跟着程序走:
由于你已经注册了ADC中断:

/* 4.3 注册中断,当按下触摸屏时会产生中断 */
	request_irq(IRQ_TC,pen_down_up_irq,IRQF_SAMPLE_RANDOM,"ts_pen",NULL);

而上面的程序已经开启了ADC:start_adc();
所以我们将进入ADC的中断处理函数adc_irq,详细的代码:

static irqreturn_t adc_irq(int irq, void *dev_id)
{
	static int cnt = 0;
	int adcdat0, adcdat1;
	static int x[4], y[4];
	
	/* 优化措施2
	 * 如果ADC完成之后,发现触摸笔已经松开了,则丢弃此次结果
	 */
	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
	{
	//优化措施3  多次测量求平均值
		x[cnt] = adcdat0&0x3ff;
		y[cnt] = adcdat1&0x3ff;
		++cnt;
		if(cnt==4)
		{
			//优化措施4 软件过滤
			if(s3c_filter_ts(x, y))
			{
				/* 4.6 在ADC中断处理函数中上报(input_event),启动定时器 */
				//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_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
				input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
				input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
				input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
				input_sync(s3c_ts_dev);

			}
			enter_wait_pen_up_mode();
			cnt=0;

			/* 启动定时器处理长按和滑动情况 */
			mod_timer(&ts_timer, jiffies + HZ/100);
		}
		else
		{
			enter_measure_xy_mode();
			start_adc();
		}
		
	}	
	return IRQ_HANDLED;
}

而通过上面的程序,我们知道了触摸屏的处理过程,此时,触摸屏驱动已经基本写好了。而下面就是对特殊的处理,如当长按或者滑动时,此时我们将通过定时器来处理这样的问题,假如此时你还没有松手,触摸屏还处于按下状态。(你可能觉得怎么这么长时间了笔还没抬起啊,其实上面分析的可能麻烦,但在程序中上面的代码还是跑的很快的)。那么,我们接下来就是对定时器的分析了,因为在上文中我们启动了定时器:mod_timer(&ts_timer,jiffies + HZ/100);
当定时时间(此处我们设置定时时间为10ms)到时,进入定时器处理函数:

static void s3c_ts_timer_function(unsigned long data)
{
	if(s3c_ts_regs->adcdat0 & (1<<15))
	{
		//已经松开	
		/* 4.7 定时器时间到,在定时器处理函数中,重复4.4的步骤(用于处理长按和滑动) */
		enter_wait_pen_down_mode();
		input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
		input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
		input_sync(s3c_ts_dev);
	}
	else
	{
		//继续测量XY坐标
			/* 4.4 在中断中启动ADC,转化XY坐标 */
		enter_measure_xy_mode();
		start_adc();
	}
}

在上面的程序中,可以看出定时时间到后定时器又会启动ADC然后将接着步骤2继续执行,一直这样循环,直到松开,而这样也就可以处理长按或者滑动了,当你长按或者滑动时,定时时间到后会采集你所在的位置的XY坐标,然后上报,然后继续进入定时器,继续进入ADC,继续采集上报,直到你松开触摸屏。

好了,上面就是触摸屏驱动的大致流程和代码分析了。

下面是进入不同模式和滤波的函数

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;
}

static void enter_measure_xy_mode(void)
{
	s3c_ts_regs->adctsc = (1<<3) | (1<<2);
}
static void	start_adc(void)
{
	s3c_ts_regs->adccon |= (1<<0);
}




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;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值