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

一、GPIO地址

        我看了中嵌的嵌入式开发视频,里面使用三星2440控制LED的亮灭,但是驱动程序中没有写清楚具体的底层是如何实现的,这也是我查找的重点。我首先翻阅了树莓派CPU(bcm2835)的芯片手册,查到了GPIO的物理地址:
        但是在芯片资料的最开始,有提到芯片内部已经把上图中的物理总线地址抽象到了面对操作系统的物理地址:
        所以,我们在编写驱动程序的时候,IO空间的起始地址是0x20000000,加上GPIO的偏移量2000000,所以GPIO的物理地址应该是从0x20200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,银蛇到虚拟地址上。

二、硬件平台

        我在树莓派的扩展口的GPIO 17上接上了一个LED:

三、编写驱动代码

        一般的设备驱动我们需要设置主设备号和次设备号,在编写应用程序的时候还要生成设备文件,比较麻烦。Linux针对像LED这样的操作,有一种设备叫做混杂设备:是一种特殊的字符设备,杂设备早已经存在,是为了给开发者一个较为简单的操作方式,因为不用再重新申请一个设备号了(misc就是混杂设备的意思)。
        驱动代码:
[cpp] view plaincopy
  1. #include <linux/miscdevice.h>
  2. #include <linux/delay.h>
  3. #include <asm/irq.h>
  4. #include <linux/kernel.h>
  5. #include <linux/module.h>
  6. #include <linux/init.h>
  7. #include <linux/mm.h>
  8. #include <linux/fs.h>
  9. #include <linux/types.h>
  10. #include <linux/delay.h>
  11. #include <linux/moduleparam.h>
  12. #include <linux/slab.h>
  13. #include <linux/errno.h>
  14. #include <linux/ioctl.h>
  15. #include <linux/cdev.h>
  16. #include <linux/string.h>
  17. #include <linux/list.h>
  18. #include <linux/pci.h>
  19. #include <asm/uaccess.h>
  20. #include <asm/atomic.h>
  21. #include <asm/unistd.h>
  22. #include <asm/io.h>
  23. #include <asm/uaccess.h>
  24. #include <linux/ioport.h>
  25. #include "bcm2835.h"
  26. // Blinks on RPi Plug P1 pin 11 (which is GPIO pin 17)
  27. #define PIN RPI_GPIO_P1_11
  28. int open_state = 0;         //文件打开状态
  29. static int leds_open(struct inode *inode, struct file *filp)
  30. {
  31. if(open_state == 0)
  32. {
  33. open_state =  1;
  34. printk("Open file suc!\n");
  35. return 0;
  36. }
  37. else
  38. {
  39. printk("The file has opened!\n");
  40. return -1;
  41. }
  42. }
  43. static int leds_ioctl(struct file*filp, unsigned int cmd, unsigned long arg)
  44. {
  45. switch(cmd)
  46. {
  47. case 0:
  48. bcm2835_gpio_clr(PIN);
  49. printk("LED OFF!\n");
  50. break;
  51. case 1:
  52. bcm2835_gpio_set(PIN);
  53. printk("LED ON!\n");
  54. break;
  55. default:
  56. return-EINVAL;
  57. }
  58. return 0;
  59. }
  60. static int leds_release(struct inode *inode, struct file *filp)
  61. {
  62. if(open_state == 1)
  63. {
  64. open_state =  0;
  65. printk("close file suc!\n");
  66. return 0;
  67. }
  68. else
  69. {
  70. printk("The file has closed!\n");
  71. return -1;
  72. }
  73. }
  74. static const struct file_operations leds_fops = {
  75. .owner = THIS_MODULE,
  76. .open = leds_open,
  77. .unlocked_ioctl = leds_ioctl,
  78. .release = leds_release,
  79. };
  80. static struct miscdevice misc = {
  81. .minor =MISC_DYNAMIC_MINOR,
  82. .name ="my_leds",
  83. .fops =&leds_fops,
  84. };
  85. static int __init leds_init(void)
  86. {
  87. int ret;
  88. //注册混杂设备
  89. ret =misc_register(&misc);
  90. //配置功能选择寄存器为输出
  91. bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);
  92. //设置输出电平为高电平,LED亮
  93. bcm2835_gpio_set(PIN);
  94. printk("ledsinit.\n");
  95. return ret;
  96. }
  97. static void leds_exit(void)
  98. {
  99. //LED灭
  100. bcm2835_gpio_clr(PIN);
  101. misc_deregister(&misc);
  102. printk("leds_exit\n");
  103. }
  104. module_init(leds_init);
  105. module_exit(leds_exit);
  106. MODULE_AUTHOR("Hu Chunxu");
  107. MODULE_LICENSE("GPL");

