Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2374011
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: LINUX

2011-03-24 13:07:10

11mini2440之按键驱动

 

 

注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方

1.本文所介绍的程序平台

开发板:arm9-mini2440

虚拟机为:Red Hat Enterprise Linux 5

开发板上系统内核版本:linux-2.6.32.2

 

2.程序清单

本次实验程序为网络代码,本人作了改动和较为详细的注释,如有错误请指出。

内核维护了一个中断信号线的注册表,类似于 I/O 端口的注册表。模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。所用的函数声明在 (在此文件中并未真正包含,是通过它include的文件间接包含的,函数在/kernel/irq/Manage.h),中断注册和释放的函数接口如下:

int request_irq(unsigned int irq,
             irqreturn_t (*handler)(int, void *, struct pt_regs *),
                unsigned long flags,

                const char *dev_name,
                void *dev_id);

void free_irq(unsigned int irq, void *dev_id);

 

request_irq 的返回值: 0 指示成功,或返回一个负的错误码,如 -EBUSY 表示另一个驱动已经占用了你所请求的中断线。

函数的参数如下:

unsigned int irq 请求的中断号

irqreturn_t (*handler) :安装的处理函数指针。

unsigned long flags :一个与中断管理相关的位掩码选项。

const char *dev_name 传递给 request_irq 的字符串,用来在 /proc/interrupts 来显示中断的拥有者。

void *dev_id :用于共享中断信号线的指针。它是唯一的标识,在中断线空闲时可以使用它,驱动程序也可以用它来指向自己的私有数据区(来标识哪个设备产生中断)。若中断没有被共享,dev_id 可以设置为 NULL,但推荐用它指向设备的数据结构。

 

 

flags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的) 

dev_id
在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL 

 

flags 中可以设置的位如下:

SA_INTERRUPT 快速中断标志。快速中断处理例程运行在当前处理器禁止中断的状态下。

现在没有了 !!

SA_SHIRQ : 在设备间共享中断标志。

SA_SAMPLE_RANDOM :该位表示产生的中断能对 /dev/random /dev/urandom 使用的熵池(entropy pool)有贡献。 读取这些设备会返回真正的随机数,从而有助于应用程序软件选择用于加密的安全密钥。 若设备以真正随机的周期产生中断,就应当设置这个标志。若设备中断是可预测的,这个标志不值得设置。可能被攻击者影响的设备不应当设置这个标志。更多信息看 drivers/char/random.c 的注释。

 

mini_button.c

 

#include

#include

#include

#include

 

#include

#include

#include

#include

#include

#include /* current and everything */

 

//#include "regs-gpio.h"

//#include "irqs.h"

 

 

#include

#include /*端口定义*/

#include

#include /*s3c2410_gpio_cfgpin等函数*/

#include

#include

 

#define DEVICE_NAME     "buttons"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */

 

#define BUTTON_MAJOR    232         /* 主设备号 */

 

 

 

struct button_irq_desc {

 

     int irq;//中断号

      int pin;//引脚

      int pin_setting;//引脚设置

      int number;//编号

    char *name; //名字

 

};

 

/* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 ,初始化了各值*/

 

static struct button_irq_desc button_irqs [] = {

 

    {IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"}, /* K1 */

 

    {IRQ_EINT11, S3C2410_GPG(3),  S3C2410_GPG3_EINT11,  1, "KEY2"}, /* K2 */

 

    {IRQ_EINT13,  S3C2410_GPG(5),  S3C2410_GPG5_EINT13,   2, "KEY3"}, /* K3 */

 

    {IRQ_EINT14,  S3C2410_GPG(6),  S3C2410_GPG6_EINT14,   3, "KEY4"}, /* K3 */

 

    {IRQ_EINT15,  S3C2410_GPG(7),  S3C2410_GPG7_EINT15,   4, "KEY5"}, /* K3 */

 

    {IRQ_EINT19,  S3C2410_GPG(11),  S3C2410_GPG11_EINT19,   5, "KEY6"}, /* K4 */

 

};

 

 

/* 记录按键的状态 */

static volatile int key_values [] = {0, 0, 0, 0, 0, 0};

 

/* 等待队列:

 * 当没有按键被按下时,如果有进程调用mini2440_buttons_read函数,

 * 它将休眠

 */

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

 

 

 

/* 中断事件标志, 中断服务程序将它置1,mini2440_buttons_read将它清0 */

static volatile int ev_press = 0;

 

 

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

 

{

 

    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

    int up = s3c2410_gpio_getpin(button_irqs->pin);//获取引脚状态

       if (up)//如果是释放按键

            //键值

              key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;

       else////如果是按下按键

              key_values[button_irqs->number] = (button_irqs->number + 1);

 

    ev_press = 1;                  /* 表示中断发生了 */

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */   

    return IRQ_RETVAL(IRQ_HANDLED);

 

}

 

 

/* 应用程序对设备文件/dev/buttons执行open(...)时,

 * 就会调用mini2440_buttons_open函数

 */

 

static int mini2440_buttons_open(struct inode *inode, struct file *file)

 

{

 

    int i;

    int err;

 

   

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

        // 注册中断处理函数

            //设置引脚作用 为中断

            s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);

