0 前言
本来是准备写一篇用python控制GPIO的例子,结果发现太简单了,还是深入一些吧,多深入底层
接下来,我会按照下面的顺序来介绍:
1)GPIO控制器地址映射
2)GPIO主要控制寄存器介绍
3)GPIO操作流程
4)用C语言控制GPIO
6)用python来控制GPIO
1 概述
树莓派2B采用的CPU为BCM2836,外设部分和BCM2835完全一致,所以就可以参考BCM2835的寄存器手册
但是,这个寄存器手册笔误有点多,建议配合着wiki的勘误手册来看
CPU总共有54个IO,每个IO都可以复用,配置输入或者输出模式,单从GPIO部分来看,比较简单,无非是输入/输出,触法模式等,和一般的MCU没有太大差异
2 地址映射
BCM2835将BUS的地址空间,通过MMU映射到线性寻址空间,如下所示
这里,我们从左到右看,IO Peripherals的基地址为0x7E000000,然后映射后的基地址为0x20000000
也就是说外设的地址空间为0x20000000~0x20FFFFFF
接下来,在手册中,找到GPIO章节,查看寄存器地址,如下所示
比如GPFSEL0寄存器的BUS地址为0x7E200000,也就是说相对偏移为0x00200000,所以我们可以知道映射后的GPIO基地址为
0x20200000(0x20000000 + 0x00200000),其它寄存器的地址以此类推
3 寄存器介绍
解决了GPIO寄存器地址问题,下面就需要知道如何控制GPIO,大致浏览GPIO章节,发现相当简单,GPIO的主要功能有
- 外设模式
- GPIO模式
- 输出/输出
- 上拉/下拉
- 事件监测(上升沿/下降沿/边缘变化/高电平/低电平)
下面简单介绍GPIO相关寄存器
- GPFSELn(n=0/1/2/3/4)
用于选择GPIO管脚的模式是IO输出还是输入或者是复用 - GPSETn
设置IO输出值,写1有效,表示置高对应管脚 - GPCLRn
设置IO输出值,写1有效,表示置低对应管脚 - GPLEVn
IO当前值,用于读入当前的电平值 - GPEDSn
也就是所谓的事件标志位,可以通过该位来检查是否有事件发生,写1清零 - GPRENn/GPRENn/GPHENn/GPLENn
用于设置IO的事件模式,比如上升沿/下降沿/跳变/高电平/低电平触发方式,触发事件后,会发送一个中断信号给CPU
从而实现中断机制 - GPPUD/GPPUDCLKn
上拉/下拉配置寄存器,这个比较特殊,CPU不会保存这个配置状态,需要用户用软件来配置并保存,配置掉电不丢失,同时,
系统只提供两个相关寄存器,所以配置时需要采用复用模式,并遵循特定的配置方式和延时,具体参见手册介绍
总共就这么多寄存器,介绍完毕,下面说一下操作流程
4 操作流程
对于普通的IO来说,无非就是输入或者输出,输入模式下,可以轮询或者中断;输出模式下,往对应管脚寄存器里写1/0即可
输出配置
- 通过GPFSELn设置输出模式
- 往GPSETn寄存器写1,表示在对应管脚输出高电平
- 往GPCLRn寄存器写0,表示在对应管脚输出低电平
- 可以通过GPLEVn来读取当前输出值
输入配置
- 通过GPFSELn设置输入模式
- 通过GPPUD/GPPUDCLKn设置上拉或者下拉模式
- (可选)通过GPRENn/GPRENn/GPHENn/GPLEN设置事件模式
- 可以通过GPLEVn来读取当前输出值
5 用C语言控制IO
请参考 https://github.com/WiringPi/WiringPi/blob/master/wiringPi/wiringPi.c
6 用python语言控制IO
请参考 http://raspberrywebserver.com/gpio/