基于ok6410的韦东山驱动视频简要分析--ts驱动

一、写ts驱动步骤(新手稍微看下即可,内容有点搞)

1、复制头文件;

2、写入口函数跟出口函数

3、分配一个input_dev结构体,

在头文件下插入:static struct input_dev *ts_dev;

在init中分配:ts_dev = input_allocate_device();

4、注册:在init中注册:input_register_device(ts_dev);

5、设置:

1、能产生哪类事件:

set_bit(EV_KEY, ts_dev->evbit);按键类事件

set_bit(EV_ABS, ts_dev->evbit);触摸屏事件

2、能产生这类事件的哪些事件:

set_bit(BTN_TOUCH, ts_dev->keybit);按键类里的触摸屏事件

input_set_abs_params(ts_dev, ABS_X, 0, 0xfff, 0, 0);

input_set_abs_params(ts_dev, ABS_Y, 0, 0xfff, 0, 0);

input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);

***********************************************************************************

所有的触摸屏程序几乎都是上面的框架,可直接采用

***********************************************************************************

6、写硬件相关的操作:

1、使能时钟:

clk = clk_get(NULL, "adc");  //使能时钟

clk_enable(clk);  /* PCLK_GATE[12]设为1 */

2、对各种寄存器进行设置

1、在init外定义一个结构体struct adc_regs {}把各种寄存器放进去,再定义一个指针

static struct adc_regs *adc_regs;

2、在init中对地址映射:adc_regs = ioremap(0x7E00B000, sizeof(struct adc_regs));

7、先只对三个寄存器进行设置:adc_regs->adccon = (1<<16) | (1<<14) | (65<<6);

adc_regs->adcdly = 0xffff;  //给它最大的delay延迟时间。

adc_regs->adcclrintpndnup = 0;

8、定义第一个中断:

1、request_irq(IRQ_TC, ts_pen_down_up_isr, IRQF_SHARED, "pen_down_up", 1)

2、然后进入该函数,在该中断下写入:enter_wait_for_pen_down();

3、在init外定义这个函数,在里面定义寄存器adc_regs->adctsc = 0xd3;进入等待中断方式,检测按下;再定义一个函数static void enter_wait_for_pen_up(void)检测松开

4、写上面那个中断的中断函数:static irqreturn_t ts_pen_down_up_isr(int irq, void *dev_id)

5、在中断中判断是按下还是松开,然后对其分别进行操作:

1如果是按下的(down),进入enter_measure_xy_mode();(自动的XY坐标转化模式),再之后启动start_adc()这个函数,这就需要添加两个函数:static void enter_measure_xy_mode(void)static void start_adc(void)

6、start_adc转换不能瞬间完成,我们也不能死等,它完成后会产生一个中断,so在init中再添加一个中断request_irq(IRQ_ADC, adc_isr, IRQF_SHARED, "adc", 1);写这个中断的中断函数

***************************************************************************************

优化措施

***************************************************************************************

9、在自动的xy坐标模式,在adc中断后,需要几毫秒时间去判断下是up还是down;

10、如果ADC完成之后,如果触摸笔已经松开,则丢弃此次结果;

11、多册测量,求平均值;

12、软件过滤;

***************************************************************************************

13、按下的时候如果是长按,滑动,那就需要启动定时器来定时。

1、static struct timer_list ts_timer;

2、在init中写入:

init_timer(&ts_timer);

ts_timer.expires  = 0;

ts_timer.function = ts_timer_function;

add_timer(&ts_timer);

3、在init外设置函数:static void ts_timer_function(unsigned long data)

4、在adc_irq函数中启动定时器:mod_timer(&ts_timer, jiffies + HZ/100); HZ为1s,所以定时10ms;

14、对ts_timer_function()这个函数进行设置;

15、上报事件:

input_event(ts_dev, EV_ABS, ABS_X, x);

input_event(ts_dev, EV_ABS, ABS_Y, y);

input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1);

input_event(ts_dev, EV_KEY, BTN_TOUCH, 1);

input_sync(ts_dev);

多放也不会有什么影响。

 **********************************************************************************************************************

 二、驱动程序

#include <linux/module.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/io.h>
#include <linux/clk.h>
#include <linux/delay.h>

static struct timer_list ts_timer;
static struct input_dev *ts_dev;

