【玩转开源】BananaPi R2——移植RPi.GPIO 到 R2

1. 首先给大家介绍一下什么是RPi.GPIO.

简单去讲,RPi.GPIO就是一个运行在树莓派开发板上可以通过Python去控制GPIO的一个中间件。

现在我这边做了一个基础功能的移植,接下来大家可以跟着我去学习一下RPi.GPIO是如何通过Python去实现控制开发板上的GPIO的。

2. 看一下效果图:

2.1 硬件实物运行效果

2.2 执行Python脚本打印的log

3. 那么RPi.GPIO在R2上是如何使用的呢?

3.1 首先在R2上面运行一个Ubuntu镜像,然后下载代码:git clone https://github.com/BPI-SINOVOIP/RPi.GPIO

这里如果不清楚如何安装一个Ubuntu镜像到R2,欢迎留言。

3.2 安装Python环境

1 sudo apt-get update
2 sudo apt-get install python-dev python3-dev

3.3 安装中间件

sudo python setup.py install
或者是 sudo python3 setup.py install 来安装
(前者是使用Python2.X,后者是Python3.X)

安装参考:http://wiki.banana-pi.org/Getting_Started_with_R2#Install_RPi.GPIO(偷偷告诉你们这个wiki我也在维护哦,欢迎大家留言提建议)

3.4 安装完成后进入到R2 Ubuntu下的路径:cd /usr/local/bin,你会发现有个g40.py的python文件,我们可以打开简单看一下(这里代码的注释,是我加的,目的是为了让大家更好理解这段Python程序):

#!/usr/bin/python
import RPi.GPIO as GPIO  #这里导入RPi.GPIO模块,重命名为GPIO
import time              #为了方便理解import xxx, 大家可以当作是C语言的#include<xxx.h>
#定义两组要控制的Led GPIO pin数组
phy_led2 = [8, 10, 12, 16, 18, 22, 24, 26, 28, 32, 36, 38, 40,
        37, 35, 33, 31, 29, 27, 23, 21, 19, 15, 13, 11, 7, 5, 3];

phy_led = [8, 10, 12, 16, 18, 22, 24, 26, 32, 36, 38, 40,
        37, 35, 33, 31, 29, 23, 21, 19, 15, 13, 11, 7, 5, 3];

print ‘Pi Board Information‘
print ‘---------------------‘
#这里是一个for循环, 从GPIO.RPI_INFO.items里面读取数据,并打印出来for key,val in GPIO.RPI_INFO.items():
    print ‘%s => %s‘%(key,val)#读取键盘输入
response = raw_input(‘\nIs this board info correct (y/n) ? ‘).upper()
#调用GPIO.setmode方法,并传入参数GPIO.BOARD#(这里大家肯定看得一愣一愣的,这些GPIO.XXX方法,参数究竟是在哪里定义的,不要急,后面就会介绍)
GPIO.setmode(GPIO.BOARD)
#读取phy_led数组,并调用GPIO.setup方法,传入参数pin和GPIO.OUT
for pin in phy_led:
  print pin, "GPIO.setup GPIO.OUT"
  GPIO.setup(pin, GPIO.OUT)
#这里是一个死循环,意思就是设置GPIO循环输出高低电平不断打开,关闭LED
while True:
  for pin in phy_led:
    GPIO.output(pin, True)
    print ‘on ‘, pin
    time.sleep(.1)
  for pin in phy_led:
    GPIO.output(pin, False)
    print ‘off ‘,pin
    time.sleep(.1)

#如果大家对Python不太熟悉,建议可以先去学习一些基础的语法,这样有助于理解。#不过不熟悉也没有关系,这里我也会尽量讲明白这个Python文件是在做什么。

3.5 最上层的接口看完了,接下来我们该看中间件是如何实现上面这些接口的调用的

下载代码:git clone https://github.com/BPI-SINOVOIP/RPi.GPIO

代码下载完成后,我们打开先简单看一看,我们从g40.py里面调用的第一个接口看起:GPIO.RPI_INFO.items()

我的开发环境是Ubuntu,使用的IDE是SourceInsight

我们先搜索一下接口RPI_INFO:

发现这个接口三在Py_gpio.c里面有出现,我们点进去看一下:

原来这个接口在这里,那这里的PyModule_AddObject是干什么的呢?先看一下官方解释:

这个接口实际上是Python调用外部代码的一种方式,简单理解就是可以通过 “Module.name” 的方式来调用 "value".