硬件相关操作:

[cpp] view plaincopy
  1. #include <linux/miscdevice.h>
  2. #include <linux/delay.h>
  3. #include <asm/irq.h>
  4. #include <linux/kernel.h>
  5. #include <linux/module.h>
  6. #include <linux/init.h>
  7. #include <linux/mm.h>
  8. #include <linux/fs.h>
  9. #include <linux/types.h>
  10. #include <linux/delay.h>
  11. #include <linux/moduleparam.h>
  12. #include <linux/slab.h>
  13. #include <linux/errno.h>
  14. #include <linux/ioctl.h>
  15. #include <linux/cdev.h>
  16. #include <linux/string.h>
  17. #include <linux/list.h>
  18. #include <linux/pci.h>
  19. #include <asm/uaccess.h>
  20. #include <asm/atomic.h>
  21. #include <asm/unistd.h>
  22. #include <asm/io.h>
  23. #include <asm/uaccess.h>
  24. #include <linux/ioport.h>
  25. #include "bcm2835.h"
  26. int bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
  27. {
  28. //初始化GPIOB功能选择寄存器的物理地址
  29. volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
  30. volatile uint32_t * bcm2835_gpio_fsel = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);
  31. uint8_t   shift = (pin % 10) * 3;
  32. uint32_t  value = mode << shift;
  33. *bcm2835_gpio_fsel = *bcm2835_gpio_fsel | value;
  34. printk("fsel address: 0x%lx : %x\n", bcm2835_gpio_fsel, *bcm2835_gpio_fsel);
  35. return 0;
  36. }
  37. int bcm2835_gpio_set(uint8_t pin)
  38. {
  39. //GPIO输出功能物理地址
  40. volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
  41. volatile uint32_t * bcm2835_gpio_set = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32;
  42. uint8_t   shift = pin % 32;
  43. uint32_t  value = 1 << shift;
  44. *bcm2835_gpio_set = *bcm2835_gpio_set | value;
  45. printk("set address:  0x%lx : %x\n", bcm2835_gpio_set, *bcm2835_gpio_set);
  46. return 0;
  47. }
  48. int bcm2835_gpio_clr(uint8_t pin)
  49. {
  50. //GPIO清除功能物理地址
  51. volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
  52. volatile uint32_t * bcm2835_gpio_clr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32;
  53. uint8_t   shift = pin % 32;
  54. uint32_t  value = 1 << shift;
  55. *bcm2835_gpio_clr = *bcm2835_gpio_clr | value;
  56. printk("clr address:  0x%lx : %x\n", bcm2835_gpio_clr, *bcm2835_gpio_clr);
  57. return 0;
  58. }

应用测试程序:

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

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

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

欢迎大家转载我的文章。

