Chinaunix首页 | 论坛 | 博客
  • 博客访问: 723884
  • 博文数量: 104
  • 博客积分: 4320
  • 博客等级: 上校
  • 技术积分: 1948
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-30 14:42
文章分类

全部博文(104)

文章存档

2012年(4)

2011年(65)

2010年(35)

分类: LINUX

2011-10-29 15:46:56

先说明一下按键与S3C2440芯片的连接方式:
KEY1 <----> EINT8
<----> GPG0
KEY2 <----> EINT11 <----> GPG3
KEY3 <----> EINT13 <----> GPG5
KEY4 <----> EINT14 <----> GPG6
KEY5 <----> EINT15 <----> GPG7
KEY6 <----> EINT19 <----> GPG11

驱动程序源码如下:
(drivers/char/mini2440_buttons.c)
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/delay.h>
  6. #include <linux/poll.h>
  7. #include <linux/irq.h>
  8. #include <asm/irq.h>
  9. #include <linux/interrupt.h>
  10. #include <asm/uaccess.h>
  11. #include <mach/regs-gpio.h>
  12. #include <mach/hardware.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/cdev.h>
  15. #include <linux/miscdevice.h>
  16. #include <linux/sched.h>
  17. #include <linux/gpio.h>

  18. #define DEVICE_NAME "buttons" //设备名称


  19. /*定义中断所用的结构体*/

  20. struct button_irq_desc
  21. {
  22.     int irq; //按键对应的中断号
  23.     int pin; //按键所对应的GPIO 端口
  24.     int pin_setting; //按键对应的引脚描述,实际并未用到,保留
  25.     int number; //定义键值,以传递给应用层/用户态
  26.     char *name; //每个按键的名称
  27. };


  28. /*结构体实体定义*/
  29. static struct button_irq_desc button_irqs [] =
  30. {
  31.     {IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"},
  32.     {IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"},
  33.     {IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"},
  34.     {IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"},
  35.     {IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"},
  36.     {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
  37. };

  38. /*开发板上按键的状态变量,注意这里是’0’,对应的ASCII 码为30*/
  39. static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};

  40. /*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读>取到键值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户>态;当没有按键按下时,系统并不*/
  41. /*会轮询按键状态,以节省时钟资源*/
  42. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

  43. /*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/
  44. static volatile int ev_press = 0;

  45. /*本按键驱动的中断服务程序*/
  46. static irqreturn_t buttons_interrupt(int irq, void *dev_id)
  47. {
  48.     struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
  49.     int down;

  50.     // udelay(0);

  51.     /*获取被按下的按键状态*/
  52.     down = !s3c2410_gpio_getpin(button_irqs->pin);

  53.     /*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为1(上拉)
  54. 但按键被按下的时候,寄存器对应的值为0*/
  55.     if (down != (key_values[button_irqs->number] & 1)) { // Changed
  56.         /*如果key1 被按下,则key_value[0]就变为’1’,对应的ASCII 码为31*/
  57.         key_values[button_irqs->number] = '0' + down;
  58.         ev_press = 1; /*设置中断标志为1*/
  59.         wake_up_interruptible(&button_waitq); /*唤醒等待队列*/
  60.     }
  61.     return IRQ_RETVAL(IRQ_HANDLED);
  62. }

  63. /**在应用程序执行open(/dev/buttons”,)时会调用到此函数,在这里,它的作用主要是注册6 个按
  64. 键的中断。

  65.  *所用的中断类型是IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上升沿和下降沿均会产生中断,这样>

  66.  **是为了更加有效地判断按键状态

  67.  */

  68. static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
  69. {
  70.     int i;
  71.     int err = 0;
  72.     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
  73.     {
  74.         if (button_irqs[i].irq < 0)
  75.         {
  76.             continue;
  77.         }

  78.         /*注册中断函数*/
  79.         err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
  80.                 button_irqs[i].name, (void *)&button_irqs[i]);
  81.         if (err)
  82.             break;
  83.     }

  84.     if (err)
  85.     {
  86.         /*如果出错,释放已经注册的中断,并返回*/
  87.         i--;
  88.         for (; i >= 0; i--)
  89.         {
  90.             if (button_irqs[i].irq < 0)
  91.             {
  92.                 continue;
  93.             }
  94.             disable_irq(button_irqs[i].irq);
  95.             free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
  96.         }
  97.         return -EBUSY;
  98.     }

  99.     /*注册成功,则中断队列标记为1,表示可以通过read 读取*/
  100.     ev_press = 1;
  101.     /*正常返回*/
  102.     return 0;
  103. }

  104. /*

  105.  *此函数对应应用程序的系统调用close(fd)函数,在此,它的主要作用是当关闭设备时释放6 个按键
  106. 的中断*处理函数

  107.  */

  108. static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
  109. {
  110.     int i;
  111.     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
  112.     {
  113.         if (button_irqs[i].irq < 0)
  114.         {
  115.             continue;
  116.         }

  117.         /*释放中断号,并注销中断处理函数*/
  118.         free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
  119.     }
  120.     return 0;
  121. }
  122. /*

  123.  *对应应用程序的read(fd,)函数,主要用来向用户空间传递键值

  124.  */

  125. static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
  126. {
  127.     unsigned long err;
  128.     if (!ev_press)
  129.     {
  130.         if (filp->f_flags & O_NONBLOCK)
  131.             /*当中断标识为0 时,并且该设备是以非阻塞方式打开时,返回*/
  132.             return -EAGAIN;
  133.         else
  134.             /*当中断标识为0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/
  135.             wait_event_interruptible(button_waitq, ev_press);
  136.     }

  137.     /*把中断标识清零*/
  138.     ev_press = 0;

  139.     /*一组键值被传递到用户空间*/
  140.     err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));

  141.     return err ? -EFAULT : min(sizeof(key_values), count);
  142. }

  143. static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
  144. {
  145.     unsigned int mask = 0;

  146.     /*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/
  147.     poll_wait(file, &button_waitq, wait);

  148.     if (ev_press)
  149.         mask |= POLLIN | POLLRDNORM;

  150.     return mask;
  151. }

  152. /*设备操作集*/
  153. static struct file_operations dev_fops = {
  154.     .owner = THIS_MODULE,
  155.     .open = s3c24xx_buttons_open,
  156.     .release = s3c24xx_buttons_close,
  157.     .read = s3c24xx_buttons_read,
  158.     .poll = s3c24xx_buttons_poll,
  159. };

  160. static struct miscdevice misc = {
  161.     .minor = MISC_DYNAMIC_MINOR,
  162.     .name = DEVICE_NAME,
  163.     .fops = &dev_fops,
  164. };

  165. /*设备初始化,主要是注册设备*/
  166. static int __init dev_init(void)
  167. {
  168.     int ret;

  169.     /*把按键设备注册为misc 设备,其设备号是自动分配的*/
  170.     ret = misc_register(&misc);
  171.     printk (DEVICE_NAME"\tinitialized\n");
  172.     return ret;
  173. }

  174. /*注销设备*/
  175. static void __exit dev_exit(void)
  176. {
  177.     misc_deregister(&misc);
  178. }

  179. module_init(dev_init); //模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是
  180. 通过模块方式加载,此处将不会被调用

  181. module_exit(dev_exit); //卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将
  182. 调用此函数

  183. MODULE_LICENSE("GPL");//版权信息

  184. MODULE_AUTHOR("FriendlyARM Inc.");//作者名字
测试程序:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/ioctl.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <sys/select.h>
  9. #include <sys/time.h>
  10. #include <errno.h>

  11. int
  12. main(void)
  13. {
  14.     int buttons_fd;
  15.     char buttons[6] = {'0', '0', '0', '0', '0', '0'};

  16.     buttons_fd = open("/dev/buttons", 0);
  17.     if(buttons_fd < 0){
  18.         perror("open device buttons error!");
  19.         exit(1);
  20.     }

  21.     for(;;){
  22.         char current_buttons[6];
  23.         int count_of_changed_key;
  24.         int i;
  25.         if(read(buttons_fd, current_buttons, sizeof(current_buttons)) != sizeof(current_buttons)){
  26.             perror("read buttons:");
  27.             exit(1);
  28.         }

  29.         for(i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++){
  30.             if(buttons[i] != current_buttons[i]){
  31.                 buttons[i] = current_buttons[i];
  32.                 printf("%s key %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up":"down");
  33.                 count_of_changed_key++;
  34.             }
  35.         }
  36.         if(count_of_changed_key){
  37.             printf("\n");
  38.         }
  39.     }
  40.     close(buttons_fd);
  41.     return 0;
  42. }
在开发板上运行测试程序,按不同的按键,串口输出如下:
  1.  key 1 is down
  2.  key 1 is up
  3.  key 2 is down
  4.  key 2 is up
  5.  key 2 is down
  6.  key 2 is up
  7.  key 3 is down
  8.  key 3 is up
  9.  key 6 is down
  10.  key 6 is up
  11.  key 4 is down
  12.  key 4 is up
  13.  key 5 is down
  14.  key 5 is up

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