树莓派linux驱动学习之LED控制

        前面我们编写了hello world的程序,接下来继续研究GPIO功能,通过GPIO来控制LED的亮灭,这在单片机中应该算是十分简单的一个程序了,但是在Linux系统中控制GPIO没有那么简单,难点就在于GPIO地址的获取,也是我一直在纠结的问题。

一、GPIO地址

        我看了中嵌的嵌入式开发视频,里面使用三星2440控制LED的亮灭,但是驱动程序中没有写清楚具体的底层是如何实现的,这也是我查找的重点。我首先翻阅了树莓派CPU(bcm2835 )的芯片手册,查到了GPIO的物理地址:
树莓派linux驱动学习之LED控制_第1张图片
        但是在芯片资料的最开始,有提到芯片内部已经把上图中的物理总线地址抽象到了面对操作系统的物理地址:

树莓派linux驱动学习之LED控制_第2张图片
        所以,我们在编写驱动程序的时候,IO空间的起始地址是0x20000000,加上GPIO的偏移量2000000,所以GPIO的物理地址应该是从0x20200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,银蛇到虚拟地址上。

二、硬件平台

        我在树莓派的扩展口的GPIO 17上接上了一个LED:
树莓派linux驱动学习之LED控制_第3张图片
树莓派linux驱动学习之LED控制_第4张图片

三、编写驱动代码

        一般的设备驱动我们需要设置主设备号和次设备号,在编写应用程序的时候还要生成设备文件,比较麻烦。 Linux针对像LED这样的操作,有一种设备叫做混杂设备:是一种特殊的字符设备,杂设备早已经存在,是为了给开发者一个较为简单的操作方式,因为不用再重新申请一个设备号了(misc就是混杂设备的意思)。
        驱动代码:
[cpp]  view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12. #include   
  13. #include   
  14. #include   
  15. #include   
  16. #include   
  17. #include   
  18. #include   
  19. #include   
  20. #include   
  21. #include   
  22. #include   
  23. #include   
  24. #include   
  25.   
  26. #include "bcm2835.h"  
  27.   
  28. // Blinks on RPi Plug P1 pin 11 (which is GPIO pin 17)  
  29. #define PIN RPI_GPIO_P1_11  
  30.   
  31. int open_state = 0;         //文件打开状态  
  32.   
  33. static int leds_open(struct inode *inode, struct file *filp)  
  34. {  
  35.     if(open_state == 0)    
  36.     {    
  37.         open_state =  1;    
  38.         printk("Open file suc!\n");    
  39.         return 0;    
  40.     }    
  41.     else    
  42.     {    
  43.         printk("The file has opened!\n");    
  44.         return -1;    
  45.     }    
  46. }  
  47.   
  48. static int leds_ioctl(struct file*filp, unsigned int cmd, unsigned long arg)  
  49. {  
  50.     switch(cmd)    
  51.     {    
  52.         case 0:    
  53.             bcm2835_gpio_clr(PIN);  
  54.             printk("LED OFF!\n");  
  55.             break;    
  56.         case 1:    
  57.             bcm2835_gpio_set(PIN);  
  58.             printk("LED ON!\n");  
  59.             break;    
  60.   
  61.         default:    
  62.             return-EINVAL;    
  63.     }    
  64.   
  65.     return 0;  
  66. }  
  67.   
  68. static int leds_release(struct inode *inode, struct file *filp)  
  69. {  
  70.     if(open_state == 1)    
  71.     {    
  72.         open_state =  0;    
  73.         printk("close file suc!\n");    
  74.         return 0;    
  75.     }    
  76.     else    
  77.     {    
  78.         printk("The file has closed!\n");    
  79.         return -1;    
  80.     }    
  81. }  
  82.   
  83. static const struct file_operations leds_fops = {  
  84.     .owner = THIS_MODULE,  
  85.     .open = leds_open,  
  86.     .unlocked_ioctl = leds_ioctl,  
  87.     .release = leds_release,  
  88. };  
  89.   
  90. static struct miscdevice misc = {  
  91.     .minor =MISC_DYNAMIC_MINOR,  
  92.     .name ="my_leds",  
  93.     .fops =&leds_fops,  
  94. };  
  95.   
  96.   
  97. static int __init leds_init(void)  
  98. {  
  99.     int ret;  
  100.   
  101.     //注册混杂设备  
  102.     ret =misc_register(&misc);  
  103.   
  104.     //配置功能选择寄存器为输出  
  105.     bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);  
  106.   
  107.     //设置输出电平为高电平,LED亮  
  108.     bcm2835_gpio_set(PIN);  
  109.   
  110.     printk("ledsinit.\n");  
  111.     return ret;  
  112. }  
  113.   
  114. static void leds_exit(void)  
  115. {  
  116.     //LED灭  
  117.     bcm2835_gpio_clr(PIN);  
  118.   
  119.     misc_deregister(&misc);          
  120.   
  121.     printk("leds_exit\n");  
  122. }  
  123.   
  124. module_init(leds_init);  
  125. module_exit(leds_exit);  
  126.   
  127. MODULE_AUTHOR("Hu Chunxu");  
  128. MODULE_LICENSE("GPL");  
    硬件相关操作:
