最近入手OrangePi Zero一块,程序上需要使用板子上自带的LED灯,在网上一查,不得不说OPi的支持跟树莓派无法相比。自己摸索了一下,实现简单的GPIO控制方法,作者的Zero安装的是Armbian系统,使用python写了一个读写寄存器的简单模块,通过这个模块,即可实现对GPIO的控制。

  作者以前使用过STM32的MCU,这类MCU,如果要实现对GPIO的控制,只需要根据datasheet查找相应GPIO寄存器并进行配置,即可实现IO控制,例如,要将内存地址为0x12345678的寄存器全部置为0xFFFFFFFF,只需要一条C语句:

 (uint32 *)(0x12345678) = 0xFFFFFFFF;

  但是,这个方法在Linux中行不通,编译运行的时候,会提示"segmentation fault",这个段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的。所以只能使用其他方法。

  首先总结一下实现对OrangePi GPIO控制的两种方法,第一种是通过Linux内存映射的方式,将实际CPU硬件的内存地址映射到用户程序的内存空间,再进行操作;第二种是通过sysfs方式控制GPIO,在程序中,操作/sys/class/gpio目录实现io口的控制。

  这两种方式都有现成已完成的案例,例如在python环境使用的pyH3库、C环境的WiringPi库使用的就是第一种方式;从树莓派移植的OPi.GPIO库使用的是第二种方式。其中第二种方式个人感觉更加简单,因为只需要在用户的程序里面读写系统目录的文件,即可实现对GPIO的控制,非常方便,但作者发现这种方式有个严重的问题,就是它无法使用那些板子上没引出来的IO口,因为板载的两个LED(红灯和绿灯)分别是使用了PA17和PL10引脚,如果使用第二种方式控制这两个引脚,会提示“Device or resource busy”的错误,通过以下命令:

cat /sys/kernel/debug/gpio

可以看出系统已经占用的IO口如下:

第一个GPIO17即为PA17,系统默认已经把该引脚配置为输出模式,并置为低电平。这里需要说明一下系统里对GPIO口的编号方法,系统是按照PA~PL共12组、每组32个引脚的方式对IO口进行统一编号的,如上图,GPIO-0~GPIO-31为PA口的IO,GPIO-32~GPIO-63为PB口的IO,以此类推。所以上图最后一行GPIO-362实际就是PL10引脚,即电源的绿灯引脚。

  回归本文,因为第二种方法无法操作PA17,所以只能使用第一种方法。

  第一种方法已经有现成的实现,通过深入研究库源码,内部实际都是通过C的mmap函数来实现CPU的物理地址映射到用户程序的内存空间。作者习惯使用Pyhon在Linux环境下进行程序开发,pyH3库使用感觉比较繁琐。所以特意研究了一下能否在python环境下实现物理地址的映射。实际果然不出所料,C里有mmap函数,python里同样有内置的mmap模块。说明文档在这里:https://docs.python.org/2/library/mmap.html

  其中最重要的就是mmap类的构造函数:

class mmap.mmap(fileno, length[, flags[, prot[, access[, offset]]]])

fileno: 文件描述符,可以是file对象的fileno()方法,或者来自os.open(),在调用mmap()之前打开文件,不再需要文件时要关闭。

length:要映射文件部分的大小(以字节为单位),这个值为0,则映射整个文件,如果大小大于文件当前大小,则扩展这个文件。

flags:MAP_PRIVATE:这段内存映射只有本进程可用;mmap.MAP_SHARED:将内存映射和其他进程共享,所有映射了同一文件的进程,都能够看到其中一个所做的更改;
prot:mmap.PROT_READ, mmap.PROT_WRITE 和 mmap.PROT_WRITE | mmap.PROT_READ。最后一者的含义是同时可读可写。

access:在mmap中有可选参数access的值有

ACCESS_READ:读访问。

ACCESS_WRITE:写访问,默认。

