Chinaunix首页 | 论坛 | 博客
  • 博客访问: 594882
  • 博文数量: 88
  • 博客积分: 4769
  • 博客等级: 中校
  • 技术积分: 989
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-24 02:31
文章分类

全部博文(88)

文章存档

2012年(4)

2011年(35)

2010年(49)

分类: LINUX

2011-09-27 00:47:02

开发环境:Ubuntu 10.04

开发板: Frindly ARM Micro 2440


一、首先查看与led相关的原理图,如下图所示:

        从图中可以看出LED1234分别连接到S3C2440GPB5GPB6GPB7GPB8这四个Pin脚,且为共阳极,当IO口输出为低时,对应的LED会点亮。

        以LED1为例,其对应的pin脚为GPB5GPB5/nXBACK被配置成GPIO,方向为output后,若输出低电平,则LED1会被点亮。

        datasheet中查到GPB5的基地址为 0x56000010,通过ioremap后的虚拟地址为: 0xc4810010,但是驱动代码中使用的却是:0xfb000010,这其中有什么玄机吗?为什么使用的不是虚拟地址 0xc4810010

source code 

1. source code list


  1. #include <linux/miscdevice.h>
  2. #include <linux/delay.h>
  3. #include <asm/irq.h>
  4. #include <mach/regs-gpio.h>
  5. #include <mach/hardware.h>
  6. #include <linux/kernel.h>
  7. #include <linux/module.h>
  8. #include <linux/init.h>
  9. #include <linux/mm.h>
  10. #include <linux/fs.h>
  11. #include <linux/types.h>
  12. #include <linux/delay.h>
  13. #include <linux/moduleparam.h>
  14. #include <linux/slab.h>
  15. #include <linux/errno.h>
  16. #include <linux/ioctl.h>
  17. #include <linux/cdev.h>
  18. #include <linux/string.h>
  19. #include <linux/list.h>
  20. #include <linux/pci.h>
  21. #include <linux/gpio.h>
  22. #include <asm/uaccess.h>
  23. #include <asm/atomic.h>
  24. #include <asm/unistd.h>


  25. #define DEVICE_NAME "leds"

  26. static unsigned long led_table [] = {
  27.     S3C2410_GPB(5),
  28.     S3C2410_GPB(6),
  29.     S3C2410_GPB(7),
  30.     S3C2410_GPB(8),
  31. };

  32. static unsigned int led_cfg_table [] = {
  33.     S3C2410_GPIO_OUTPUT,
  34.     S3C2410_GPIO_OUTPUT,
  35.     S3C2410_GPIO_OUTPUT,
  36.     S3C2410_GPIO_OUTPUT,
  37. };

  38. static int sbc2440_leds_ioctl(
  39.     struct inode *inode,
  40.     struct file *file,
  41.     unsigned int cmd,
  42.     unsigned long arg)
  43. {
  44.     switch(cmd) {
  45.     case 0:
  46.     case 1:
  47.         if (arg > 4) {
  48.             return -EINVAL;
  49.         }
  50.         s3c2410_gpio_setpin(led_table[arg], !cmd);
  51.         return 0;
  52.     default:
  53.         return -EINVAL;
  54.     }
  55. }

  56. static struct file_operations dev_fops = {
  57.     .owner    =    THIS_MODULE,
  58.     .ioctl    =    sbc2440_leds_ioctl, //只注册了ioctl,真省事儿...
  59. };

  60. static struct miscdevice misc = {
  61.     .minor = MISC_DYNAMIC_MINOR,
  62.     .name = DEVICE_NAME,
  63.     .fops = &dev_fops,
  64. };

  65. static int __init dev_init(void) //模块注册函数
  66. {
  67.     int ret;

  68.     int i;
  69.  
  70. /* datasheet上的基地址,经过ioremap后为: 0xc4810010,但是在 函数 s3c2410_gpio_cfgpin却使
  71.  * 用的是0xfb000010,搞不懂为什么这样用? 下面三句是我自己加的打印信息。
  72.  */
  73.     unsigned int __iomem base = (unsigned int)ioremap(0x56000010,1);
  74.     printk(KERN_INFO"s3c2410_gpio_cfgpin ioremap == 0x%0x\n", base);
  75.     iounmap(base);
  76.     
  77.     for (i = 0; i < 4; i++) {
  78.         printk(KERN_INFO"dev_init\n");
  79.         s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //管脚复用为GPIO
  80.         s3c2410_gpio_setpin(led_table[i], 0); //配置GPIO的方向为output
  81.     }

  82.     ret = misc_register(&misc); //注册设备

  83.     printk (DEVICE_NAME"\tinitialized\n");

  84.     return ret;
  85. }

  86. static void __exit dev_exit(void) //注销设备
  87. {
  88.     misc_deregister(&misc);
  89. }

  90. module_init(dev_init);
  91. module_exit(dev_exit);
  92. MODULE_LICENSE("GPL");
  93. MODULE_AUTHOR("FriendlyARM Inc.");