[cpp]  view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12. #include   
  13. #include   
  14. #include   
  15. #include   
  16. #include   
  17. #include   
  18. #include   
  19. #include   
  20. #include   
  21. #include   
  22. #include   
  23. #include   
  24. #include   
  25.   
  26. #include "bcm2835.h"  
  27.   
  28. int bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)  
  29. {  
  30.     //初始化GPIOB功能选择寄存器的物理地址  
  31.     volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);  
  32.     volatile uint32_t * bcm2835_gpio_fsel = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);  
  33.     uint8_t   shift = (pin % 10) * 3;  
  34.     uint32_t  value = mode << shift;  
  35.     *bcm2835_gpio_fsel = *bcm2835_gpio_fsel | value;  
  36.   
  37.     printk("fsel address: 0x%lx : %x\n", bcm2835_gpio_fsel, *bcm2835_gpio_fsel);  
  38.   
  39.     return 0;  
  40. }  
  41.   
  42. int bcm2835_gpio_set(uint8_t pin)  
  43. {  
  44.     //GPIO输出功能物理地址  
  45.     volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);  
  46.     volatile uint32_t * bcm2835_gpio_set = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32;  
  47.     uint8_t   shift = pin % 32;  
  48.     uint32_t  value = 1 << shift;  
  49.     *bcm2835_gpio_set = *bcm2835_gpio_set | value;  
  50.   
  51.     printk("set address:  0x%lx : %x\n", bcm2835_gpio_set, *bcm2835_gpio_set);  
  52.   
  53.     return 0;  
  54. }  
  55.   
  56. int bcm2835_gpio_clr(uint8_t pin)  
  57. {  
  58.    //GPIO清除功能物理地址  
  59.     volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);  
  60.     volatile uint32_t * bcm2835_gpio_clr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32;  
  61.     uint8_t   shift = pin % 32;  
  62.     uint32_t  value = 1 << shift;  
  63.     *bcm2835_gpio_clr = *bcm2835_gpio_clr | value;  
  64.       
  65.     printk("clr address:  0x%lx : %x\n", bcm2835_gpio_clr, *bcm2835_gpio_clr);  
  66.   
  67.     return 0;  
  68. }  

        应用测试程序:
[cpp]  view plain copy
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5.   
  6. int main(int argc, char **argv)  
  7. {  
  8.     int on;  
  9.     int fd;  
  10.     if (argc != 2 || sscanf(argv[1],"%d", &on) != 1 ||on < 0 || on > 1 ) {  
  11.         fprintf(stderr, "Usage:%s 0|1\n",argv[0]);  
  12.         exit(1);  
  13.     }  
  14.     fd = open("/dev/my_leds", 0);  
  15.     if (fd < 0) {  
  16.         perror("open device leds");  
  17.         exit(1);  
  18.     }  
  19.     /*通过ioctl来控制灯的亮、灭*/  
  20.     if(on){  
  21.         printf("turn on leds!\n");  
  22.         ioctl(fd, 1);  
  23.     }  
  24.     else {  
  25.         printf("turn off leds!\n");  
  26.         ioctl(fd, 0);  
  27.     }  
  28.     close(fd);  
  29.     return 0;  
  30. }  

      分别编译,插入模块,然后运行测试程序,可以控制LED的亮灭了。

----------------------------------------------------------------

欢迎大家转载我的文章。

你可能感兴趣的:(高级计算与工程)