ACCESS_COPY:拷贝访问,不会把更改写入到文件,使用flush把更改写到

  fileno参数需要指定为系统“/dev/mem”的文件描述符,可以通过open()函数和fileno()方法得到,flags、prot、access参数指定为读写访问即可。

  length和offset参数比较重要,首先是offset参数,这个参数指示从哪个内存地址开始映射,注意,这个数值必须是页大小的整数倍,在OrangePi Zero中,页大小为4096字节。根据datasheet,GPIO的内存地址是从0x01C200800开始,但这个值并不是页大小的整数倍,所以只能往前截取,最近一个页大小整数倍的地址是0x01C200000,offset就是要设置为这个值。

  那还有0x0800的偏移量怎么办呢,这个就通过length参数来设定了,length参数指定了从这个offset开始,映射多少字节的物理内存到用户程序的内存空间,显然,这个length必须足够长把整个gpio模块的寄存器地址全部映射了,才能在用户程序里正常访问GPIO寄存器,这里设置为两个页大小,即8192字节(0x01C20000 ~ 0x01C21FFF),从datasheet看,这个地址空间包含了CCU、PIO、TIMER、OWA、PWM、KEYADC模块的所有寄存器。

  映射之后,可以得到一个mmap类的对象,使用这个对象,我们可以像操作文件一样对寄存器进行读写操作。在以下模块的代码中,实现了两个方法:读寄存器和写寄存器。注意在操作寄存器的过程中,有一点必须注意,每次读写寄存器必须四字节对齐,即一次读取或写入4个字节(所有寄存器都是32位),读写的寄存器地址也必须是4的倍数,否则会操作失败,板子会死机。

import mmap
import struct class GPIO: #-------------------------------------------------------------------------------------#
#定义GPIO相对0x01C20000的偏移地址
PIO_ADDR_OFFSET = 0x0800 #定义GPIOA的寄存器相对0x01C20000的偏移地址
#作者只写了GPIOA的寄存器定义,如果需要使用其他IO,请参考datasheet在下面增加定义
PIO_PA_CFG0_REG = PIO_ADDR_OFFSET + 0x00
PIO_PA_CFG1_REG = PIO_ADDR_OFFSET + 0x04
PIO_PA_CFG2_REG = PIO_ADDR_OFFSET + 0x08
PIO_PA_CFG3_REG = PIO_ADDR_OFFSET + 0x0C
PIO_PA_DATA_REG = PIO_ADDR_OFFSET + 0x10 PIO_PA_DRV0_REG = PIO_ADDR_OFFSET + 0x14
PIO_PA_DRV1_REG = PIO_ADDR_OFFSET + 0x18
PIO_PA_PUL0_REG = PIO_ADDR_OFFSET + 0x1C
PIO_PA_PUL1_REG = PIO_ADDR_OFFSET + 0x20 #-------------------------------------------------------------------------------------#
#以下是构造函数和析构函数
def __init__(self):
self.m_mmap = None
self.fd = None def __del__(self):
if(self.m_mmap != None):
self.m_mmap.close()
if(self.fd != None):
self.fd.close() #-------------------------------------------------------------------------------------#
#以下是成员函数
def Init(self):
"""
GPIO初始化函数
函数会打开/dev/mem文件,并映射从0x01C20000地址开始,共8192字节长度(2页)的内存空间到用户的虚拟地址
返回值:无
"""
START_ADDR = 0x01C20000
self.fd = open("/dev/mem", "rb+")
self.m_mmap = mmap.mmap(self.fd.fileno(), 4096 * 2, mmap.MAP_SHARED, mmap.PROT_WRITE | mmap.PROT_READ, mmap.ACCESS_WRITE, START_ADDR)
assert self.m_mmap != None,"Init Fails" def ReadReg(self,reg_addr):
"""
读取一个寄存器的值
reg_addr:要读取的寄存器地址(必须为4的倍数),且范围在2个pagesize内,即小于8192
返回值:寄存器的值(4字节)
"""
assert self.m_mmap != None,"Init Fails"
assert reg_addr%4 == 0,"reg_addr must be mutiple of 4"
assert 0<=reg_addr<=8192,"reg_addr must be less than 8192,which is 2 pagesize" self.m_mmap.seek(reg_addr)
ReadBytes = self.m_mmap.read(4)
return struct.unpack('L',ReadBytes)[0] def WriteReg(self,reg_addr,value):
"""
写一个寄存器的值
reg_addr:要写入的寄存器地址(必须为4的倍数),且范围在2个pagesize内,即小于8192
value:要写入的值,整形,一次写入四个字节长度的整数,即0xffffffff
返回值:无
"""
assert self.m_mmap != None,"Init Fails"
assert reg_addr%4 == 0,"reg_addr must be mutiple of 4"
assert 0<=reg_addr<=8192,"reg_addr must be less than 8192,which is 2 pagesize"
assert 0<=value<=0xFFFFFFFF,"value must be less than 0xFFFFFFFF,which is 4 bytes" self.m_mmap.seek(reg_addr)
BytesToWrite = struct.pack('L',value)
self.m_mmap.write(BytesToWrite)
return

  要使用这个模块,只需要把这个模块的py文件放在用户程序同一个目录下,直接导入即可,以下是令PA17(红色LED)闪烁的范例。

  说明:

  1、OPiZero_GPIO是上面定义的模块的文件名,直接导入使用即可。

  2、PIO_PA_CFG2_REG寄存器的第4~第7位为Pin17的模式配置,配置为001即输出模式

  3、PIO_PA_DATA_REG寄存器的第17位为Pin17的高底电平输出控制,这里采用了一个巧妙的方法,读取PIO_PA_DATA_REG的值与0x00020000按位异或即可实现第17位的取反。

  4、切勿直接往寄存器里写入数据,因为PA口有很多IO用作板子内部使用,直接写入的话很容易导致其他IO口逻辑输出错误,导致板子死机,作者已亲身体验n次,务必使用读-修改-写的模式修改寄存器的值。

