[IoT-Intro] Raspberry Pi GPIO === ###### tags: `IoT-Intro` [TOC] # 使用設備 1. Raspberry Pi 2. 杜邦線 3. 示波器 # GPIO 腳位 ![image alt](https://docs.microsoft.com/en-us/windows/iot-core/media/pinmappingsrpi/rp2_pinout.png) :::warning 這是 BCM2837 GPIO 腳位的定義, 使用 sysfs 或直接映射記憶體時, 應以**上圖**為準, 而使用 Wiring Pi 時, 腳位定義略有不同, 請參見下圖 ![image alt](http://3.bp.blogspot.com/-gmSvHB8OAHo/VRQvYSy5BLI/AAAAAAAABBQ/GHkqcVJEp4Q/s1600/Raspberry%2BPi%2BGPIO%2BPins.png =640x480) 或利用 ```gpio readall``` 指令取得 ::: # Demo [![Demo](https://img.youtube.com/vi/KbAsGLYd6UY/0.jpg)](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 ``` ![image alt](https://cdn.sparkfun.com/assets/4/2/3/1/e/52f3c108ce395fd3368b4568.png) ## 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)