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

发布者:NatureLover最新更新时间:2024-11-06 来源: cnblogs关键字:ok6410  头文件 手机看文章 扫描二维码
随时随地手机看文章

一、写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


#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


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 upn');

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 downn');

//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%xn', adcdat0, adcdat1);

if (down)

{

//printk('enter_wait_for_pen_upn');

enter_wait_for_pen_up();


//printk('x = %d, y = %dn', 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_downn');

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');


关键字:ok6410  头文件 引用地址:基于ok6410的韦东山驱动视频简要分析--ts驱动

上一篇:基于ok6410的韦东山驱动视频简要分析--USB驱动
下一篇:基于ok6410的韦东山驱动视频简要分析--lcd驱动

小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved