前面我们编写了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就是混杂设备的意思)。
        驱动代码:
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>

#include "bcm2835.h"

// Blinks on RPi Plug P1 pin 11 (which is GPIO pin 17)
#define PIN RPI_GPIO_P1_11

int open_state = 0;         //文件打开状态

static int leds_open(struct inode *inode, struct file *filp)
{
    if(open_state == 0)
    {
        open_state =  1;
        printk("Open file suc!\n");
        return 0;
    }
    else
    {
        printk("The file has opened!\n");
        return -1;
    }
}

static int leds_ioctl(struct file*filp, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case 0:
            bcm2835_gpio_clr(PIN);
            printk("LED OFF!\n");
            break;
        case 1:
            bcm2835_gpio_set(PIN);
            printk("LED ON!\n");
            break;  

        default:
            return-EINVAL;
    }  

    return 0;
}

static int leds_release(struct inode *inode, struct file *filp)
{
    if(open_state == 1)
    {
        open_state =  0;
        printk("close file suc!\n");
        return 0;
    }
    else
    {
        printk("The file has closed!\n");
        return -1;
    }
}

static const struct file_operations leds_fops = {
    .owner = THIS_MODULE,
    .open = leds_open,
    .unlocked_ioctl = leds_ioctl,
    .release = leds_release,
};

static struct miscdevice misc = {
    .minor =MISC_DYNAMIC_MINOR,
    .name ="my_leds",
    .fops =&leds_fops,
};

static int __init leds_init(void)
{
    int ret;

    //注册混杂设备
    ret =misc_register(&misc);

    //配置功能选择寄存器为输出
    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);

    //设置输出电平为高电平,LED亮
    bcm2835_gpio_set(PIN);

    printk("ledsinit.\n");
    return ret;
}

static void leds_exit(void)
{
    //LED灭
    bcm2835_gpio_clr(PIN);

    misc_deregister(&misc);        

    printk("leds_exit\n");
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_AUTHOR("Hu Chunxu");
MODULE_LICENSE("GPL");

硬件相关操作:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>

#include "bcm2835.h"

int bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
{
    //初始化GPIOB功能选择寄存器的物理地址
    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
    volatile uint32_t * bcm2835_gpio_fsel = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);
    uint8_t   shift = (pin % 10) * 3;
    uint32_t  value = mode << shift;
    *bcm2835_gpio_fsel = *bcm2835_gpio_fsel | value;

    printk("fsel address: 0x%lx : %x\n", bcm2835_gpio_fsel, *bcm2835_gpio_fsel);

    return 0;
}

int bcm2835_gpio_set(uint8_t pin)
{
    //GPIO输出功能物理地址
    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
    volatile uint32_t * bcm2835_gpio_set = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32;
    uint8_t   shift = pin % 32;
    uint32_t  value = 1 << shift;
    *bcm2835_gpio_set = *bcm2835_gpio_set | value;

    printk("set address:  0x%lx : %x\n", bcm2835_gpio_set, *bcm2835_gpio_set);

    return 0;
}

int bcm2835_gpio_clr(uint8_t pin)
{
   //GPIO清除功能物理地址
    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);
    volatile uint32_t * bcm2835_gpio_clr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32;
    uint8_t   shift = pin % 32;
    uint32_t  value = 1 << shift;
    *bcm2835_gpio_clr = *bcm2835_gpio_clr | value;

    printk("clr address:  0x%lx : %x\n", bcm2835_gpio_clr, *bcm2835_gpio_clr);

    return 0;
}

应用测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
	int on;
	int fd;
	if (argc != 2 || sscanf(argv[1],"%d", &on) != 1 ||on < 0 || on > 1 ) {
		fprintf(stderr, "Usage:%s 0|1\n",argv[0]);
		exit(1);
	}
	fd = open("/dev/my_leds", 0);
	if (fd < 0) {
		perror("open device leds");
		exit(1);
	}
	/*通过ioctl来控制灯的亮、灭*/
	if(on){
		printf("turn on leds!\n");
		ioctl(fd, 1);
	}
	else {
		printf("turn off leds!\n");
		ioctl(fd, 0);
	}
	close(fd);
	return 0;
}

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


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