import OPiZero_GPIO
import time #以下为主程序
GPIO = OPiZero_GPIO.GPIO()
GPIO.Init(); #PA17配置为输出模式
GPIO.WriteReg(GPIO.PIO_PA_CFG2_REG,GPIO.ReadReg(GPIO.PIO_PA_CFG2_REG) | 0x00000010)
while(1):
GPIO.WriteReg(GPIO.PIO_PA_DATA_REG,GPIO.ReadReg(GPIO.PIO_PA_DATA_REG) ^ 0x00020000)
time.sleep(0.3)

实际效果如下:   

  最后把源码附上:

  https://files.cnblogs.com/files/qzrzq1/OPiZero_GPIO.zip

  https://pan.baidu.com/s/1yiely1q_4LPZ4Bs8gyKDGg

python环境下实现OrangePi Zero寄存器访问及GPIO控制的更多相关文章

  1. Python环境下NIPIR(ICTCLAS2014)中文分词系统使用攻略

    一.安装 官方链接:http://pynlpir.readthedocs.org/en/latest/installation.html 官方网页中介绍了几种安装方法,大家根据个人需要,自行参考!我采 ...

  2. Azure REST API (4) 在Python环境下,使用Azure REST API

    <Windows Azure Platform 系列文章目录> 之前遇到的项目中,客户需要在Python环境下,监控Azure VM的CPU利用率,在这里简单记录一下. 笔者的环境是Win ...

  3. 图像处理基本工具——Python 环境下的 Pillow( PIL )

    由于笔者近期的研究课题与图像后处理有关,需要通过图像处理工具对图像进行变换和处理,进而生成合适的训练图像数据.该系列文章即主要记录笔者在不同的环境下进行图像处理时常用的工具和库.在 Python 环境 ...

  4. 关于python环境下的opencv安装

    吐槽: 这一天我终于记起了这个博客.今天搞python环境下的opencv,又弄了一天,很烦躁.之前配置VS的opencv也是花了好久的时间,然后突然发现之前记录在电脑上的文档都找不到了,于是决定还是 ...

  5. windows下多Python环境指定pip安装模块到对应Python环境下

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿在windows下装了2套Python,一套是直接安装的Pytho ...

  6. python环境下安装virtualenv,virtualenvwrapper

    在使用 Python 开发的过程中,工程一多,难免会碰到不同的工程依赖不同版本的库的问题: 亦或者是在开发过程中不想让物理环境里充斥各种各样的库,引发未来的依赖灾难. 此时,我们需要对于不同的工程使用 ...

  7. Python环境下如何安装爬虫需求的一些库

    是在CMD环境下的: request库pip install request 回车 pandas库 同理,pip install pandas :这里需要声明下,这个是不一定成功的,刚入门稍微看了一点 ...

  8. Windows10系统python环境下安装Dlib库(转载,蔡军帅亲测可用)

    Dlib是一个很优秀的机器学习库,最近做人脸识别要用到这个库,简要记录一下配置过程,准备工作: 1.python环境 2.安装好pip(这里有个简单的安装教程) 3.Dlib包,贴一个我安装的版本,链 ...

  9. Python环境下的Sublime Text3无法使用input()函数

    在Sublime Text3中写好Python程序,按Ctrl+B运行程序,在控制台中输入内容,回车,程序没有响应.最后求助网络,找到了解决办法. 一.安装插件SublimeREPL 按Ctrl+Sh ...