2. source code 浅析

在模块注册函数static int __init dev_init(void) 中使用for循环来初始化led要用到的GPIO口:

  1. for (i = 0; i < 4; i++) {
  2.     printk(KERN_INFO"dev_init\n");
  3.     s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //管脚复用为GPIO
  4.     s3c2410_gpio_setpin(led_table[i], 0); //配置GPIO的方向为output
  5. }

在函数 s3c2410_gpio_cfgpin()s3c2410_gpio_setpin()都出现了一对与中断相关的函数:

  1. local_irq_save();
  2. local_irq_restore();

这两个函数在《Linux内核设计与实现 第二版》中有详细的解释,见下文蓝字部分:

6.7.1 禁止和激活中断

用于禁止当前处理器上的本地中断,随后又激活他们的语句为:

  1. local_irp_disable();
  2. /* 禁止中断 */
  3. local_irq_enable();

这两个函数通常以单个汇编指令来实现。

如果在调用local_irq_disable()例程之前已经禁止了中断,那么该例程往往会带来潜在的危险; 样相应的local_irq_enable()例程也存在潜在危险,因为它将无条件地激活中断,尽管这些中断可能在开始时就是关闭的。所以我们需要一种机制把中断恢复到以前的状态而不是简单地禁止或激活。内核普遍关心这点,因为内核中一个给定的代码路径既可以在中断激活的情况下到达,也可以在中断禁止的情况下到达,这取决于具体的调用链。随着内核的不断增长,要想知道到达这个函数的所有代码路径将变得越来越困难,因此,在禁止中断之前保存中断系统的状态会更加安全一些。相反,在准备激活中断时,只需要把中断恢复到它们原来的状态。

  1. unsigned long flags;
  2. local_irq_save(flags); /* 禁止中断 */
  3. /**/
  4. local_irq_restore(flags); /* 中断被恢复到它们原来的状态 */

        这些方法至少部分要以宏的形式实现,因此表面上flags参数是以值传递的。该参数包含具体体系结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合(SPARC),因此flags不能传递给另一个函数(特别是它必须驻留在同一栈帧中)。基于这个原因,对local_irq_save()的调用和对local_irq_restore()的调用必须在同一个函数中进行。

        接下来使用misc_register(&misc)来注册设备。参数&misc的类型为struct miscdevice,在Linux中,miscdevice是特殊的字符设备,注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,无需使用mknod指令,这是因为misc_register()会调用device_create()device_creater()函数会以主设备号10跟次设备号来注册设备并创建设备节点。在include/linux/miscdevice.h文件中,要把自己的miscdevice从设备号定义在这里,如果不定义的话,可以使用MISC_DYNAMIC_MINOR参数,这样内核会自动分配一个从设备号。

        应用层的ioctl调用,最终会落在函数 sbc2440_leds_ioctl中,在此函数中,实际操GPIO口时是使用 s3c2410_gpio_setpin来写相应的数据寄存器。

        代码的其它部分都很明了,不再赘述。


阅读(3596) | 评论(0) | 转发(1) |
0

上一篇:NEWT库

下一篇:S3C2440 LED 驱动浅析(2)

给主人留下些什么吧!~~