linux驱动摸索-- 触摸屏

        在linux内核文件中,自带一个s3c2410_ts.c文件,实现了触摸屏的功能。现在参考该文件重新写一个驱动文件。

       在init函数中,

1.硬件相关配置:

    使能ADC时钟

	clk = clk_get(NULL, "adc");
	clk_enable(clk);

其中clk_get函数在/arm/plat-s3cxx/s3c2410-clock.c中定义

static struct clk init_clocks_disable[] = {
	{
		.name		= "nand",
		.id		= -1,
		.parent		= &clk_h,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_NAND,
	}, {
		.name		= "sdi",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_SDI,
	}, {
		.name		= "adc",
		.id		= -1,
		.parent		= &clk_p,
		.enable		= s3c2410_clkcon_enable,
		.ctrlbit	= S3C2410_CLKCON_ADC,
.......

进一步追踪,可以看到函数:

int s3c2410_clkcon_enable(struct clk *clk, int enable)
{
	unsigned int clocks = clk->ctrlbit;
	unsigned long clkcon;

	clkcon = __raw_readl(S3C2410_CLKCON);

	if (enable)
		clkcon |= clocks;
	else
		clkcon &= ~clocks;

	/* ensure none of the special function bits set */
	clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);

	__raw_writel(clkcon, S3C2410_CLKCON);

	return 0;
}

整个函数无非就是配置2440的CLKCON寄存器。在该配置中配置ADC(&Touch Screen) [15]这一位。


之后分配一个input_dev结构,这一系列过程可以参考前面的输入子系统,类似。


然后注册一个TC和ADC中断。

最后就是在中断中进行数据处理了。

代码如下:

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

#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>


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

static struct input_dev *ts_dev;
static struct timer_list ts_timer;
static struct clk	*adc_clock;


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

static volatile struct s3c_ts_regs *ts_regs;

static void ts_wait_down_mode(void)
{
	ts_regs->adctsc = 0xd3;
}

static void ts_wait_up_mode(void)
{
	ts_regs->adctsc = 0x1d3;
}

static void ts_measure_xy_mode(void)
{
	ts_regs->adctsc =(1<<2)|(1<<3); //S3C2410_ADCTSC_PULL_UP_DISABLE|/*(1<<3)*/AUTOPST;
}

static void ts_start_ad(void)
{
	ts_regs->adccon |= 1;
}

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 irqreturn_t ts_adc_handler(int irq, void *dev_id)
{
    int adcdat0,adcdat1;
	static int cnt=0;
	static int x[4],y[4];
    adcdat0 = ts_regs->adcdat0;
    adcdat1 = ts_regs->adcdat1;

/*	if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃
	{
        printk("ts_adc_handler TS_up \n");
		cnt = 0;
		input_report_abs(ts_dev, ABS_PRESSURE, 0);
		input_report_key(ts_dev, BTN_TOUCH, 0);
		input_sync(ts_dev);	
		ts_wait_down_mode();
	}
	else*/
	{
		//printk("TS_down \n");
		x[cnt] = adcdat0& 0x3ff;
		y[cnt] = adcdat1& 0x3ff;
		cnt++; 
		if(cnt >= 4)
		{
			//printk("TS_down \n");
			if(s3c_filter_ts(x,y))
				{
				//printk("adcdat0=%d,adcdat1=%d\n",(x[0]+x[1]+x[2]+x[3])>>2,\
					                       //    (y[0]+y[1]+y[2]+y[3])>>2 );
			    input_report_abs(ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
				input_report_abs(ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
				input_report_abs(ts_dev, ABS_PRESSURE, 1);
				input_report_key(ts_dev, BTN_TOUCH, 1);
				input_sync(ts_dev);	
	       }

	
			cnt=0;
			ts_wait_up_mode();
			/* 启动定时器处理长按/滑动的情况 */
		    mod_timer(&ts_timer, jiffies + HZ/100);
		}
		else
		{
			ts_measure_xy_mode();
			ts_start_ad();
		}


	}
	return IRQ_HANDLED;
}

static irqreturn_t ts_tc_handler(int irq, void *dev_id)
{
	if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15))
	{
		//printk("ts_tc_handler TS_up \n");
		input_report_abs(ts_dev, ABS_PRESSURE, 0);
		input_report_key(ts_dev, BTN_TOUCH, 0);
		input_sync(ts_dev);
		ts_wait_down_mode();
	}
	else
	{
		//down mode
		//printk("TS_down \n");
		ts_measure_xy_mode();
		ts_start_ad();

	}
	return IRQ_HANDLED;
}


static void ts_timer_handler(unsigned long dat)
{
	if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15))
	{
		/* 已经松开 */
	    input_report_abs(ts_dev, ABS_PRESSURE, 0);
		input_report_key(ts_dev, BTN_TOUCH, 0);
		input_sync(ts_dev);
		ts_wait_down_mode();
	}
	else
	{
		//down mode
		//printk("TS_down \n");
		ts_measure_xy_mode();
		ts_start_ad();

	}
	return;
}