然后我们再来看一下g40.py里面的这段代码:

for key,val in GPIO.RPI_INFO.items():
    print ‘%s => %s‘%(key,val)

Module的名称为GPIO,name是"RPI_INFO",这里使用GPIO.RPI_INFO实际上就调用到了value,根据上面代码的定义,这个value是board_info,那么这段代码实际上就是调用board_info,接下来我们再看看board_info是怎么定义的.

board_info = Py_BuildValue("{sissssssssss}",
                              "P1_REVISION",rpiinfo.p1_revision,
                              "REVISION",&rpiinfo.revision,
                              "TYPE",rpiinfo.type,
                              "MANUFACTURER",rpiinfo.manufacturer,
                              "PROCESSOR",rpiinfo.processor,
                              "RAM",rpiinfo.ram);

这里可以看到board_info是从Py_BuildValue这里获取的值,那么Py_BuildValue又是做什么的呢?先看一下官方的解释:

意思就是获取C语言格式的字符串,看起来这里Python和C开始有联系咯,我们看看最开始的打印:

看到这里的打印,和上面Py_BuildValue()里面的参数对比一下,是不是开始有感觉了;没错这里已经成功从Python调用到C了.

我们接下来再看看g40.py,代码马上执行到:GPIO.setmode(GPIO.BOARD),对应的我们去看源码是怎么样的:

首先我们找一下这个BOARD参数,可以看到这个参数是一个int类型的变量;

接下来我们再看看setmode方法:

这里可以看到setmode方法实际上是调用的py_setmode,我们再看看这个py_setmode是怎么定义的:

看到这里实际上我们会发现,其实就是C的写法了,简单去看就是返回一个PyObject* 类型的C函数;到这里我们已经大概学习到Python如何调用C的参数和函数了.

4.  那么 R2 上的GPIO又是如何实现被控制的呢?

在这里大家有没有注意到,当我们运行g40.py的时候,实际上里面也是运行的C代码,再挖一层实际上当执行这个Python脚本就相当于就是执行的C代码,换句话说,就是我们只要实现了如何通过C去控制GPIO,那么就能实现Python去控制GPIO;那么我们要如何去实现C代码控制GPIO呢?

这里插入两个知识点:用户态,内核态
Linux系统运行的时候实际上是分内核态和用户态的,内核态和用户态之间相互分隔,但是可以通过一些特殊的接口让他们之间相互访问。比如我们进到Ubuntu系统,GUI,桌面软件,命令行等等这些实际上都是用户态的操作。一般涉及到较深层次的硬件驱动接口时,才会去操作内核态,比如我们开发嵌入式拿到的BSP代码,去写的各个硬件驱动代码一般就是属于内核部分。

如果你使用过单片机,你可能还记得操作GPIO,直接去给对应的寄存器赋值就好了,没错思路是对的;那么我们如何通过Ubuntu系统去写芯片GPIO的寄存器呢?

这里我直接使用mmap的办法,即内存映射,暂时就不发散用户态如何调用到内核态的方法了,后续写关于嵌入式的知识时再介绍.

那么我们看看mmap是做什么用处的:

简单去讲,就是虚拟地址映射到实际物理地址的方法;我们知道在单片机上面操作的寄存器地址,实际上就是它的物理地址;但是嵌入式系统不一样,嵌入式系统有一个内存管理的机制,我们叫MMU,具体MMU的原理暂时也不发散了,后续有机会再介绍(后面会专门再写一篇关于MMU的文章).

通过查询芯片的Datasheet,我们可以找到GPIO的实际寄存器地址,然后读取出来后,就可以像单片机操作GPIO那样去写程序了,这里我贴R2的例子:

我们要操作的GPIO口是这些:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MMAP_PATH    "/dev/mem"

static uint8_t* gpio_mmap_reg = NULL;
static int gpio_mmap_fd = 0;
//这里定义GPIO 原理图里面的 PIN
int pins[] = {75, 76, 206, 80, 79, 205, 56, 55, 54, 57, 126, 74, 73, 49, 202,
              82, 81, 24, 25, 21, 18, 53, 20, 58, 72, 19, 22, 200};
//芯片GPIO寄存器
#define GPIO_DOUT_BASE_OFFSET  0x500
#define GPIO_MODE_BASE_OFFSET  0x760
#define GPIO_REG_BASE          0x10005000

