网站首页
IC库存
IC展台
电子资讯
技术资料
PDF文档
我的博客
IC72论坛
ic72 logo
搜索关键字: 所有资讯 行业动态 市场趋势 政策法规 新品发布 技术资讯 价格快报 展会资讯
  • 达普IC芯片交易网 > 新闻中心 > 技术资料 > 正文
  • RSS
  • FS2410开发板上的按键驱动程序(2)续
    http://www.ic72.com 发布时间:2009/7/23 9:58:41

      按键字符设备的file_operations结构定义为:

    static struct file_operations button_fops =
        {
            .owner = THIS_MODULE,
            .ioctl = button_ioctl,
            .open = button_open,
            .read = button_read,
            .release = button_release,
        };

    以下为open和release函数接口的实现。

    /* 打开文件, 申请中断 */
        static int button_open(struct inode *inode,struct file *filp)
        {
            int ret = nonseekable_open(inode, filp);
            if (ret < 0)
            {
                 return ret;
            }
            init_gpio(); /* 相关GPIO端口的初始化*/
            request_irqs(); /* 申请4个中断 */
            if (ret < 0)
            {
                 return ret;
            }
            init_keybuffer(); /* 初始化按键缓冲数据结构 */
            return ret;
        }
        /* 关闭文件, 屏蔽中断 */
        static int button_release(struct inode *inode,struct file *filp)
        {
            free_irqs(); /* 屏蔽中断 */
            return 0;
        }

      在open函数接口中,进行了GPIO端口的初始化、申请硬件中断以及按键缓冲的初始化等工作。在以前的章节中提过,中断端口是比较宝贵而且数量有限的资源。因此需要注意,最好要在第一次打开设备时申请(调用request_irq函数)中断端口,而不是在驱动模块加载的时候申请。如果已加载的设备驱动占用而在一定时间段内不使用某些中断资源,则这些资源不会被其他驱动所使用,只能白白浪费掉。而在打开设备的时候(调用open函数接口)申请中断,则不同的设备驱动可以共享这些宝贵的中断资源。

      以下为中断申请和释放的部分以及中断处理函数。

    /* 中断处理函数,其中irq为中断号 */
        static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
        {
            unsigned char ucKey = 0;
            disable_irqs(); /* 屏蔽中断 */
            /* 延迟50毫秒, 屏蔽按键毛刺 */
            udelay(50000);
            ucKey = button_scan(irq); /* 扫描按键,获得进行操作的按键的ID */
            if ((ucKey >= 1) && (ucKey <= 16))
            {
                /* 如果缓冲区已满, 则不添加 */
                if (((key_buffer.head + 1) & (MAX_KEY_COUNT - 1)) != key_buffer.tail)
                {
                     spin_lock_irq(&buffer_lock);
                     key_buffer.jiffy[key_buffer.tail] = get_tick_count();
                   key_buffer.tail ++;
                   key_buffer.tail &= (MAX_KEY_COUNT -1);
                     spin_unlock_irq(&buffer_lock);
                 }
            }
            init_gpio(); /* 初始化GPIO端口,主要是为了恢复中断端口配置 */
            enable_irqs(); /* 开启中断 */
            return IRQ_HANDLED;/* 2.6内核返回值一般是这个宏 */
        }
        /* 申请4个中断 */
        static int request_irqs(void)
        {
            int ret, i, j;
            for (i = 0; i < MAX_COLUMN; i++)
        {
                 ret = request_irq(key_info_matrix[i][0].irq_no,
                          button_irq, SA_INTERRUPT, BUTT*_DEVICE_NAME, NULL);
                 if (ret < 0)
                 {
                         for (j = 0; j < i; j++)
                         {
                             free_irq(key_info_matrix[j][0].irq_no, NULL);
                         }
                         return -EFAULT;
                 }
             }
             return 0;
        }
        /* 释放中断 */
        static __inline void free_irqs(void)
        {
            int i;
            for (i = 0; i < MAX_COLUMN; i++)
            {
                free_irq(key_info_matrix[i][0].irq_no, NULL);
            }
        }

      中断处理函数在每次中断产生的时候会被调用,因此它的执行时间要尽可能得短。通常中断处理函数只是简单地唤醒等待资源的任务,而复杂且耗时的工作则让这个任务去完成。中断处理函数不能向用户空间发送数据或者接收数据,不能做任何可能发生睡眠的操作,而且不能调用schedule()函数。

      为了简单起见,而且考虑到按键操作的时间比较长,在本实例中的中断处理函数button_irq()里,通过调用睡眠函数来消除毛刺信号。读者可以根据以上介绍的对中断处理函数的要求改进该部分代码。

      按键扫描函数如下所示。首先根据中断号确定操作按键所在行的位置,然后采用逐列扫描法最终确定操作按键所在的位置。

    /*
        ** 进入中断后, 扫描铵键码
        ** 返回: 按键码(1-16), 0xff表示错误
        */
        static __inline unsigned char button_scan(int irq)
        {
            unsigned char key_id = 0xff;
            unsigned char column = 0xff, row = 0xff;
            s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_INP); /* GPF0 */
           s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_INP); /* GPF2 */
           s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_INP); /* GPG3 */
           s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_INP); /* GPG11 */
            switch (irq)
            { /* 根据irq值确定操作按键所在行的位置*/
                case IRQ_EINT0:
                {
                      column = 0;
                }
                break;
                case IRQ_EINT2:
                {
                      column = 1;
                }
                break;
                case IRQ_EINT11:
                {
                      column = 2;
                }
                break;
                case IRQ_EINT19:
                {
                      column = 3;
                }
                break;
            }
            if (column != 0xff)
            { /* 开始逐列扫描, 扫描第0列 */
                 s3c2410_gpio_setpin(S3C2410_GPE11, 0); /* 将KSCAN0置为低电平 */
                 s3c2410_gpio_setpin(S3C2410_GPG6, 1);
                 s3c2410_gpio_setpin(S3C2410_GPE13, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG2, 1);
             if(!s3c2410_gpio_getpin(key_info_matrix[column][0].irq_gpio_port))
                 { /* 观察对应的中断线的输入端口值 */
                      key_id = key_info_matrix[column][0].key_id;
                      return key_id;
                 }
                 /* 扫描第1列*/
                 s3c2410_gpio_setpin(S3C2410_GPE11, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG6, 0); /* 将KSCAN1置为低电平 */
                 s3c2410_gpio_setpin(S3C2410_GPE13, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG2, 1);
                 if(!s3c2410_gpio_getpin(key_info_matrix[column][1].irq_gpio_port))
                 {
                      key_id = key_info_matrix[column][1].key_id;
                      return key_id;
                 }
                 /* 扫描第2列*/
                 s3c2410_gpio_setpin(S3C2410_GPE11, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG6, 1);
                 s3c2410_gpio_setpin(S3C2410_GPE13, 0); /* 将KSCAN2置为低电平 */
                 s3c2410_gpio_setpin(S3C2410_GPG2, 1);
                 if(!s3c2410_gpio_getpin(key_info_matrix[column][2].irq_gpio_port))
                 {
                      key_id = key_info_matrix[column][2].key_id;
                      return key_id;
                 }
                 /* 扫描第3列*/
                 s3c2410_gpio_setpin(S3C2410_GPE11, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG6, 1);
                 s3c2410_gpio_setpin(S3C2410_GPE13, 1);
                 s3c2410_gpio_setpin(S3C2410_GPG2, 0); /* 将KSCAN3置为低电平 */
                 if(!s3c2410_gpio_getpin(key_info_matrix[column][3].irq_gpio_port))
                 {
                       key_id = key_info_matrix[column][3].key_id;
                       return key_id;
                 }
             }
             return key_id;
         }

      以下是read函数接口的实现。首先在按键缓冲中删除已经过时的按键操作信息,接下来,从按键缓冲中读取一条信息(按键ID)并传递给用户层。

    /* 从缓冲删除过时数据(5秒前的按键值) */
        static void remove_timeoutkey(void)
        {
            unsigned long tick;
            spin_lock_irq(&buffer_lock); /* 获得一个自旋锁 */
            while(key_buffer.head != key_buffer.tail)
            {
                 tick = get_tick_count() - key_buffer.jiffy[key_buffer.head];
                 if (tick < 5000) /* 5秒 */
                 break;
                 key_buffer.buf[key_buffer.head] = 0;
                 key_buffer.jiffy[key_buffer.head] = 0;
                 key_buffer.head ++;
                 key_buffer.head &= (MAX_KEY_COUNT -1);
            }
            spin_unlock_irq(&buffer_lock); /* 释放自旋锁 */
        }
        /* 读键盘 */
        static ssize_t button_read(struct file *filp,
                                char *buffer, size_t count, loff_t *f_pos)
        {
             ssize_t ret = 0;
             remove_timeoutkey(); /* 删除过时的按键操作信息 */
             spin_lock_irq(&buffer_lock);
             while((key_buffer.head != key_buffer.tail) && (((size_t)ret) < count))
             {
                   put_user((char)(key_buffer.buf[key_buffer.head]), &buffer[ret]);
                   key_buffer.buf[key_buffer.head] = 0;
                   key_buffer.jiffy[key_buffer.head] = 0;
                   key_buffer.head ++;
                   key_buffer.head &= (MAX_KEY_COUNT -1);
                   ret ++;
             }
             spin_unlock_irq(&buffer_lock);
             return ret;
        }

      以上介绍了按键驱动程序中的主要内容。

     


    www.ic72.com 达普IC芯片交易网
  • 行业动态
  • 市场趋势
  • 政策法规
  • 新品发布
  • Baidu

    IC快速检索:abcdefghijklmnopqrstuvwxyz0123456789
    COPYRIGHT:(1998-2010) IC72 达普IC芯片交易网
    客户服务:service@IC72.com 库存上载:IC72@IC72.com
    (北京)联系方式: 在线QQ咨询:点击这里给我发消息 联系电话:010-82614113 传真:010-82614123
    京ICP备06008810号-21 京公网安备 11010802032910 号 企业资质