树莓派linux驱动学习之LED控制相关推荐

  1. linux uart寄存器 代替 printk,Linux驱动学习之设备树(设备树下的LED驱动实验),...

    Linux驱动学习之设备树(设备树下的LED驱动实验), 概念 Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离.相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设 ...

  2. Linux驱动学习--USB接口wifi/BT芯片开发之BT开发(BlueDroid框架)

    目录 一.引言 二.整体框架分析(结合实际芯片分析) 三.内核中的相关配置 四.厂家驱动分析 五.蓝牙BlueDroid协议 一.引言 之前我们简单分析过BlueDroid框架,今天来结合源码,挑重点 ...

  3. IMX6ULL嵌入式Linux驱动学习笔记(二)

    IMX6ULL嵌入式Linux驱动学习 一.字符设备驱动 二.驱动模块的加载与卸载 三.字符设备的注册与注销 四.设备号 五.file_operations的具体实现 六.字符设备驱动框架 七.编写应 ...

  4. Linux 驱动学习笔记 - beep(九)

    Linux 驱动学习笔记 - beep(九) 本系列均为正点原子 Linux 驱动的学习笔记, 以便加深笔者记忆.如读者想进一步学习,可以到正点原子官网中下载资料进行学习. 添加 pinctrl 节点 ...

  5. 讯为4412开发板Linux驱动学习笔记

    驱动理论专题一 Linux驱动程序的基本认识 有了内存管理单元,就有虚拟地址,物理地址. 驱动理论专题二 学会查看原理图 以LED2为示例 通过原理图查看到KP_COL0,赋予高电平则能点亮LED2, ...

  6. Linux驱动学习笔记

    驱动学习笔记 1.字符设备驱动 Linux 驱动有两种运行方式 第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序. 第二种就是将驱动编译成模块(Li ...

  7. (学习)linux驱动学习知识积累(一)

    一.基础知识扫盲 1.dev_t结构体 在内核中,dev_t结构体用来保存设备编号信息,在linux/type.h中定义,是一个32位的数,12位表示主设备号+20位的次设备号 int MAJOR(d ...

  8. Linux驱动学习之什么是驱动?

    一.什么是驱动? 1: 驱动一词的字面意思 2: 物理上的驱动 3: 硬件中的驱动 4: linux内核驱动.软件层面上的驱动广义上是指:这一段代码操作了硬件去动,所以这一段代码就叫硬件的驱动程序. ...

  9. Linux驱动学习(一):什么是Linux驱动

    文章目录 前言 一.设备驱动简介 二.模块的编译和加载 总结 前言 一.设备驱动简介 驱动程序在 Linux 内核里扮演着特殊的角色. 它们是截然不同的"黑盒子", 使硬件的特殊的 ...

最新文章

  1. 使用git上传代码到github
  2. 文献记录(part91)--A boundary method for outlier detection based on support vector domain description
  3. mysql cookbook
  4. Qt 第二章 创建对话框--快速设计对话框(2)
  5. C语言 找出任意两整数之间的素数以及他们的和
  6. excel删除行 uipath_工作再忙,也要学会这10个最经典的Excel小技巧
  7. 1个app的完整测试用例_同你分享1个完整的聚类分析案例
  8. 互联网日报 | 8月3日 星期二 | 陌陌宣布更名为“Hello”集团;国家电网升至世界500强第二位;比亚迪总市值超8300亿...
  9. mysql正则mybatis中用法_SQL正则表达式及mybatis中使用正则表达式
  10. 微信僵尸粉删除工具 WeTool v4.0.7.0 免费版
  11. 【Week 8 作业 B】猫猫向前冲
  12. 详解 ARM PMU (Performance Monitoring Unit)
  13. openresty ngx_lua重定向
  14. 远程粒子计数器助力药企环境监测 为洁净卫生护航
  15. Andorid微信刷脸支付使用过程解析
  16. go语言学习-- chan与goroutine
  17. 可以测试流放之路伤害的软件,测试平台及细节一览 - 《流放之路》国服硬件需求测试:低配也能续写ARPG传奇 - 超能网...
  18. CVE-2020-14364:QEMU USB模块越界读写漏洞通告
  19. Mac回收站清空还能恢复吗?2个方法快速找回废纸篓清空文件
  20. 省级面板数据(2000-2019)八:人民生活篇(城乡人均收入、支出、耐消品、住房)(stata版)

热门文章

  1. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )
  2. 【嵌入式开发】ARM 芯片简介 (ARM芯片类型 | ARM处理器工作模式 | ARM 寄存器 | ARM 寻址)
  3. issubclass和isinstance 反射 内置方法(魔术方法)
  4. Autodesk布道GIS新理念
  5. Java(CS)请求分流
  6. windows下定时利用bat脚本实现ftp上传和下载
  7. statspack系列8
  8. ThinkPad E440 加内存后导致开不了机
  9. UpdatePanel 后台注册脚本失效
  10. MySQL数据导入oracle