static int gpio_mmap(void)
{
    if ((gpio_mmap_fd = open(MMAP_PATH, O_RDWR|O_SYNC)) < 0) {
        fprintf(stderr, "unable to open mmap file");
        return -1;
    }

    gpio_mmap_reg = (uint8_t*) mmap(NULL, 1024, PROT_READ | PROT_WRITE,
        MAP_FILE | MAP_SHARED , gpio_mmap_fd, GPIO_REG_BASE);
    if (gpio_mmap_reg == MAP_FAILED) {
        perror("foo");
        fprintf(stderr, "failed to mmap");
        gpio_mmap_reg = NULL;
        close(gpio_mmap_fd);
        return -1;
    }

    return 0;
}

int main()
{
    int ret = -1;
    int pin = 75;

    if (gpio_mmap())
        return -1;
    printf("set dir\n");
    uint32_t tmp;
    int position = 0;
    //SET MODE AS GPIO,GPIO模式的值为0
    printf("BASEADDR=%X\nSET MODE AS GPIO\n", gpio_mmap_reg);
    for(int i = 0; i < sizeof(pins)/sizeof(int); i++){
        pin = pins[i];
        position = gpio_mmap_reg + GPIO_MODE_BASE_OFFSET + (pin / 5) * 16;
        printf("pin=%d, positon = %X\n", pin, position);
        tmp = *(volatile uint32_t*)(position);
        tmp &= ~(1u << ((pin % 5) * 3));    //赋值为0
        *(volatile uint32_t*)(position) = tmp;

    }

    printf("\nSET DIR AS OUT\n");
    for(int i = 0; i < sizeof(pins)/sizeof(int); i++){
        pin = pins[i];
        if(pin < 199){
            position = gpio_mmap_reg + (pin / 16) * 16;
        }else{
            position = gpio_mmap_reg + (pin / 16) * 16 + 0x10;
        }
      printf("pin=%d, positon = %X\n", pin, position);
        tmp = *(volatile uint32_t*)(position);
      tmp |= (1u << (pin % 16));
  *(volatile uint32_t*)(position) = tmp;
        usleep(100000);
    }

    //SET VAULE
    printf("\nSET VALUE AS HIGH LEVEL\n");
    for(int i = 0; i < sizeof(pins)/sizeof(int); i++){
        pin = pins[i];
      position = gpio_mmap_reg + GPIO_DOUT_BASE_OFFSET + (pin / 16) * 16;
      printf("pin=%d, positon = %X\n", pin, position);
      tmp = *(volatile uint32_t*)(position);
      tmp |= (1u << (pin % 16));
      *(volatile uint32_t*)(position) = tmp;
      usleep(100000);
    }

  printf("\nSET VALUE AS LOW LEVEL\n");
    for(int i = 0; i < sizeof(pins)/sizeof(int); i++){
        pin = pins[i];
        position = gpio_mmap_reg + GPIO_DOUT_BASE_OFFSET + (pin / 16) * 16;
      printf("pin=%d, positon = %X\n", pin, position);
        tmp = *(volatile uint32_t*)(position);
        printf("tmp = %X\n", tmp);
      tmp &= ~(1u << (pin % 16));
      printf("tmp = %X\n", tmp);
      *(volatile uint32_t*)(position) = tmp;
        usleep(100000);
    }
    close(gpio_mmap_fd);
}

上面这个例子就实现了用户态操作GPIO的方法,具体地址,和运算方法跟你的芯片GPIO寄存器定义有关,看到这里如果有很多东西还是不太明白也没有关系,因为这里涉及的知识面稍微多一些,不过不要灰心,自己多实战,再回过头来看其实就变简单了,关于嵌入式的学习也欢迎大家一起留言交流.

更多的实现请大家看github上的commit记录:https://github.com/BPI-SINOVOIP/RPi.GPIO/commits/master

原文地址:https://www.cnblogs.com/topbin/p/10577137.html

时间: 2024-11-18 05:14:34

【玩转开源】BananaPi R2——移植RPi.GPIO 到 R2的相关文章

【玩转开源】BananaPi R2 —— 第二篇 Openwrt 网口配置分析

上次和大家分享了如何烧录和安装Openwrt到BananaPi R2,运行Openwrt的R2目前就具备路由器的功能了,这次我们来看看R2运行Openwrt的性能如何,同时也会讲解一些常用的网络知识. 首先看一下硬件上的设计: 可以看出硬件上,R2设计了一个wan口和4个lan口: 这里可能有人会有疑问,什么是wan口,什么是lan口? wan(Wide Area Network)表示广域网,lan(Local Area Network)表示局域网:简单的讲就是wan口是连接Internet上网

