Chinaunix首页 | 论坛 | 博客
  • 博客访问: 724613
  • 博文数量: 105
  • 博客积分: 3532
  • 博客等级: 中校
  • 技术积分: 1328
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-20 18:57
文章分类

全部博文(105)

文章存档

2015年(1)

2014年(1)

2013年(7)

2012年(14)

2011年(21)

2010年(61)

分类: 嵌入式

2010-10-11 02:36:24

  1. 平台:
    • CPU:S3C2440(arm9)
    • 触摸屏:4线制电阻触摸屏
    • 操作系统:linux-2.6.34
    • 时钟:FCLK=399.65MHz;FCLK:HCLK:PCLK=1:4:8
    • 编译器:arm-none-eabi-gcc v4.4.1
    • 参考资料:s3c2440 datasheet,LINUX 设备驱动开发详解,网络
  2. 原理图:

    F-1

    F-2
  3. 四线制触摸屏原理:
    文件:4wires-ts.pdf
    大小:4154KB
    下载:下载

  4. 思路:
    初始化ADC的时钟和电源,配置TS(触摸屏)到中断等待模式,初始化定时器和中断;当有触笔按下的时候相应IRQ_TC中断函数,然后禁止IRQ_TC中断,延时25毫秒用于消除抖动,用自动测量X、Y模式,启动ADC转换,在IRQ_ADC中断函数中得到X、Y值存储在FIFO的缓存中(这样做的原因是避免在中断函数里面做太多事),然后再把触摸屏Y方向的一面的YP通参考电压(3.3v),YM高阻,XP和XM也为高阻,即把XP作为AN7,ADC转换AN7的值,如果有触笔按下,则AN7相当于直接和YP相连,所以采样值应该接近最大值(本人用>1000表示),如果没有按下,则不可能会有这么大的值出现,这样就可以检测出触笔是按下还是抬起了。如果是抬起了,把上次采样存入FIFO的数据作废,使能IRQ_TC,重新等待触笔按下;如果按下,如果FIFO里面的数据于现在的数据相差较大(我定以为相差5,这样做是避免画线时小范围的抖动干扰),则可用FIFO缓存里面的数据更新X、Y的值,如果你在画线过程中并不停顿,这个值是很容易超过的,如果停顿下来也可以避免没必要的数据更新,然后,延时10毫秒(也可以长些),再次采样X、Y数据进FIFO,之后也再次检测是否还是按下状态……如此循环,呵呵,原理不难,可是最初想和调试的时候真是很难,花了整整一天的时间才把它调到如此流畅

  5. 触摸屏驱动代码:ts.c,file_operations结构体里面的功能函数没写,因为这不是关键部分,所用到头文件 ypublic.h请参考 《MrY Linux驱动公用文件》

    //ts.c

    #include "ypublic.h"                    //import the public header file


    #define YDRIVER_NAME "TS_TEST"     //device name

    #define YMAJOR 100                //device major

    MODULE_LICENSE("Dual BSD/GPL");    //license

    void adc_task(ulong p);            //adc sub irq routine

    DECLARE_TASKLET(_adc_task,adc_task,0);    //the tasklet for adc irq


    #define ST_ADC_TEST 1            //a state for test stylus is up or down    

    #define ST_ADC_AUTOXY 2            //a state for measure x and y position


    typedef struct _t_adc_dev{            //my device strtuct

        //struct cdev dev;            //my device,if you want add open\close...,need initialize this member

        t_adcregs_p padcreg;            //adc is register pointer

        t_clockreg_p pclkreg;            //clock and power are register pointer

        struct timer_list timer;            //timer

        uint counter;                    //counter

        t_fifo fifo;                    //an FIFO buffer

        uint x;                        //current     X

        uint y;                        //current     y

        uint adc_state;                //adc state

    }t_adc_dev;
    t_adc_dev *_padcdev;                //a global device pointer



    void adc_task(ulong p)                //adc sub irq routine

    {
        uint x,y;
        x=fifo_pop(&_padcdev->fifo);        //get x from FIFO buffer

        y=fifo_pop(&_padcdev->fifo);        //get y from FIFO buffer

        if(abs(_padcdev->x-x)>5||abs(_padcdev->y-y)>5)    //

        {
            if(_padcdev->x==0xfff)        //first pointer whit stylus down

                printk(KERN_INFO"have stylus down\n");
            _padcdev->x=x;            //update current x

            _padcdev->y=y;            //update current y

            printk(KERN_INFO"%u : ( %u , %u )\n",
                _padcdev->counter++,x,y);

        }
        _padcdev->timer.data=1;            //timer for measure x and y position

        _padcdev->timer.expires=MS(10);    //delay 10 ms

        add_timer(&_padcdev->timer);    //add timer

    }

    irqreturn_t on_adc(int irq,void *id)    //adc irq routine

    {
        uint x,y;
        if(_padcdev->adc_state==ST_ADC_AUTOXY)    //for measure x and y position

        {
            x=_padcdev->padcreg->ADCDAT0&0x3ff;    //get X

            y=_padcdev->padcreg->ADCDAT1&0x3ff;    //get Y

            if(x!=0&&y!=0)                            //stylus was down or not

            {                                    //stylus was down

                fifo_push(&_padcdev->fifo,x);            //push X value to FIFO buffer

                fifo_push(&_padcdev->fifo,y);            //push Y value to FIFO buffer

                _padcdev->timer.data=2;                //timer for test stylus was down or not

                _padcdev->timer.expires=MS(1);        //delay 1 ms

                add_timer(&_padcdev->timer);        //add timer

            }
            else{                                    //stylus isn't down

                if(_padcdev->x!=0xfff)                //stylus was up or not

                    printk(KERN_INFO"stylus up\n");
                _padcdev->padcreg->ADCTSC=0xd3;    //Waiting for Interrupt Mode

                enable_irq(IRQ_TC);                //enable touch screen interrupt

            }
            
        }
        else if(_padcdev->adc_state==ST_ADC_TEST)    //for test stylus is up or down

        {
            if((_padcdev->padcreg->ADCDAT0&0x3ff)<1000)
            {                                    //stylus was not down

                fifo_flush(&_padcdev->fifo,2);            //clear the dataes of pushed on current

                if(_padcdev->x!=0xfff)                //stylus was up

                    printk(KERN_INFO"stylus up\n");
                _padcdev->padcreg->ADCTSC=0xd3;    //Waiting for Interrupt Mode

                enable_irq(IRQ_TC);                //enable touch screen interrupt

            }
            else
            {
                tasklet_schedule(&_adc_task);            //run sub irq routine on free time

            }
        }
        return IRQ_HANDLED;
    }

    irqreturn_t on_tc(int irq,void* id)
    {
        disable_irq_nosync(irq);                //disable touch screen interrupt

        _padcdev->y=0xfff;                    //initialize X andY for a impossible value

        _padcdev->x=_padcdev->y;            //X and Y region was (0-0x3ff)

        _padcdev->padcreg->ADCTSC&=0xffc;    //clear INT_TC bit by change the mode to no operation mode

        _padcdev->timer.data=1;                //timer for measure x and y position

        _padcdev->timer.expires=MS(25);        //delay 25 ms

        add_timer(&_padcdev->timer);        //add timer


        return IRQ_HANDLED;                //irq return

    }

    void on_timer(ulong d)
    {
        switch(d){
            case 1:
            {    
                _padcdev->padcreg->ADCTSC=0x90|SETB(3)|SETB(2);    // XP pull-up disable and auto sequential measurement of X-position, Y-position

                _padcdev->padcreg->ADCCON|=0x1;                //adc conversion start

                _padcdev->adc_state=ST_ADC_AUTOXY;            //adc conversion for measure x and y

                break;
            }
            case 2:
            {
                _padcdev->adc_state=ST_ADC_TEST;                //adc conversion for test stylus was down or not

                _padcdev->padcreg->ADCTSC=SETB(4);                //YP output heigh level,XP was used as analog input, and no operation mode

                _padcdev->padcreg->ADCCON|=setNB(7,_padcdev->padcreg->ADCCON,3,5)|0x1;//start adc conversion from XP

                break;
            }

        }
    }

    int __init init_adc(void)
    {
        int rs=0;                                    //result                                        

        dev_t dev=MKDEV(YMAJOR,0);                //device ID

        _padcdev=kmalloc(sizeof(t_adc_dev),GFP_KERNEL);//malloc space for device

        if(_padcdev==NULL)
        {
            rs=-ENOMEM;                            //no memery

            goto fail1;
        }
        fifo_init(&_padcdev->fifo);                    //initialize FIFO buffer struct

        
        _padcdev->pclkreg=(t_clockreg_p)yioremap(CLOCKBASE,sizeof(t_clockreg),"clkio");    //remap clock and power registers base address

        if(_padcdev->pclkreg==NULL){
            rs=-EBUSY;                            //clock register was used by other device

            goto fail2;

        }
        else{
            _padcdev->pclkreg->CLKCON|=SETB(15);    //enable adc clk

            yiounmap(CLOCKBASE,_padcdev->pclkreg,sizeof(t_clockreg));    //release the right that uding clock register

        }
        
        _padcdev->padcreg=(t_adcregs_p)yioremap(ADCBASE,sizeof(t_adcregs),"adcio");        //remap adc registers base address

        if(_padcdev->padcreg==NULL){
            rs=-EBUSY;                            //adc register was used by other device

            goto fail2;

        }
        rs=register_chrdev_region(dev,1,YDRIVER_NAME);//register char device

        if(rs<0){    
            rs=-EBUSY;                            //register char device failed                        

            goto fail3;

        }

        rs=request_irq(IRQ_ADC,on_adc,IRQF_DISABLED,"adc_int",NULL);    //request ADC interruput

        if(rs)goto fail4;                            //request ADC interrupt failed

        rs=request_irq(IRQ_TC,on_tc,IRQF_DISABLED,"tc_int",NULL);        //request touch screen interrupt

        if(rs)goto fail5;                            //request touch screen interrupt failed

        
        _padcdev->counter=0;                        //initialize the tounter to zero

        init_timer(&_padcdev->timer);                    //initialize timer

        _padcdev->timer.function=&on_timer;            //specify the timer is routine

        
        printk(KERN_INFO"TS driver installed\n");        //tip driver install success

        _padcdev->padcreg->ADCCON=(0x1<<14)|(0x13<<6);    //ADC clock=PCLK/(0x13+1)

        _padcdev->padcreg->ADCTSC=0xd3;            //Waiting for Interrupt Mode

        return rs;
    fail5:
        free_irq(IRQ_ADC,NULL);                    //free adc irq

    fail4:
        unregister_chrdev_region(MKDEV(YMAJOR,0),1);    //unregister the char device

    fail3:
        yiounmap(ADCBASE,_padcdev->padcreg,sizeof(t_adcregs));    //unremap ADC registeres

        
    fail2:
        kfree(_padcdev);                            //free device space

    fail1:
        return rs;
    }

    void __exit exit_adc(void)
    {
        free_irq(IRQ_TC,NULL);        //free touch screen irq                    

        free_irq(IRQ_ADC,NULL);    //free adc irq

        del_timer(&_padcdev->timer);    //delete timer

        yiounmap(ADCBASE,_padcdev->padcreg,sizeof(t_adcregs));    //unremap ADC registeres

        kfree(_padcdev);            //free device space

        unregister_chrdev_region(MKDEV(YMAJOR,0),1);    //unregister the char device

    }

    module_init(init_adc);    //register the module for initialize driver

    module_exit(exit_adc);    //register the module for release driver

    MODULE_AUTHOR("MrY");    //driver's author

    MODULE_VERSION("0.1");    //driver's version

  6. 干脆makefile文件也贴了吧,呵呵

    # If KERNELRELEASE is defined, we've been invoked from the

    # kernel build system and can use its language.

    #arm-none-eabi-

    CROSS_COMPILE ?=
    AS = $(CROSS_COMPILE)as
    LD = $(CROSS_COMPILE)ld
    CC = $(CROSS_COMPILE)gcc
    ifneq ($(KERNELRELEASE),)

        obj-m := ts.o
        module-objs :=
    # Otherwise we were called directly from the command

    # line; invoke the kernel build system.

    else

        #KERNELDIR ?= /lib/modules/$(shell uname -r)/build

        KERNELDIR ?= /media/DISK1_VOL3/PROGRAM_LINUX/linux-2.6.34-cd
        PWD := $(shell pwd)
    default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    clear:
        rm -rf *.o *.mod.* *.order *.symvers *.ko *.markers *~ .*
    app:
        arm-linux-gcc -o main main.c

    endif

  7. 运行截图

    F-3.编译

    F-4.安装、点击、画线、点击、参看模块、卸载
  8. 声明:
    驱动部分的注释用的全是英文,不是想显摆自己的英文,是在linux里面用wine上的source insight编辑器写中文是乱码,所以没办法。linux对中文的支持是有点龌龊
阅读(1997) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~