struct adc_regs {
	unsigned long adccon;
	unsigned long adctsc;
	unsigned long adcdly;
	unsigned long adcdat0;
	unsigned long adcdat1;
	unsigned long adcupdn;
	unsigned long adcclrint;
	unsigned long reserved;
	unsigned long adcclrintpndnup;
};

static struct adc_regs *adc_regs;

static void enter_wait_for_pen_down(void)
{
	adc_regs->adctsc = 0xd3;  //进入等待中断模式,检测按下
}

static void enter_wait_for_pen_up(void)
{
	adc_regs->adctsc = 0x1d3;//进入等待中断模式,检测松开
}

static void enter_measure_xy_mode(void)
{
	adc_regs->adctsc = ((1<<7) | (1<<6) | (1<<4) | (1<<3) | (1<<2));  //自动的 xy坐标模式
}

static void start_adc(void)
{
	adc_regs->adccon |= (1<<0);   //开始adc转换
}

static irqreturn_t ts_pen_down_up_isr(int irq, void *dev_id)
{
	unsigned long data0, data1;
	int down;

	data0 = adc_regs->adcdat0; //adcdat0存放X坐标
	data1 = adc_regs->adcdat1; //adcdat0存放y坐标

	down = (!(data0 & (1<<15))) && (!(data1 & (1<<15)));//第十五位判断是按下还是松开,0是down,1是up
	
	if (!down)	//if not down
	{
		//printk("pen up\n");
		enter_wait_for_pen_down();  //wait for down;

		/* 调用evdev_event: 保存,唤醒 */
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);  //0 is 松开
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(ts_dev);
	}
	else    //if down
	{
		//printk("pen down\n");
		//enter_wait_for_pen_up();

		/* 进入"自动测量x/y座标模式" */
		enter_measure_xy_mode();
		
		/* 启动ADC */
		start_adc();    //start ADC change
	}

	adc_regs->adcupdn   = 0;   //触摸笔向上或向下中断寄存器,0表示无触摸笔向下或向上
	adc_regs->adcclrint = 0;   //清楚ADC中断,在这寄存器上写入任何值都将清楚相关有效的中断
	adc_regs->adcclrintpndnup = 0;  //查无此寄存器澹?

	return IRQ_HANDLED;
}

static irqreturn_t adc_isr(int irq, void *dev_id)
{
	/* 为何不在这里立刻处理ADC到结果?
	 * 因为6410的ADC有个缺点: 
	 * 当ADC刚完成时, 必须等待若干ms, 
	 * 才能读取adcdat0,adcdata1来判断
	 * 当前的触摸屏是被按下还是松开
	 */
	 
	/* 启动定时器, 是为了不在中断处理函数里等待 */
	mod_timer(&ts_timer, jiffies + HZ/100); //HZ是1秒,so HZ/100 =10ms,10ms后再去判断按下还是松开

	enter_wait_for_pen_up();
		
	adc_regs->adcupdn   = 0;
	adc_regs->adcclrint = 0;
	adc_regs->adcclrintpndnup = 0;
	
	return IRQ_HANDLED;
}
***************************************************************************************
static irqreturn_t adc_isr_ok_old(int irq, void *dev_id)//没用到
{
	int x,y;
	int adcdat0,adcdat1;
	int down;

#if 1 /* in auto xy mode, after adc interrupt, have to wait several ms to test up/down */ 
	udelay(1000);
	udelay(1000);
	udelay(1000);
	udelay(1000);
#endif

	adcdat0 = adc_regs->adcdat0;
	adcdat1 = adc_regs->adcdat1;

	x = adcdat0 & 0xfff;
	y = adcdat1 & 0xfff;
	
	down = (!(adcdat0 & (1<<15))) && (!(adcdat1 & (1<<15)));
	//printk("adcdat0 = 0x%x, adc_dat1 = 0x%x\n", adcdat0, adcdat1);
	if (down)
	{
		//printk("enter_wait_for_pen_up\n");
		enter_wait_for_pen_up();

		//printk("x = %d, y = %d\n", x, y);
		input_event(ts_dev, EV_ABS, ABS_X, x);
		input_event(ts_dev, EV_ABS, ABS_Y, y);
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1); //1表示按下,0表示松开
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 1);
		input_sync(ts_dev);

		/* 启动定时器 */
		mod_timer(&ts_timer, jiffies + HZ/100);
	}
	else
	{
		//printk("enter_wait_for_pen_down\n");
		enter_wait_for_pen_down();
		
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(ts_dev);
	}
	
	adc_regs->adcupdn   = 0;
	adc_regs->adcclrint = 0;
	adc_regs->adcclrintpndnup = 0;
	
	return IRQ_HANDLED;
}