树莓派瞎玩~7~RPi.GPIOのWIKI文档

树莓派瞎玩~7~RPi.GPIOのWIKI文档 RPiGPIO Python Module RPiGPIO module basics Importing the module Pin numbering Warnings Setup up a channel Setup more than one channel Input Output Output to several channels Cleanup RPi Board Information and RPiGPIO version I

实现自己的RPi.GPIO(三)-Python C API

Python C 的扩展按照模板来就行了,其实就4步:参数传递,返回值传递,函数注册,初始化. 直接上代码: 1 /* 2 * PyGPIO.h 3 * 4 * Created on: 2015年5月19日 5 * Author: jugg 6 */ 7 8 #ifndef SRC_PYGPIO_H_ 9 #define SRC_PYGPIO_H_ 10 11 #include <errno.h> 12 #include <string.h> 13 #include <std

实现自己的RPi.GPIO(一)-Python C Extension

树莓派有个RPi.GPIO的Python模块,import之后就可以直接用Python操作ARM芯片的GPIO硬件模块.觉得这个很有意思,于是查资料想在自己画的DM8148平台上面也实现这样的一个模块. DM8148是TI推出的一款达芬奇系列数字图像处理芯片,比DM8168性能略差,但是功耗更低.之前公司的很多图像处理都是用的DM8168,但是散热和功耗问题一直很让人头疼,于是就让我预研更低功耗的DM8148.DM8148可以理解为ARM+DSP的异构核心,我们要用到的就是它的一颗Cortex

部署FIM 2010 R2&mdash;4安装FIM 2010 R2 Synchronization Service

部署FIM 2010 R2-4安装FIM 2010 R2 Synchronization Service 1.打开FIM2010 R2安装控制台,选择"Install Synchronization Service" 2.选择"下一步", 同意许可,选择"下一步", 保持默认, 3.为FIM同步服务指定数据库和实例, 4.为FIM同步服务指定服务账户,如下图,(注:Domain必须写为contoso,而不是contoso.com) 5.使用默认组

部署FIM 2010 R2&mdash;3安装FIM 2010 R2 Service and Portal

部署FIM 2010 R2-3安装FIM 2010 R2 Service and Portal 1.打开FIMSplash文件,如下图, 选择"Install Service and Portal", 2.选择"下一步", 选择"下一步", 选择"下一步", 3.选择要安装的角色,这里我们全部安装, 4.为FIM服务器指定SQL 2008数据库服务器名称以及FIM数据库名称,这里使用本地数据库服务,数据库名称使用默认名称, 5

Oracle 10g R2升级到Oracle 11g R2

Oracle 10g R2升级到Oracle 11g R2 系统环境: 操作系统:RedHat EL55 Oracle 软件: Oracle 10g R2.Oracle 11g R2 Oracle 升级线路 本案例是从Oracle 10.2.0.4.0升级到Oracle 11.2.0.1.0 参考文档: Oracle patch CPU Update: http://tiany.blog.51cto.com/513694/846066 Oracle 10g 升级(10.2.0.1.0升级到10.

(转)为什么adrl r2,mem_cfg_val这里不用ldr r2,=mem_cfg_val

网址:http://blog.csdn.net/glorin/article/details/6327083 memsetup:mov r1, #MEM_CTL_BASEadrl r2,mem_cfg_valadd r3,  r1,#521:ldr r4,  [r2],#4str r4,  [r1],#4cmp r1,r3bne 1bmov pc,lrmem_cfg_val:  .long 0x22011110  .long 0x00000700  .long 0x00000700  ·····

从开源编译器中移植代码的正确姿势

转眼间已经到了大三下学期了,马上就要实习了,最后一个学期我会好好珍惜的.为了让这个学期过的有格调,我打算每一件事情都做得有逼格一点. 开学第三周我们学校就开始陆陆续续有实验课了,做一个词法分析器? 好嘞,劳资要用Sourceinsight把开源的GCC里面的词法分析代码全部移植出来,就问你牛不牛批!!! 脑海中已经浮现出了助教对我异样的眼光,哈哈,不多说了,盘它!!!!!! 首先,进入gcc的官网,找到它最旧的一个版本(好像88年就有gcc了),但是我在官网上只能找到98年的.整个文件也才几百K