【玩转开源】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环境
sudo apt-get update
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 = ;
//这里定义GPIO 原理图里面的 PIN
int pins[] = {, , , , , , , , , , , , , , ,
, , , , , , , , , , , , };
//芯片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)) < ) {
fprintf(stderr, "unable to open mmap file");
return -;
} gpio_mmap_reg = (uint8_t*) mmap(NULL, , 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 -;
} return ;
} int main()
{
int ret = -;
int pin = ; if (gpio_mmap())
return -;
printf("set dir\n");
uint32_t tmp;
int position = ;
//SET MODE AS GPIO,GPIO模式的值为0
printf("BASEADDR=%X\nSET MODE AS GPIO\n", gpio_mmap_reg);
for(int i = ; i < sizeof(pins)/sizeof(int); i++){
pin = pins[i];
position = gpio_mmap_reg + GPIO_MODE_BASE_OFFSET + (pin / ) * ;
printf("pin=%d, positon = %X\n", pin, position);
tmp = *(volatile uint32_t*)(position);
tmp &= ~(1u << ((pin % ) * )); //赋值为0
*(volatile uint32_t*)(position) = tmp; } printf("\nSET DIR AS OUT\n");
for(int i = ; i < sizeof(pins)/sizeof(int); i++){
pin = pins[i];
if(pin < ){
position = gpio_mmap_reg + (pin / ) * ;
}else{
position = gpio_mmap_reg + (pin / ) * + 0x10;
}
printf("pin=%d, positon = %X\n", pin, position);
tmp = *(volatile uint32_t*)(position);
tmp |= (1u << (pin % ));
*(volatile uint32_t*)(position) = tmp;
usleep();
} //SET VAULE
printf("\nSET VALUE AS HIGH LEVEL\n");
for(int i = ; i < sizeof(pins)/sizeof(int); i++){
pin = pins[i];
position = gpio_mmap_reg + GPIO_DOUT_BASE_OFFSET + (pin / ) * ;
printf("pin=%d, positon = %X\n", pin, position);
tmp = *(volatile uint32_t*)(position);
tmp |= (1u << (pin % ));
*(volatile uint32_t*)(position) = tmp;
usleep();
} printf("\nSET VALUE AS LOW LEVEL\n");
for(int i = ; i < sizeof(pins)/sizeof(int); i++){
pin = pins[i];
position = gpio_mmap_reg + GPIO_DOUT_BASE_OFFSET + (pin / ) * ;
printf("pin=%d, positon = %X\n", pin, position);
tmp = *(volatile uint32_t*)(position);
printf("tmp = %X\n", tmp);
tmp &= ~(1u << (pin % ));
printf("tmp = %X\n", tmp);
*(volatile uint32_t*)(position) = tmp;
usleep();
}
close(gpio_mmap_fd);
}
上面这个例子就实现了用户态操作GPIO的方法,具体地址,和运算方法跟你的芯片GPIO寄存器定义有关,看到这里如果有很多东西还是不太明白也没有关系,因为这里涉及的知识面稍微多一些,不过不要灰心,自己多实战,再回过头来看其实就变简单了,关于嵌入式的学习也欢迎大家一起留言交流.
更多的实现请大家看github上的commit记录:https://github.com/BPI-SINOVOIP/RPi.GPIO/commits/master
【玩转开源】BananaPi R2——移植RPi.GPIO 到 R2的更多相关文章
- 【玩转开源】BananaPi R2 —— 第一篇 Openwrt安装
最近手上拿到一块香蕉派的R2,这块板子可以用作路由器,所以决定在板子上面跑一下Openwrt. R2的外观长这个样子,看起来还是比较酷的: 硬件介绍 CPU 是MTK的4核芯片mt7623n,搭配mt ...
- 【玩转开源】BananaPi R2 —— 第二篇 Openwrt 网口配置分析
上次和大家分享了如何烧录和安装Openwrt到BananaPi R2,运行Openwrt的R2目前就具备路由器的功能了,这次我们来看看R2运行Openwrt的性能如何,同时也会讲解一些常用的网络知识. ...
- 关于RPi.GPIO、BCM2835 c library、WiringPi、Gertboard
1.RPi.GPIO//RPi.GPIO-0.5.5.tar.gz 开发者:python官网:https://www.python.org/ 官网:https://pypi.python.org/py ...
- nanopi NEO2 学习笔记 3:python 安装 RPi.GPIO
如果我要用python控制NEO2的各种引脚,i2c 或 spi ,RPi.GPIO模块是个非常好的选择 这个第三方模块是来自树莓派的,好像友善之臂的工程师稍作修改移植到了NEO2上,就放在 /roo ...
- 树莓派 - RPi.GPIO
RPi.GPIO是通过Python/C API实现的,C代码操作底层寄存器, python通过Python/C API调用这些C接口. 这是关于RPi.GPIO项目的介绍. 其中提到了有python ...
- RPi.GPIO 和 HM
后续笔记不再记录导入的模块和硬件的连接方法,请根据关键词自行搜索. RPi.GPIO模块 GPIO:General Purpose Input Output 即 通用输入/输出 RPi.GPIO是一个 ...
- 树莓派RPi.GPIO+Flask构建WebApi实现远程控制
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import RPi.GPIO as GPIO from flask import Flask, requ ...
- (转)为什么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 ...
- Oracle 11g R2 32位 & Oracle 11g R2 64位 -百度云下载
Oracle 11g R2 32位 & Oracle 11g R2 64位 -百度云下载 https://pan.baidu.com/s/1fuzy67Olfxzsy3WJMCrCnQ 提取码 ...
随机推荐
- Spark-1(概念)
1. 什么是Spark? Apache Spark™是用于大规模数据处理的统一分析引擎. spark是一个实现快速通用的集群计算平台.它是由加州大学伯克利分校AMP实验室开发的通用内存并行计算框架,用 ...
- 百度地图--JS版
百度地图JS版本 ----选择关键字地图展示对应地址---- CSS body, html { width: %; height: %; margin: ; font-family: "微软 ...
- Outlook 2013 日历/任务本地备份与还原
1.日历: 备份日历:切换到日历项,按如下步骤备份.文件 --> 保存日历 --> 其它选项: 日期范围:指定日期(开始日期:2018/1/1,结束日期:2018/12/31) 详细信息: ...
- pwn-GUESS
参考了其他wp之后才慢慢做出来的 记录一下 首先checksec一下 有canary 放到IDA看下源码 运行流程大概是 有三个fork 即三次输入机会,于是无法爆破cannary 本题用的是SSP ...
- [ZJOI2019]线段树(线段树)
看到这题,首先想到将求和转期望,即每次操作进行概率为1/2,求节点打标记概率. 首先对于每次区间修改操作,对节点进行分类: 1.这个点和其父亲都和修改区间无交,这种情况可以无视. 2.这个点和修改区间 ...
- netcore项目在Windows部署:使用NSSM部署Windows服务
NSSM部署Windows服务 1 准备工作 在Windows平台部署Asp.net core应用程序一般采用IIS,但是如果我们的net core应用执行的是定时任务,需要开机自启,稳定运行的话,使 ...
- CentOS使用@Value注解为属性赋值的时候出现乱码
在本地开发用windows的没有出现乱码,在CentOS上运行的时候出现乱码. 1.修改中文的编码方式 (成功) env.properties为ANSI格式 先设置idea编码格式,utf-8, 将 ...
- Angular记录(7)
文档资料 箭头函数--MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_fun ...
- 《11招玩转网络安全》之第五招:DVWA命令注入
首先还是将DVWA的安全级别设置为Low,然后单击DVWA页面左侧的Command Injection按钮. 图5-1 Low级别的命令注入 这个就是最典型的命令注入接口.在文本框中输入一个IP ...
- SQL数字型注入代码审计
数字型注入 SQL注入攻击,简称注入攻击,是发生于应用程序与数据库层的安全漏洞. 简而言之,是在输入的字符串之中注入sql指定,在设计不良的程序当中忽略了检查,那么这些注入进去的指令就会被数据库服务器 ...