linux3.4.2 触摸屏驱动

目录

 

1  触摸屏硬件连接

2  触摸屏检测原理

3  触摸屏接口模式

4  触摸屏驱动编程流程图​

5  触摸屏驱动程序

6 触摸屏驱动程序测试


1  触摸屏硬件连接

2440开发板使用的是4线触摸屏,该4线连接在2440的AIN4~AIN7引脚上
YM: (Y Minus)触摸屏的Y坐标的负线
YP : (Y Power)触摸屏的Y坐标的正线
XM: (Y Minus)触摸屏的Y坐标的负线
XP : (Y Power)触摸屏的Y坐标的正线

linux3.4.2 触摸屏驱动_第1张图片

2  触摸屏检测原理

测X坐标方向时:XP接3.3V , XM接0V, YP与ADC检测引脚相连,当按压时就可以检测到YP电压,根据该电压可计算x坐标。
测Y坐标方向时:   YP接3.3V , YM接0V, XP与ADC检测引脚相连,当按压时就可以检测到XP电压,根据该电压可计算x坐标。

linux3.4.2 触摸屏驱动_第2张图片

3  触摸屏接口模式

  • 分离的 X/Y 方向转换模式

    触摸屏控制器可以工作在两个转换模式之一。方向转换模式如下方法操作。X 方向模式写 X 方向转换数据到
    ADCDAT0,故触摸屏接口产生中断源给中断控制器。Y 方向模式写 Y 方向转换数据到 ADCDAT1,故触摸屏接口产
    生中断源给中断控制器。

  • 自动(顺序)X/Y 方向转换模式

    自动(顺序)X/Y 方向转换模式操作如下。触摸屏控制器顺序变换触摸 X 方向和 Y 方向。在自动方向转变模式中触摸
    控制器在写入 X 测量数值到 ADCDAT0 和写入 Y 测量数值到 ADCDAT1 后,触摸屏接口产生中断源给中断控制器。

  • 等待中断模式

   当笔尖落下时触摸屏控制器产生中断(INT_TC)信号。等待中断模式设置值为 rADCTSC=0xd3; // XP_PU,XP_Dis,
XM_Dis,YP_Dis,YM_En触摸屏控制器产生中断信号(INT_TC)后,必须清除等待中断模式。(XY_PST 设置到无操作模式)

4  触摸屏驱动编程流程图
linux3.4.2 触摸屏驱动_第3张图片

5  触摸屏驱动程序

ts_drv.c文件

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include //包含了GPIO相关宏
#include 
#include 
#include 

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

static struct input_dev *touch_dev;
static volatile struct touch_regs* ts_regs;
static struct timer_list touch_timer;

/*进入等待中断模式:
*等待触摸笔按下中断,开始手动测量X坐标,电压从YM端点进行检测
*/
static void enter_wait_down_mode(void)
{
	//检测笔尖抬起中断|YM输出使能|YP输出禁止|XP输出禁止|手动测量X方向
	ts_regs->adctsc = S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | 
	                  S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_XY_PST(3);
}

/*进入等待中断模式:
*等待触摸笔松开中断,开始手动测量X坐标,电压从YM端点进行检测
*/
static void enter_wait_up_mode(void)
{
	//检测笔尖抬起中断|YM输出使能|YP输出禁止|XP输出禁止|手动测量X方向
	ts_regs->adctsc = S3C2443_ADCTSC_UD_SEN | S3C2410_ADCTSC_YM_SEN |
	                  S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | 
					  S3C2410_ADCTSC_XY_PST(3);
}

/*进入自动测量xy模式
*自动转换xy模式:x,y都转换完成后才产生ADC中断
*分离xy转换模式:x转换完成产生一次中断,y转换完成产生一次中断
*/
static void enter_measure_xy_mode(void)
{
	//XP上拉禁止 | 自动顺序x方向和Y方向测量
	ts_regs->adctsc = S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_AUTO_PST;
}

static void start_adc(void)
{
	//时能ADC转换启动,且此位在启动后自动清0
	ts_regs->adccon |= S3C2410_ADCCON_ENABLE_START;
}

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


static void touch_timer_function(unsigned long data)
{
	if(ts_regs->adcdat0 & S3C2410_ADCDAT1_UPDOWN )//触摸笔抬起
	{
		input_report_abs(touch_dev, ABS_PRESSURE, 0);
		input_report_key(touch_dev, BTN_TOUCH, 0);
		input_sync(touch_dev);
		enter_wait_down_mode();
	}
	else  //触摸笔按下
	{
		//测量xy坐标
		enter_measure_xy_mode();
		start_adc();
	}
}

/*触摸笔按下,松开中断处理函数*/
static irqreturn_t touch_irq_handler(int irq, void *dev_id)
{
	if(ts_regs->adcdat0 & S3C2410_ADCDAT1_UPDOWN  )//触摸比抬起
	{
		enter_wait_down_mode();
	}
	else//触摸比按下
	{
		enter_measure_xy_mode();
		start_adc();
	}
	
	return IRQ_HANDLED;
}

