• 基于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");
    
    


     

  • 相关阅读:
    chrome视频播放加速
    centos磁盘空间重新分配
    mseed2sac的安装和使用
    查找台站信息得到台站数据的网站
    java install
    CMT learning
    hosts持续更新
    what is SVD and how to calculate it
    google 镜像
    z变换
  • 原文地址:https://www.cnblogs.com/start530/p/3834397.html
Copyright © 2020-2023  润新知