1

GPIO layout

使用RPi的GPIO,一般情况P1就够了,常用的SPI、I2C、UART都在这。使用时参考下面两个图(都是从elinux.org搬运过来的,详细的描述可以在这里找到)。
RPi的GPIO只支持3.3V电压,如果接5V的IO,要注意电平转换。

P1在板子上的位置:
GPIO real

P1每个Pin的功能:
GPIO layout

一个例子

通过UART登录RPi。

首先参考上面的两个图连接PC和RPi,无非是TxD接RxD,RxD接TxD,注意共地。
我用的是一个TTL-USB的转换模块,所以连接后,PC系统中会多一个tty设备/dev/ttyUSB0。根据和PC的不同连接方式,设备节点可能会是不同的名字。
连接

然后打开minicom连接RPi。建议将串口配置命名保存起来,便于下次直接使用,如果有多个串口配置,也能快速切换。

以下是操作过程:
操作

如果不知道默认用户名、密码,可以根据使用的系统类型,在官方下载页面查找:
RaspBian

Arch Linux

又一个例子

点灯。

用P1-11,P1-12,P1-13分别控制三个LED。电路很简单,一目了然。BTW,原理图,我是用Circuit Lab在线画的,小巧方便。

原理图

LED接线

然后是代码,借鉴了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


suosuopuo
207 声望2 粉丝

下一篇 »
errno in Linux

引用和评论

0 条评论