欢迎大家转载我的文章。

转载请注明:转自古-月

http://blog.csdn.net/hcx25909

欢迎继续关注我的博客

树莓派linux驱动学习之LED控制的更多相关文章

  1. 树莓派linux驱动学习之hello world

    最近想学习一下linux驱动,看了一些书和教学视频,大概了解了一下,不过要想深入,肯定需要实践.手上有几块linux的板子,最终选择了树莓派作为我的实验平台,资料比较丰富,接口也比较简单. 程序员的入 ...

  2. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  3. linux驱动学习(二) Makefile高级【转】

    转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...

  4. 超简单易用的 “在 pcduino 开发板上写 Linux 驱动控制板载 LED 的闪烁”

    版权声明:本文为博主原创文章,未经博主同意不得转载.转载联系 QQ 30952589,加好友请注明来意. https://blog.csdn.net/sleks/article/details/251 ...

  5. Linux驱动开发之LED驱动

    首先讲下字符设备控制技术 : 大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力.比如: 改变波特率. 在用户空间,使用ioctl系统调用来控制设备,原型如下:int ioctl(i ...

  6. Linux驱动学习之常用的模块操作命令

    1.常用的模块操作命令 (1)lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表 (2)insmod(install module,安装模块),功能是向当前 ...

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

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

  8. Linux驱动学习步骤(转载)

    1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, ls ...

  9. linux驱动学习之Input输入子系统

    以前,看过国嵌关于input子系统的视频课程,说实话,我看完后脑子里很乱,给我的印象好像是input子系统驱动是一个全新的驱动架构,疑惑相当多.前几天在网上,看到有很多人介绍韦东山老师的linux驱动 ...

随机推荐

  1. lua 序列化函数

    local function f( ... ) print('hello') end local x = string.dump(f, true) loadstring(x)()

  2. RxJava(十一)defer操作符实现代码支持链式调用

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52597643 本文出自:[余志强的博客] 一.前言 现在越来越多An ...

  3. ROS机器人程序设计(原书第2版)学习镜像分享及使用说明

    ROS机器人程序设计(原书第2版)学习镜像分享及使用说明 系统用于ROS爱好者学习交流,也可用于其他用途,并不局限于ROS. 这款镜像文件是基于一年前的Ubuntu ROS Arduino Gazeb ...

  4. Unity 资源管理插件

    之所以写这个插件呢,就是为了方便整理项目中的资源文件,我记得之前好像也用了这么一个插件,但是也没去找,还是自己动手写一个吧,需要什么功能就看自己的需求. 在项目的过程中呢,已经写了一个插件来管理材质, ...

  5. AR模块常用函数

    --AR模块常用函数 FUNCTION get_fnd_user_name ( p_user_id IN NUMBER ) return VARCHAR2 IS CURSOR c_user_name ...

  6. 在Windows Service 2012上安装IIS 8.0 IIS 6

    我的目的是在服务器上安装IIS6 ,但是受到这边文章的启发和按照他的步骤,看到了"IIS 6管理兼容性",我的问题就决解了,我这里是因为要安装vss 2005 和u8等比较早期的软 ...

  7. 微软Telnet的回显功能开启

    win7和XP系统默认telnet的回显功能是关闭的.启用telnet回显功能:(1)首先进入命令行界面:输入telnet(2)进入Microsoft Telnet>命令提示符下,输入:set ...

  8. 剑指Offer——银行考试

    剑指Offer--银行考试 网申简历 一. 银行网申简历主要看哪些方面? 1.职业形象(30%),基本体现为证件照: 2.学校+成绩+校内表现(40%),体现为证书,成绩排名以及任职经历等: 3.校外 ...

  9. 04springMVC结构,mvc模式,spring-mvc流程,spring-mvc的第一个例子,三种handlerMapping,几种控制器,springmvc基于注解的开发,文件上传,拦截器,s

     1. Spring-mvc介绍 1.1市面上流行的框架 Struts2(比较多) Springmvc(比较多而且属于上升的趋势) Struts1(即将被淘汰) 其他 1.2  spring-mv ...

  10. How to migrate data from another Mac using Mountain Lion and earlier

    链接:http://support.apple.com/zh-cn/HT4889