static irqreturn_t adc_irq_handler(int irq, void *dev_id)
{
	static int cnt = 0;
	int adcdat0,adcdat1;
	static int x[4],y[4];
	
	/*优化2: 如果ADC完成时,发现触摸笔已经松开, 则丢弃本次结果*/
	adcdat0 = ts_regs->adcdat0;
	adcdat1 = ts_regs->adcdat1;
	if(ts_regs->adcdat0 & S3C2410_ADCDAT1_UPDOWN)//触摸笔抬起
	{
		//Stylus up state
		cnt = 0;
		input_report_abs(touch_dev, ABS_PRESSURE, 0);
		input_report_key(touch_dev, BTN_TOUCH, 0);
		input_sync(touch_dev);
		enter_wait_down_mode();
	}
	else//触摸笔按下
	{
		/*优化3:多次测量,求平均值*/
		x[cnt] = ts_regs->adcdat0 & 0x3ff;
		y[cnt] = ts_regs->adcdat1 & 0x3ff;
		++cnt;
		if(cnt >= 4)
		{
			/*优化4:软件过滤*/
			if(s3c_filter_ts(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(touch_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
				input_report_abs(touch_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
				input_report_abs(touch_dev, ABS_PRESSURE, 1);
				input_report_key(touch_dev, BTN_TOUCH, 1);
				input_sync(touch_dev);
			}
			cnt = 0;//为了能够处理滑动事件,
			enter_wait_up_mode();
			mod_timer(&touch_timer, jiffies + HZ/100);
		}
		else
		{
			enter_measure_xy_mode();
			start_adc();
		}
	}
	
	return IRQ_HANDLED;
}


static int touch_init(void)
{
	struct clk*  clk;
    int ret;
	//1.分配一个input_device结构体
	touch_dev = input_allocate_device();
	
	//2.设置
	//2.1 事件类型: 设置evbit
	set_bit(EV_KEY, touch_dev->evbit);//按键事件
	set_bit(EV_ABS, touch_dev->evbit);//绝对位移事件
	//2.2 具体事件
	set_bit(BTN_TOUCH ,touch_dev->keybit);
	input_set_abs_params(touch_dev, ABS_X, 0, 0x3FF, 0, 0);
	input_set_abs_params(touch_dev, ABS_Y, 0, 0x3FF, 0, 0);
	input_set_abs_params(touch_dev, ABS_PRESSURE, 0, 1, 0, 0);
	
	//3.注册
	ret = input_register_device(touch_dev);
	if(ret < 0){
        printk("failed to register input device\n");
		ret = -EIO;
        return ret;
    }

	//4.硬件相关
	//4.1 使能时钟CLKCON
	clk = clk_get(NULL, "adc");
	clk_enable(clk);
	//4.2 设置相关寄存器
	ts_regs = ioremap(S3C24XX_PA_ADC, sizeof(struct touch_regs));
    /*bit[14]:PRSCEN = 1 //预分频使能
	*bit[13:6]:PRSCVL =49 //预分频系数, ADCCLK=PCLK/(49+1) = 1MHz
	*bit[0]: ENABLE_START = 0 //A/D conversion starts by enable
	*/
	ts_regs->adccon = S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(49);
   	ret = request_irq(IRQ_TC, touch_irq_handler, IRQF_SAMPLE_RANDOM, "touch_pen", NULL);//注册一个触摸中断
   	if (ret) {
        printk("request IRQ_TC error\n");
        return ret;
    }
    ret = request_irq(IRQ_ADC, adc_irq_handler, IRQF_SAMPLE_RANDOM, "adc", NULL);
    if (ret) {
        printk("request IRQ_TC error\n");
        return ret;
    }
	/*优化1:设置ADCDLY为最大值*/
	ts_regs->adcdly = 0xFFFF;
	/*优化5:使用定时器处理长按滑动*/
	init_timer(&touch_timer);
	touch_timer.function = touch_timer_function;
	add_timer(&touch_timer);
	
	enter_wait_down_mode(); 
	return 0;
}

static void touch_exit(void)
{
	free_irq(IRQ_TC, NULL);
	free_irq(IRQ_ADC, NULL);
	input_unregister_device(touch_dev);
	input_free_device(touch_dev);
	iounmap(ts_regs);
	del_timer(&touch_timer);
//	clk_disable(clk);
}

module_init(touch_init);
module_exit(touch_exit);
MODULE_LICENSE("GPL");

makefile文件

KERN_DIR = /home/ningjw/linux-3.4.2

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= ts_drv.o

6 触摸屏驱动程序测试

  • 去掉自带的触摸屏驱动程序,重新make uImage,并烧录uImage
    -> Device Drivers                                                                      
       -> Input device support              
         -> Touchscreens
            ->Samsung S3C2410/generic touchscreen input driver

如果不去掉触摸屏会报如下错误:
request IRQ_TC error
insmod: can't insert 'ts_drv.ko': Device or resource busy

  • 测试方式1:执行hexdump /dev/input/event0 命令,然后点击触摸屏可以看到有如下类似输出,则驱动正常
0000000 0235 0000 4877 0008 0003 0000 026a 0000                                   
0000010 0235 0000 4889 0008 0003 0001 0201 0000                                   
0000020 0235 0000 488d 0008 0003 0018 0001 0000                                   
0000030 0235 0000 4892 0008 0001 014a 0001 0000       
  • 测试方式2:执行/usr/local/tslib/bin 下的ts_test文件(需要移植tslib),点击触摸屏,有如下输出
    /usr/local/tslib/bin # ./ts_test
    1067.438427:     43    165      1                                                 
    1067.455615:     44    165      1                                                 
    1067.485053:     44    166      0                                                 
    1068.963966:    176    142      1 

     

你可能感兴趣的:(嵌入式linux)