標籤:樹莓派 sysfs gpio
0 前言 本文描述如果通過檔案IO sysfs方式控制樹莓派 GPIO連接埠。通過sysfs方式控制GPIO,先訪問/sys/class/gpio目錄,向export檔案寫入GPIO編號,使得該GPIO的操作介面從核心空間暴露到使用者空間,GPIO的操作介面包括direction和value等,direction控制GPIO方向,而value可控制GPIO輸出或獲得GPIO輸入。 Linux學習可從應用出發,先不糾結Linux驅動編寫,先把Linux給玩起來。
【相同與不同】 本文和【EasyARM i.mx28學習筆記——檔案IO方式操作GPIO】內容相似,大部分代碼相同。通過檔案IO操作可以有效地避免平台差異,雖然EasyARM im287平台和樹莓派完全不同,但是通過sysfs操作GPIO實現代碼大致相同。 和EasyARM im287不同,此處並沒有使用交叉編譯工具,有樹莓派中的gcc工具鏈編譯連結獲得可執行檔,而EasyARM im287並不能這樣操作。EasyARM im287採用busybox指令集,這也與樹莓派中的debian指令集存在差異。
【相關博文】 【EasyARM i.mx28學習筆記——檔案IO方式操作GPIO】 【樹莓派學習筆記——Shell指令碼操作GPIO】 【樹莓派學習筆記——交叉編譯工具鏈】 【 Linux學習筆記——例說makefile 索引博文】
【代碼倉庫】 代碼倉庫位於bitbucket——rpi-gpio-sysfs ,請使用Hg複製或者直接下載zip包。請不要使用任何版本的IE瀏覽器訪問連結,除非你已經知道所使用的IE瀏覽器符合HTML5標準。推薦使用Google或者Firefox瀏覽器訪問,若使用國產雙核瀏覽器請切換到極速模式。 【原理圖示意】
圖1 連線
1 暴露GPIO操作介面
static int gpio_export(int pin){ char buffer[BUFFER_MAX]; int len; int fd; fd = open("/sys/class/gpio/export", O_WRONLY); if (fd < 0) { fprintf(stderr, "Failed to open export for writing!\n"); return(-1); } len = snprintf(buffer, BUFFER_MAX, "%d", pin); if (write(fd, buffer, len) < 0) { fprintf(stderr, "Fail to export gpio!"); return -1; } close(fd); return 0;}
2 隱藏GPIO操作介面
static int gpio_unexport(int pin){ char buffer[BUFFER_MAX]; int len; int fd; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd < 0) { fprintf(stderr, "Failed to open unexport for writing!\n"); return -1; } len = snprintf(buffer, BUFFER_MAX, "%d", pin); if (write(fd, buffer, len) < 0) { fprintf(stderr, "Fail to unexport gpio!"); return -1; } close(fd); return 0;}
3 配置GPIO方向
static int gpio_direction(int pin, int dir){ static const char dir_str[] = "in\0out"; char path[DIRECTION_MAX]; int fd; snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin); fd = open(path, O_WRONLY); if (fd < 0) { fprintf(stderr, "failed to open gpio direction for writing!\n"); return -1; } if (write(fd, &dir_str[dir == IN ? 0 : 3], dir == IN ? 2 : 3) < 0) { fprintf(stderr, "failed to set direction!\n"); return -1; } close(fd); return 0;} 【簡單說明】 【1】dir_str[dir == IN ? 0 : 3], dir == IN ? 2 : 3 如果輸入為常數宏IN, 取dir_str[0]=“in”;若輸入常數宏為OUT,取dir_str[0]=“out”。此處巧妙的使用了在數組中的“\0”。
4 控制GPIO輸出
static int gpio_write(int pin, int value){ static const char values_str[] = "01"; char path[DIRECTION_MAX]; int fd; snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", pin); fd = open(path, O_WRONLY); if (fd < 0) { fprintf(stderr, "failed to open gpio value for writing!\n"); return -1; } if (write(fd, &values_str[value == LOW ? 0 : 1], 1) < 0) { fprintf(stderr, "failed to write value!\n"); return -1; } close(fd); return 0;}
5 獲得GPIO輸入
static int gpio_read(int pin){ char path[DIRECTION_MAX]; char value_str[3]; int fd; snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", pin); fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "failed to open gpio value for reading!\n"); return -1; } if (read(fd, value_str, 3) < 0) { fprintf(stderr, "failed to read value!\n"); return -1; } close(fd); return (atoi(value_str));}
6 GPIO翻轉操作 【main函數】
int main(int argc, char *argv[]){ int i = 0; GPIOExport(POUT); GPIODirection(POUT, OUT); for (i = 0; i < 20; i++) { GPIOWrite(POUT, i % 2); usleep(500 * 1000); } GPIOUnexport(POUT); return(0);} 【makefile】——此處的代碼tab顯示可能存在問題,請以代碼倉庫為主。
# 可執行檔TARGET=test# 依賴目標SRCS=gpio-sysfs.c# 目標檔案OBJS = $(SRCS:.c=.o)# 指令編譯器和選項CC=gccCFLAGS=-Wall -std=gnu99$(TARGET):$(OBJS) $(CC) -o [email protected] $^clean: rm -rf $(TARGET) $(OBJS)# 連續動作,先清除再編譯連結,最後執行exec:clean $(TARGET) @echo 開始執行 sudo ./$(TARGET) @echo 執行結束# 編譯規則 [email protected]代表目標檔案 $< 代表第一個依賴檔案%.o:%.c $(CC) $(CFLAGS) -o [email protected] -c $<
【上傳樹莓派中 編譯連結並執行】 make exec makefile中exec目標包括以下一個過程,先清除目標檔案和可執行檔,然後進行交叉編譯,最後使用超級許可權運行可執行檔。 makefile的使用詳見【 Linux學習筆記——例說makefile 索引博文】
7 總結【1】樹莓派和其他嵌入式Linux開發板存在差別和聯絡,樹莓派同樣可以使用sysfs控制GPIO。【2】樹莓派即在其他助劑中交叉編譯,也可在平台直接編譯。
8 參考資料【1】RPi Low-level peripherals