static void ts_timer_function_ok_old(unsigned long data)  //没用到
{
	/* 如果触摸笔已经松开, 就没必要再次启动ADC */
	/* 否则, 启动ADC */

	unsigned long data0, data1;
	int down;

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

	down = (!(data0 & (1<<15))) && (!(data1 & (1<<15)));
	
	if (down)
	{
		enter_measure_xy_mode();
		start_adc();
	}
	else
	{
		enter_wait_for_pen_down();
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(ts_dev);
	}
	
}
**********************************************************************
static void ts_timer_function(unsigned long data)
{
	/* 如果触摸笔已经松开, 就没必要再次启动ADC */
	/* 否则, 启动ADC */

	unsigned long data0, data1;
	int down;
	int x, y;

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

	down = (!(data0 & (1<<15))) && (!(data1 & (1<<15)));
	
	if (down)
	{
		x = data0 & 0xfff;
		y = data1 & 0xfff;

		input_event(ts_dev, EV_ABS, ABS_X, x);
		input_event(ts_dev, EV_ABS, ABS_Y, y);
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1);
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 1);
		input_sync(ts_dev);
		
		enter_measure_xy_mode();
		start_adc();
	}
	else
	{
		enter_wait_for_pen_down();
		input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);
		input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(ts_dev);
	}
	
}

static int ts_init(void)
{
	struct clk *clk;
	
	/* 1. 分配input_dev */
	ts_dev = input_allocate_device();
	
	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, ts_dev->evbit);//按键事件
	set_bit(EV_ABS, ts_dev->evbit);//触摸屏事件
	
	/* 2.2 能产生这类事件里的哪些事件 */
	set_bit(BTN_TOUCH, ts_dev->keybit);//按键事件中的触摸屏事件
	
	input_set_abs_params(ts_dev, ABS_X, 0, 0xfff, 0, 0);//这些内容可直接复制粘贴
	input_set_abs_params(ts_dev, ABS_Y, 0, 0xfff, 0, 0);
	input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);//压力。只有0和1,要么按下,要么松开。
	
	/* 3. 注册 */
	input_register_device(ts_dev);

	
	/* 4. 硬件相关 */
	adc_regs = ioremap(0x7E00B000, sizeof(struct adc_regs));

	clk = clk_get(NULL, "adc");  //使能时钟
	clk_enable(clk);  /* PCLK_GATE[12]设为1 */

	/* bit[16]   : 1 = 12-bit A/D conversion
	 * bit[14]   : 1 - enable A/D converter prescaler enable
	 * bit[13:6] : A/D converter prescaler value,
	 *             PCLK=66500000, adcclk=pclk/(n+1)
	 *             取值13, adclk=66.5MHz/14=4.75
	 * 
	 */
	adc_regs->adccon = (1<<16) | (1<<14) | (65<<6);/*貌似没有16位,个人觉得那个不用设置。*/

	adc_regs->adcdly = 0xffff;  //给它最大的delay延迟时间。

	adc_regs->adcclrintpndnup = 0;
	request_irq(IRQ_TC, ts_pen_down_up_isr, IRQF_SHARED, "pen_down_up", 1);
	request_irq(IRQ_ADC, adc_isr, IRQF_SHARED, "adc", 1);

	init_timer(&ts_timer);
	ts_timer.expires  = 0;
	ts_timer.function = ts_timer_function;
	add_timer(&ts_timer);

	/* 进入"wait for interrupt mode", 等待触摸笔按下或松开的模式 */
	enter_wait_for_pen_down();
	return 0;
}

static void ts_exit(void)
{
	del_timer(&ts_timer);
	free_irq(IRQ_TC, 1);
	free_irq(IRQ_ADC, 1);
	iounmap(adc_regs);
	input_unregister_device(ts_dev);
	input_free_device(ts_dev);
}

module_init(ts_init);
module_exit(ts_exit);
MODULE_LICENSE("GPL");


 

posted @ 2012-09-26 09:36  star特530  阅读(289)  评论(0编辑  收藏  举报