GPIO layout
使用RPi的GPIO,一般情况P1就够了,常用的SPI、I2C、UART都在这。使用时参考下面两个图(都是从elinux.org搬运过来的,详细的描述可以在这里找到)。
RPi的GPIO只支持3.3V电压,如果接5V的IO,要注意电平转换。
P1在板子上的位置:
P1每个Pin的功能:
一个例子
通过UART登录RPi。
首先参考上面的两个图连接PC和RPi,无非是TxD接RxD,RxD接TxD,注意共地。
我用的是一个TTL-USB的转换模块,所以连接后,PC系统中会多一个tty设备/dev/ttyUSB0
。根据和PC的不同连接方式,设备节点可能会是不同的名字。
然后打开minicom连接RPi。建议将串口配置命名保存起来,便于下次直接使用,如果有多个串口配置,也能快速切换。
以下是操作过程:
如果不知道默认用户名、密码,可以根据使用的系统类型,在官方下载页面查找:
又一个例子
点灯。
用P1-11,P1-12,P1-13分别控制三个LED。电路很简单,一目了然。BTW,原理图,我是用Circuit Lab在线画的,小巧方便。
然后是代码,借鉴了elinux.org的示例。
Python版:
#!/usr/bin/env python
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)
GPIO.output(11, GPIO.HIGH)
GPIO.output(12, GPIO.HIGH)
GPIO.output(13, GPIO.HIGH)
pin = 0;
while True:
GPIO.output(11 + pin, GPIO.LOW)
time.sleep(1)
GPIO.output(11 + pin, GPIO.HIGH)
pin = (pin + 1) % 3
C版:
/* blink.c
* 2014/6/6
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000)
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
volatile unsigned *gpio;
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
void map_registers()
{
void *gpio_map;
int mem_fd;
if((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0){
printf("can't open /dev/mem\n");
exit(-1);
}
gpio_map = mmap(
NULL,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED,
mem_fd,
GPIO_BASE
);
close(mem_fd);
if(gpio_map == MAP_FAILED){
printf("mmap error %d\n", (int)gpio_map);
exit(-1);
}
gpio = (volatile unsigned *)gpio_map;
}
int main(int argc, const char *argv[])
{
int cnt = 0;
int pin_map[3] = {17, 18 , 27};
map_registers();
INP_GPIO(pin_map[0]);
OUT_GPIO(pin_map[0]);
INP_GPIO(pin_map[1]);
OUT_GPIO(pin_map[1]);
INP_GPIO(pin_map[2]);
OUT_GPIO(pin_map[2]);
GPIO_SET = (1 << pin_map[0]) | (1 << pin_map[1]) | (1 << pin_map[2]);
while(1)
{
GPIO_CLR = 1 << pin_map[cnt];
sleep(1);
GPIO_SET = 1 << pin_map[cnt];
cnt = (cnt + 1) % 3;
}
return 0;
}
Python版代码简洁很多,因为用了RPi.GPIO这个包,而C版没有依赖任何库。
Python版代码,直接scp过去就可执行。C代码需要编译。下载编译器,然后arm-linux-gnueabihf-gcc blink.c -o blink
。同样scp到RPi执行。
两个版本执行都需要root权限。
这里有一个小小的坑。
GPIO的pin有两种命名方式,以SoC为中心,和以RPi为中心。
Python版代码中GPIO.setmode(GPIO.BOARD)
就是设置使用RPi为中心的命名方式,这样接下来GPIO.output(11, GPIO.HIGH)
中的pin编号,与本文最开始的图示是一一对应的。这种方式比较直观。
当然也可以使用另一种,GPIO.setmode(GPIO.BCM)
即可。这样的话,pin编号与它们在RPi上的布局有一个映射关系。详细的可以参考这个表格。
C版代码中用的是SoC的命名,为了方便操作,才有了这个数组int pin_map[3] = {17, 18 , 27};
。可见GPIO17等同于P1-11,GPIO18等同于P1-12,GPIO27等同于P1-13。
另外C版代码中直接操作了GPIO的寄存器。寄存器映射参考Data Sheet。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。