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

全部博文(105)

文章存档

2015年(1)

2014年(1)

2013年(7)

2012年(14)

2011年(21)

2010年(61)

分类: 嵌入式

2010-08-29 13:23:30

  1. 操作系统:linux-2.6.34
    硬件平台:pw2440(cpu is s3c2440)
    编译器:arm-none-eabi-gcc v4.4.1
  2. 电路图
  3. 思路:上面5个管脚设置为中断模式,下降沿触发中断,下面4个管脚设置为输出模式,输出低电平,当有按键按下后,会产生相应中断,此时禁止中断,并用定时器延时25ms来消抖,@1 然后判断是否有按键按下,有的话再分别让下面4个管脚输出高电平,如果发生中断的管脚也能获得高电平,则能检测持出是哪个按键按下了;继续用定时器延时100ms,重复@1 起的步骤,判断按键是不是还处于按下的状态,如果是,继续重复,如果不是,则证明按键放开,设置刚才发生中断的管脚回到中断模式,并使能中断
  4. 公共头文件 ypublic.h

    #ifndef __Y_PUBLIC__
    #define __Y_PUBLIC__

    #include <linux/ioport.h>
    #include <linux/timer.h>

    #include <asm/io.h>

    #define SETB(n) (1<<(n))
    #define CLRB(n) (~ SETB(n))
    #define SETN(v,n) (v<<n)
    #define ARRAYSIZE(ar) (sizeof(ar)/sizeof(ar[0]))

    //#define MS(n) (HZ*(n)/1000)

    #define PI 3.1415927
    #define uint unsigned int
    #define uchar unsigned char
    #define ulong unsigned long
    #define byte uchar

    //io

    #define Y2440_IO_IN 0
    #define Y2440_IO_OUT 1
    #define Y2440_IO_EINT 2
    #define Y2440_IO_OUT_H 1
    #define Y2440_IO_OUT_L 0

    #define SETNB(v,d,n,w) \
        ({\
        unsigned long rs;\
        unsigned int v1=v;\
        unsigned int d1=d;\
        unsigned int n1=n;\
        unsigned int w1=w;\
        rs=0xffffffff;\
        rs=rs<<(32-n1-w1);\
        rs=rs>>(32-w1);\
        rs=~(rs<<n1);\
        rs=d&rs|(v1<<n1);\
        rs;\
        })

    uint setNB(uint v,uint d,uint n,uint w)
    {
        uint rs;
        rs=0xffffffff;
        rs=rs<<(32-n-w);
        rs=rs>>(32-w);
        rs=~(rs<<n);
        rs=(d&rs)|(v<<n);
        return rs;
    }
    static void* yioremap(ulong start,ulong n,char name[])
    {
        if(request_mem_region(start,n,name)==NULL)return NULL;
        return ioremap(start,n);
    }

    static void yiounmap(ulong start,ulong n)
    {
        iounmap((void*)start);
        release_mem_region(start,n);
    }

    static ulong fortimems(ulong ms)
    {
        return (jiffies+HZ*ms/1000);
    }

    #define MS(n) fortimems(n)
    #endif


  5. 驱动程序 key2.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/cdev.h>
    #include <linux/ioport.h>
    #include <linux/fs.h>
    #include <linux/slab.h>
    #include <linux/interrupt.h>

    #include <mach/irqs.h>

    #include <asm/io.h>
    #include "ypublic.h"


    MODULE_LICENSE("Dual BSD/GPL");
    #define YMAJOR 100
    #define YMINOR 1
    #define YDEVICENAME "uartdriver"
    #define YBUFSIZE 10

    //io regs

    #define GPABASE 0x56000000 //0

    #define GPBBASE 0x56000010 //1

    #define GPCBASE 0x56000020 //2

    #define GPDBASE 0x56000030 //3

    #define GPEDASE 0x56000040 //4

    #define GPFBASE 0x56000050 //5

    #define GPGBASE 0x56000060 //6

    #define GPHBASE 0x56000070 //7


    #define IOA 0
    #define IOB 1
    #define IOC 2
    #define IOD 3
    #define IOE 4
    #define IOF 5
    #define IOG 6
    #define IOH 7


    #define GPIOCONF(b) (b)
    #define GPIODAT(b) ((b)+4)
    #define GPIOUP(b) ((b)+8)



    typedef struct _YCDEV{
        struct cdev dev;
        uchar x,y;            //记录当前按键的位置

        uchar pre_states[20];//记录按键的之前的状态

        uchar cur_states[20];
    }ycdev;

    typedef struct _YIO{
        uint regbasenum;//该IO对应寄存器虚拟地址在_io_reg_base数组的序列号,可在"//io regs"出查

        uint num;        //io在该端口的序列号

    }yio;

    typedef struct _YIRQIO{
        yio io;
        int irqnum;    //irq号

        char irqname[4];//irq命名

    }yirqio;

    yio _keyio[4]={
                {IOF,3},
                {IOF,4},
                {IOG,1},
                {IOG,11}};

    yirqio _keyirqio[5]={
                {{IOG,6},IRQ_EINT14,"k0"},
                {{IOF,2},IRQ_EINT2,"k1"},
                {{IOF,5},IRQ_EINT5,"k2"},
                {{IOF,6},IRQ_EINT6,"k3"},
                {{IOG,8},IRQ_EINT16,"k4"}};


    ycdev *_pdev;
    static int _major=YMAJOR;
    static struct timer_list _tm;
    uint _io_reg_base[8];
    int _cur_irq;



    void init_virtual_address(void)
    {
        uint i=0;
        char name[10];
        do{
            sprintf(name,"keyreg%d",i);
            _io_reg_base[i]=(uint)yioremap((0x56000000+i*0x00000010),12,name);
            if(_io_reg_base[i]==0)printk(KERN_ERR"CALL init_virtual_address failed\n");
            //else printk(KERN_INFO"v%u:0x%X\t",i,_io_reg_base[i]);

        }while((++i)<8);
        //printk(KERN_INFO"\n");

    }

    void enable_ios_irq(uint en)
    {
        //en=0,disable irqs; en=1 enable irqs

        uint n=sizeof(_keyirqio)/sizeof(_keyirqio[0]);
        if(en==1){
            while(n--){
                enable_irq(_keyirqio[n].irqnum);
            }
        }
        else {
            while(n--){
                disable_irq_nosync(_keyirqio[n].irqnum);
            }

        }
    }


    static irqreturn_t onIrq(int irq,void *dev_id)
    {
        //interrupt service function,disable all irq,and elimination of button's jitter

        //disable_irq_nosync((uint)dev_id);

        _cur_irq=(int)dev_id;
        _pdev->x=(int)dev_id;
        _pdev->y=255;
        enable_ios_irq(0);
        //printk(KERN_INFO"call onIrq:%d %d\n",irq,_cur_irq);

        _tm.expires=MS(25);
        add_timer(&_tm);
        
        return IRQ_HANDLED;
    }


    void setIoConf(uint func,uint ionum,uint reg)
    {
        //set gpio function model

        uint dat;
        //printk(KERN_INFO"CALL setIoConf: 0x%X\n",reg);

        dat =ioread32(reg);
        dat=setNB(func,dat,ionum*2,2);
        iowrite32(dat,reg);
    }
    void set_io_conf(yio io,uint func)
    {
        //set gpio function model for yio type

        //printk(KERN_INFO"CALL set_io_conf: %u 0x%X 0x%X\n",io.regbasenum,_io_reg_base[io.regbasenum],GPIOCONF(_io_reg_base[io.regbasenum]));

        setIoConf(func,io.num,GPIOCONF(_io_reg_base[io.regbasenum]));
    }

    void set_irqios(uint func)
    {
        //set _keyirqio to func model

        uint n=sizeof(_keyirqio)/sizeof(_keyirqio[0]);
        while(n--){
            set_io_conf(_keyirqio[n].io,func);
        }
    }

    void irqios_input(void)
    {
        //disable irqs,and set _keyirqio to input model

        enable_ios_irq(0);
        set_irqios(Y2440_IO_IN);
    }
    void irqios_input2irq(void)
    {
        //from input model to irq model

        set_irqios(Y2440_IO_EINT);
        enable_ios_irq(1);
    }

    uint init_irqio(void)
    {
        //initiality _keyirqio, disable pull and request irq

        uint n=sizeof(_keyio)/sizeof(_keyio[0]);
        uint i;
        int rs;
        for(i=0;i<n;i++){
            iowrite32(SETB(_keyirqio[i].io.num)|ioread32(GPIOUP(_io_reg_base[_keyirqio[i].io.regbasenum])),GPIOUP(_io_reg_base[_keyirqio[i].io.regbasenum]));

            set_irq_type(_keyirqio[i].irqnum,IRQ_TYPE_EDGE_FALLING);
            rs=request_irq(_keyirqio[i].irqnum,onIrq,IRQF_DISABLED,_keyirqio[i].irqname,(void *)i);
            if(rs)return rs;
        }
        return 0;
    }

    void ios_output0(void)
    {
        //set _keyio to output model ,disable pull and output 0

        uint n=sizeof(_keyio)/sizeof(_keyio[0]);
        while(n--){
            //printk(KERN_INFO"io_output0:%u %u\n",n,_keyio[n].regbasenum);

            //printk(KERN_INFO"io_output0:d0 %u\n",_io_reg_base[_keyio[n].regbasenum]);

            iowrite32(SETB(_keyio[n].num)|ioread32(GPIOUP(_io_reg_base[_keyio[n].regbasenum])),GPIOUP(_io_reg_base[_keyio[n].regbasenum]));
            //printk(KERN_INFO"io_output0:d1\n");

            set_io_conf(_keyio[n],Y2440_IO_OUT);
            //printk(KERN_INFO"io_output0:d2\n");

            iowrite32(CLRB(_keyio[n].num)&ioread32(GPIODAT(_io_reg_base[_keyio[n].regbasenum])),GPIODAT(_io_reg_base[_keyio[n].regbasenum]));
            //printk(KERN_INFO"io_output0:d3\n");

        }
    }



    static void onTime(ulong arg)
    {
        int i,n=ARRAYSIZE(_keyio);
        uint regdat1=GPIODAT(_io_reg_base[_keyirqio[_cur_irq].io.regbasenum]),regdat2;
        //printk(KERN_INFO"call onTimer\n");

        set_io_conf(_keyirqio[_cur_irq].io,Y2440_IO_IN);
        
        if((ioread32(regdat1)&SETB(_keyirqio[_cur_irq].io.num))==0){
            //have a key down

            for(i=0;i<n;i++){
                //set another io output height

                regdat2=GPIODAT(_io_reg_base[_keyio[i].regbasenum]);
                
                iowrite32(SETB(_keyio[i].num)|ioread32(regdat2),regdat2);
                //should be have one true

                if(ioread32(regdat1)&SETB(_keyio[i].num)){
                    
                    if(_pdev->pre_states[_cur_irq*5+i]==0){
                        _pdev->pre_states[_cur_irq*5+i]=1;
                        _pdev->cur_states[_cur_irq*5+i]=1;
                        _pdev->y=(uchar)i;
                        printk(KERN_INFO "have key down:(%d,%d)\n",_cur_irq,i);
                    }
                    else printk(KERN_INFO "keep key down:(%d,%d)\n",_cur_irq,i);
                    iowrite32(CLRB(_keyio[i].num)&ioread32(regdat2),regdat2);
                    _tm.expires=MS(100);
                    add_timer(&_tm);
                    break;

                }
                //have a expect

                //else printk(KERN_ERR "error 2\n");

                //set another io output low

                iowrite32(CLRB(_keyio[i].num)&ioread32(regdat2),regdat2);

            }

        }
        else{
            //key up

            
            if(_pdev->y!=255){
                _pdev->pre_states[_pdev->x*5+_pdev->y]=0;
                printk(KERN_INFO "have key up:(%u,%u)\n",_pdev->x,_pdev->y);
                _pdev->y=255;
            }
            else printk(KERN_INFO "error 3\n");
            irqios_input2irq();
        }            

    }

    int yopen(struct inode *pinode,struct file *pfile)
    {
        return 0;
    }

    int yrelease(struct inode *pinode,struct file *pfile)
    {
        return 0;
    }

    static struct file_operations _fops={
        .open=yopen,
        .release=yrelease,
    };

    int __init yinit(void)
    {
        dev_t devno=MKDEV(YMAJOR,YMINOR);
        int rs;
        //uint tmp;

        if(_major){
                rs=register_chrdev_region(devno,1,YDEVICENAME);
            }
        else{
            rs=alloc_chrdev_region(&devno,YMINOR,1,YDEVICENAME);
            _major=MAJOR(devno);
            }
        if(rs<0)return rs;
        _pdev=kmalloc(sizeof(ycdev),GFP_KERNEL);
        if(!_pdev){
            
            rs= -ENOMEM;
            goto fail1;
            }
        cdev_init(&_pdev->dev,&_fops);
        _pdev->dev.owner=THIS_MODULE;
        rs=cdev_add(&_pdev->dev,MKDEV(_major,YMINOR),0);
        if(rs){
            rs=-EBUSY;
            goto fail2;
        }
        init_virtual_address();
        ios_output0();
        if(init_irqio())printk(KERN_INFO"irq failed");
        
        //init io's pre-state and timer

        memset(_pdev->pre_states,0,sizeof(_pdev->pre_states));
        memset(_pdev->cur_states,0,sizeof(_pdev->cur_states));
        setup_timer(&_tm,onTime,IRQ_EINT16);
        _pdev->x=_pdev->y=255;
        
        printk(KERN_INFO "driver ok\n");
        return 0;
    fail2:
        printk(KERN_ERR "fail2\n");
        kfree(_pdev);
    fail1:
        printk(KERN_ERR "fail1\n");
        unregister_chrdev_region(devno,1);
        return -EBUSY;
    }

    void __exit yexit(void)
    {
        del_timer(&_tm);
        yiounmap(GPBBASE,12);
        cdev_del(&_pdev->dev);
        kfree(_pdev);
        unregister_chrdev_region(MKDEV(_major,YMINOR),1);
    }

    MODULE_AUTHOR("MrY");
    module_init(yinit);
    module_exit(yexit);


  6. 总结:该驱动未使用s3c3410_gpio_xxx()函数,对寄存器的操作改用yioremap()来操作,使得对硬件的操作过程变得更加明朗,该程序已经经过测试,,由于键盘的连接线接触不良,部分键的响应不是很准确
阅读(2634) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~