             err = request_irq(button_irqs[i].irq, buttons_interrupt, NULL,

             button_irqs[i].name, (void *)&button_irqs[i]);

 

             //set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);

//双沿IRQT_BOTHEDGE  上升沿IRQ_TYPE_EDGE_RISING

              set_irq_type(button_irqs[i].irq, IRQ_TYPE_EDGE_BOTH);

        if (err)

             break;

    }

 

    if (err) {

        // 释放已经注册的中断

        i--;

        for (; i >= 0; i--) {

            disable_irq(button_irqs[i].irq);//关闭中断

            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

        }

       return -EBUSY;

    }

    return 0;

 

}

 

 

/* 应用程序对设备文件/dev/buttons执行close(...)时,

 * 就会调用mini2440_buttons_close函数

 */

 

static int mini2440_buttons_close(struct inode *inode, struct file *file)

 

{

    int i; 

 

    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

        // 释放已经注册的中断

         disable_irq(button_irqs[i].irq);

        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

    }

    return 0;

 

}

 

 

/* 应用程序对设备文件/dev/buttons执行read(...)时,

 * 就会调用mini2440_buttons_read函数

 */

 

static int mini2440_buttons_read(struct file *filp, char __user *buff,

 

                                         size_t count, loff_t *offp)

 

{

 

    unsigned long err;

 

       if (!ev_press) {//如果中断没有发生 即ev_press等于0

              if (filp->f_flags & O_NONBLOCK)

                     return -EAGAIN;

              else

                     /* 如果ev_press等于0,休眠 */

                     wait_event_interruptible(button_waitq, ev_press);

 

       }   

    /* 执行到这里时,ev_press等于1,将它清0 */

    ev_press = 0;

    /* 将按键状态复制给用户,并清0 */

    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

    memset((void *)key_values, 0, sizeof(key_values));

    return err ? -EFAULT : min(sizeof(key_values), count);

 

}

 

/**************************************************

 

* 当用户程序调用select函数时,本函数被调用

 

* 如果有按键数据,则select函数会立刻返回

 

* 如果没有按键数据,本函数使用poll_wait等待

 

**************************************************/

 

static unsigned int mini2440_buttons_poll(

 

        struct file *file,

 

        struct poll_table_struct *wait)

 

{

 

       unsigned int mask = 0;

 

    poll_wait(file, &button_waitq, wait);

    if (ev_press)

        mask |= POLLIN | POLLRDNORM;

    return mask;

 

}

 

 

 

 

 

/* 这个结构是字符设备驱动程序的核心

 

 * 当应用程序操作设备文件时所调用的open、read、write等函数,

 

 * 最终会调用这个结构中的对应函数

 

 */

 

static struct file_operations mini2440_buttons_fops = {

 

    .owner   =   THIS_MODULE,    /* 这是一个宏,指向编译模块时自动创建的__this_module变量 */

 

    .open    =   mini2440_buttons_open,

    .release =   mini2440_buttons_close,

    .read    =   mini2440_buttons_read,

       .poll =   mini2440_buttons_poll,

 

};

 

 

 

/*

 

 * 执行“insmod mini2440_buttons.ko”命令时就会调用这个函数

 

 */

 

static int __init mini2440_buttons_init(void)

 

{

 

    int ret;

 

    /* 注册字符设备驱动程序

     * 参数为主设备号、设备名字、file_operations结构;

     * 这样,主设备号就和具体的file_operations结构联系起来了,

     * 操作主设备为BUTTON_MAJOR的设备文件时,就会调用mini2440_buttons_fops中的相关成员函数

     * BUTTON_MAJOR可以设为0,表示由内核自动分配主设备号

     */

   

    ret = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &mini2440_buttons_fops);

    if (ret < 0) {

      printk(DEVICE_NAME " can't register major number\n");

      return ret;

 

    }

      //低版本才能用devfs_mk_cdev

      // devfs_mk_cdev(MKDEV(BUTTON_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME); 

    printk(DEVICE_NAME " initialized\n");

    return 0;

 

}

 

 

 

