[IoT-Intro] Raspberry Pi GPIO
===
###### tags: `IoT-Intro`
[TOC]
# 使用設備
1. Raspberry Pi
2. 杜邦線
3. 示波器
# GPIO 腳位

:::warning
這是 BCM2837 GPIO 腳位的定義, 使用 sysfs 或直接映射記憶體時, 應以**上圖**為準, 而使用 Wiring Pi 時, 腳位定義略有不同, 請參見下圖

或利用 ```gpio readall``` 指令取得
:::
# Demo
[](https://youtu.be/KbAsGLYd6UY "Audi R8")
# 控制 GPIO
## 使用 sysfs 和 shell
``` bash
echo 4 > /sys/class/gpio/export #先將 GPIO4 設定成可以用 sysfs 控制
echo out > /sys/class/gpio/gpio4/direction #設定 GPIO4 為輸出腳
echo 1 > /sys/class/gpio/gpio4/value #設定 GPIO4 輸出值為 1 (0: 低電位, 1: 高電位)
```
* 取消建立出來的 GPIO4 node
```echo 4 > /sys/class/gpio/unexport```
## 不使用任何函式庫, 使用 C 語言
C 語言因為提供了指標,可以任意的去修改記憶體的某個部份,我們可以透過修改記憶體區塊的方式,直接與硬體溝通,來設定我們的 GPIO。
<br>
以下程式碼,首先點亮 GPIO4 上的 LED 一秒 (需接上 LED) 後,再關閉。
:::warning
執行程式需 root 權限, 可使用 ```su``` 切換成 root, 或在指令前加 ```sudo```
:::
```C-like
/* Modified from http://elinux.org/RPi_Low-level_peripherals */
#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) /* GPIO controller */
#define PAGE_SIZE (4 * 1024)
#define BLOCK_SIZE (4 * 1024)
int mem_fd;
void *gpio_map;
/* I/O access */
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 */
/**
* Set up a memory regions to access GPIO
*
*/
void setup_io()
{
/* open /dev/mem */
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
printf("can't open /dev/mem \n");
exit(-1);
}
/* mmap GPIO */
gpio_map = mmap(
NULL, /* Any adddress in our space will do */
BLOCK_SIZE, /* Map length */
PROT_READ|PROT_WRITE, /* Enable reading & writting to mapped memory */
MAP_SHARED, /* Shared with other processes */
mem_fd, /* File to map */
GPIO_BASE /* Offset to GPIO peripheral */
);
close(mem_fd); /* No need to keep mem_fd open after mmap */
if (gpio_map == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio_map); /* errno also set! */
exit(-1);
}
/* Always use volatile pointer! */
gpio = (volatile unsigned *)gpio_map;
}
int main(int argc, char **argv)
{
/* Set up gpi pointer for direct register access */
setup_io();
int g = 4;
/* Must use INP_GPIO before we can use OUT_GPIO */
INP_GPIO(g);
OUT_GPIO(g);
/* Set GPIO4 to 1 */
GPIO_SET = 1 << 4;
sleep(1);
/* Clear GPIO 4 */
GPIO_CLR = 1 << 4;
sleep(1);
return 0;
}
```
# 產生方波
使用 C 語言, 透過 sysfs 控制 GPIO 4
```C-like
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define PIN 4
int sig_handler(int signo)
{
/* Disable GPIO pins */
if (signo == SIGKILL)
if (GPIOUnexport(PIN) == -1)
return -1;
}
static int GPIOExport(int pin)
{
#define BUFFER_MAX 3
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open export for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
static int GPIOUnexport(int pin)
{
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open unexport for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
static int GPIODirection(int pin, int dir)
{
static const char s_directions_str[] = "in\0out";
#define DIRECTION_MAX 35
char path[DIRECTION_MAX];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio direction for writing!\n");
return(-1);
}
if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) {
fprintf(stderr, "Failed to set direction!\n");
return(-1);
}
close(fd);
return(0);
}
static int GPIORead(int pin)
{
#define VALUE_MAX 30
char path[VALUE_MAX];
char value_str[3];
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_RDONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio value for reading!\n");
return(-1);
}
if (-1 == read(fd, value_str, 3)) {
fprintf(stderr, "Failed to read value!\n");
return(-1);
}
close(fd);
return(atoi(value_str));
}
static int GPIOWrite(int pin, int value)
{
static const char s_values_str[] = "01";
char path[VALUE_MAX];
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio value for writing!\n");
return(-1);
}
if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) {
fprintf(stderr, "Failed to write value!\n");
return(-1);
}
close(fd);
return(0);
}
int main(int argc, char *argv[])
{
/* Enable GPIO pins */
if (GPIOExport(PIN) == -1)
return -1;
/* Set GPIO directions */
if (GPIODirection(PIN, OUT) == -1)
return -1;
int i = 5;
while(i > 0) {
/* Write GPIO value */
GPIOWrite(PIN, 1);
sleep(0.001);
GPIOWrite(PIN, 0);
sleep(0.001);
i--;
}
/* Disable GPIO pins */
if (GPIOUnexport(PIN) == -1)
return -1;
return 0;
}
```
* 編譯和執行
```shell
gcc -o executable a.c
./executable
```

## Generate PWM
### Sample code
```C-like=
int main(int argc, char *argv[])
{
/* Enable GPIO pins */
if (GPIOExport(PIN) == -1)
return -1;
usleep(100000);
/* Set GPIO directions */
if (GPIODirection(PIN, OUT) == -1)
return -1;
usleep(100000);
/* Register SIGKILL(Ctrl-C) handler */
signal(SIGKILL, sig_handler);
/* Period = 0.1 sec */
int all = 10000;
int x = 0;
/* Hold the status (By pulling up and down repeatedly for 300 times)
* to make it easier for us to observe the effect */
int count = 300;
while( 1 ) {
/* Write GPIO value */
GPIOWrite(PIN, 1);
usleep( all - x );
GPIOWrite(PIN, 0);
usleep( x );
//printf("%d %d\n",x ,all-x);
if( !count-- ){
x += 500;
if( x == 10500 ) x = 0;
count = 300;
}
}
return 0;
}
```
## Reference
* [RPi GPIO Code Samples](https://elinux.org/RPi_GPIO_Code_Samples)