static int mini2440_ts_init(void)
{
	int error;

	/*1.分配一个input_dev结构*/
	ts_dev = input_allocate_device();
	if (!ts_dev) 
	{
	    printk("input_allocate failed!\n");
		error = -ENOMEM;
	    return error;
	}
	
    /*2.设置 */
    /* 2.1 设置能产生哪类事件   */
	set_bit(EV_SYN, ts_dev->evbit);
	set_bit(EV_ABS, ts_dev->evbit);
	set_bit(EV_KEY, ts_dev->evbit);

	/*2.2 设置能产生这类操作的哪些事件 */ 
	set_bit(BTN_TOUCH, ts_dev->keybit);
	
	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);


	error = input_register_device(ts_dev);
	if (error) 
	{
		printk("input_register_device failed!\n");
		error = -ENOMEM;
	    return error;
	}

	/*3.硬件相关配置*/
	adc_clock = clk_get(NULL, "adc");
	if (!adc_clock) {
		printk(KERN_ERR "failed to get adc clock source\n");
		return -ENOENT;
	}
	clk_enable(adc_clock);
	
	ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs));


	ts_regs->adccon = (1<<14)|(0xff<<6);
	ts_regs->adcdly = 0xffff;
	ts_wait_down_mode();
	
	/*3.1初始化定时器*/
	init_timer(&ts_timer);
    ts_timer.function = ts_timer_handler;
	add_timer(&ts_timer);
	
	/*3.2中断配置*/

	error = request_irq(IRQ_ADC, ts_adc_handler, IRQF_SAMPLE_RANDOM,
		                "ts_adc", NULL);
	if (error) 
	{
		printk("request_irq failed!\n");
		error = -ENOMEM;
		return error;
	}
	error = request_irq(IRQ_TC, ts_tc_handler, IRQF_SAMPLE_RANDOM,
			"mini2440_ts", NULL);
	if (error) 
	{
		printk("IRQ_TC_irq failed!\n");
		error = -ENOMEM;
		return error;
	}	
	//ts_wait_down_mode();
    return 0;
}


static void mini2440_ts_exit(void)
{

	free_irq(IRQ_ADC,NULL);
    free_irq(IRQ_TC,NULL);
	del_timer(&ts_timer);
	//input_unregister_device(ts_regs);
	input_unregister_device(ts_dev);
	input_free_device(ts_dev);

}


/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(mini2440_ts_init);
module_exit(mini2440_ts_exit);

/* 描述驱动程序的一些信息 */

MODULE_LICENSE("GPL");

整个代码基本参考韦东山视频来写,在测试过程中发现在ADC采集过程中,已经up,数据丢弃,这样有些问题,不知mini2440的触摸屏什么问题,屏幕上方一小块区域内,在点击过程中就UP了,只好把那段滤波代码注释掉

static irqreturn_t ts_adc_handler(int irq, void *dev_id)
{
    int adcdat0,adcdat1;
	static int cnt=0;
	static int x[4],y[4];
    adcdat0 = ts_regs->adcdat0;
    adcdat1 = ts_regs->adcdat1;

/*	if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃
	{
        printk("ts_adc_handler TS_up \n");
		cnt = 0;
		input_report_abs(ts_dev, ABS_PRESSURE, 0);
		input_report_key(ts_dev, BTN_TOUCH, 0);
		input_sync(ts_dev);	
		ts_wait_down_mode();
	}
	else*/

最后经测试整个区域,触摸都可用

你可能感兴趣的:(mini2440,linux驱动)