/*

 

 * 执行”rmmod mini2440_buttons.ko”命令时就会调用这个函数

 

 */

 

static void __exit mini2440_buttons_exit(void)

 

{

 

    /* 卸载驱动程序 */

//低版本才能用devfs_mk_cdev

   //  devfs_remove(DEVICE_NAME);

 

    unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);

 

}

 

 

/* 这两行指定驱动程序的初始化函数和卸载函数 */

module_init(mini2440_buttons_init);

module_exit(mini2440_buttons_exit);

/* 描述驱动程序的一些信息,不是必须的 */

MODULE_AUTHOR("");             // 驱动程序的作者

MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver");   // 一些描述信息

MODULE_LICENSE("GPL");                              // 遵循的协议

 

button_app.c

 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

int main(void)

{

       int i;

       int buttons_fd;

       int key_value[4];

       fd_set rds;//可读集

       int ret;

 

       /*打开键盘设备文件*/

       buttons_fd = open("/dev/buttons", 0);

       if (buttons_fd < 0) {

              perror("open device buttons");

              exit(1);

       }

       for (;;) {

            

              FD_ZERO(&rds);//清空集合

              FD_SET(buttons_fd, &rds);//将文件描述符加入可读集

              /*使用系统调用select检查是否能够从/dev/buttons设备读取数据*/

             Select第一个参数为最大描述符

              ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);

              /*读取出错则退出程序*/

              if (ret < 0) {

                     perror("select");

                    exit(1);

              }

              if (ret == 0) {

                     printf("Timeout.\n");

              }

              /*能够读取到数据*/

              else if (FD_ISSET(buttons_fd, &rds)) {

                    /*开始读取键盘驱动发出的数据,注意key_value和键盘驱动中定义为一致的类型*/

   int ret = read(buttons_fd, key_value, sizeof key_value);

                     if (ret != sizeof key_value) {

                            if (errno != EAGAIN)

                                  perror("read buttons\n");

                            continue;

 

                     } else {

                            /*打印键值*/

                            for (i = 0;i < sizeof(key_value)/sizeof(key_value[0]); i++)

                                printf("K%d %s, key value = 0x%02x\n", i+1, (key_value[i] & 0x80) ? "released"     : \

                                                                                        key_value[i] ? "pressed down" : "", \

                                                                                key_value[i]);

                     }

              }

       }

 

       close(buttons_fd);

 

       return 0;

 

}

 

 

Arm平台按键实验:

[root@FriendlyARM test]# insmod  mini_button.ko

buttons initialized

[root@FriendlyARM test]# cat /proc/devices

Character devices:

  1 mem

  4 /dev/vc/0

  4 tty

  5 /dev/tty

  5 /dev/console

  5 /dev/ptmx

  7 vcs

 10 misc

 13 input

 14 sound

 21 sg

 29 fb

 81 video4linux

 89 i2c

 90 mtd

116 alsa

128 ptm

136 pts

180 usb

188 ttyUSB

189 usb_device

204 s3c2410_serial

232 buttons

254 rtc

 

Block devices:

259 blkext

  7 loop

  8 sd

 31 mtdblock

 65 sd

 66 sd

 67 sd

 68 sd

 69 sd

 70 sd

 71 sd

128 sd

129 sd

130 sd

131 sd

132 sd

133 sd

134 sd

135 sd

179 mmc

[root@FriendlyARM test]# mknod /dev/buttons c 232 0

[root@FriendlyARM test]# ./button_app

K1 , key value = 0x00

K2 , key value = 0x00

K3 pressed down, key value = 0x03

K4 , key value = 0x00

K1 , key value = 0x00

K2 , key value = 0x00

K3 released, key value = 0x83

K4 , key value = 0x00

K1 , key value = 0x00

K2 pressed down, key value = 0x02

K3 , key value = 0x00

K4 , key value = 0x00

K1 , key value = 0x00

K2 pressed down, key value = 0x02

K3 , key value = 0x00

K4 , key value = 0x00

K1 , key value = 0x00

K2 released, key value = 0x82

K3 , key value = 0x00

K4 , key value = 0x00

K1 , key value = 0x00

K2 , key value = 0x00

K3 , key value = 0x00

K4 pressed down, key value = 0x04

K1 , key value = 0x00

K2 , key value = 0x00

K3 , key value = 0x00

K4 pressed down, key value = 0x04

K1 , key value = 0x00

K2 , key value = 0x00

K3 , key value = 0x00

K4 pressed down, key value = 0x04

K1 , key value = 0x00

K2 , key value = 0x00

K3 , key value = 0x00

K4 pressed down, key value = 0x04

注意:按键有抖动,应该消抖处理

阅读(1273) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~