随机推荐

  1. python 之路,200行Python代码写了个打飞机游戏!

    早就知道pygame模块,就是没怎么深入研究过,恰逢这周未没约到妹子,只能自己在家玩自己啦,一时兴起,花了几个小时写了个打飞机程序. 很有意思,跟大家分享下. 先看一下项目结构 "" ...

  2. 实例解析Collections源码,Iterator和ListIterator

    比如一个视频或文章有多个页面标签设置,我们在看一篇文章或一个视频时,底部有为你推荐栏目. 如何根据这个文章或视频的标签,来实现这个推荐栏目呢. public List<VideoInfoVo&g ...

  3. Using Sass with the Angular CLI

    https://www.tuicool.com/articles/mauiMzY One of the first things you'll usually do in a project is t ...

  4. Codeforces Round #479 (Div. 3) C. Less or Equal

    题目地址:http://codeforces.com/contest/977/problem/C 题解:给一串数组,是否找到一个数x,找到k个数字<=x,找到输出x,不能输出-1.例如第二组,要 ...

  5. nginx配置 location及rewrite规则详解

    1. location正则写法 语法规则: location [=|~|~*|^~] /uri/ { … } =    开头表示精确匹配 ^~  开头表示uri以某个常规字符串开头,理解为匹配 url ...

  6. EntityFramework 6.x和EntityFramework Core插入数据探讨

    前言 一直以来对EF和EF Core都是单独分开来写,从未以比较的形式来讲解,如果您既用过EF 6.x也用过用EF Core是否有了解过EF和EF Core在插入数据时和返回主键有何异同呢?本篇博客是 ...

  7. SpringBoot Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback.

    使用SpringBoot写HelloWorld,当配置好启动类后,再创建新的controller或其它类,启动项目后访问对应的映射名,页面显示: Whitelabel Error Page This ...

  8. Java开源生鲜电商平台-商品表的设计(源码可下载)

    Java开源生鲜电商平台-商品表的设计(源码可下载) 任何一个电商,无论是B2C还是B2B的电商,商品表的设计关系到整个系统架构的核心. 1. 商品基本信息表:用单词:goods做为商品表 2. 商品 ...

  9. SQL转化为MapReduce的过程

    转载:http://www.cnblogs.com/yaojingang/p/5446310.html 在了解了MapReduce实现SQL基本操作之后,我们来看看Hive是如何将SQL转化为MapR ...

  10. mysql workbench EER图,里面的实线以及虚线的关系

    ERWin里面线代表实体间的三种关系:决定关系(Identifying Relationship),非决定关系(None-Identifying Relationship),多对多(Many-To-M ...