Linux 用戶態設置GPIO控制
linux內核提供了一套在用戶態配置GPIO的接口,在/sys/class/gpio/
目錄下
可以發現其中包含有兩個文件export
、unexport
和若干gpiochipN
類型文件夾
export
用於將指定編號的引腳導出,作為GPIO使用unexport
用於將導出的GPIO刪除掉gpiochipN
當前芯片中包含的GPIO控制器
GPIO使用方法
- 添加設備接口GPIO167
輸入:echo 167 > export
可以發現,目錄下出現了gpio167
,如果執行命令后沒有反應,表示當前的GPIO已經用作其他的功能,例如作為IIC的引腳等 - 刪除設備接口GPIO167
輸入:echo 167 > unexport
可以發現當前導出的接口被刪除 -
控制設備接口GPIO167
輸入:echo 167 > unexport
direction
設置輸出還是輸入模式
- 設置為輸入:echo “in” > direction
- 設置為輸出:echo “out” > direction
value
輸出時,控制高低電平;輸入時,獲取高低電平
- 高電平:echo 1 > value
- 低電平:echo 0 > value
edge
控制中斷觸發模式,引腳被配置為中斷后可以使用poll()
函數監聽引腳
- 非中斷引腳: echo “none” > edge
- 上升沿觸發:echo “rising” > edge
- 下降沿觸發:echo “falling” > edge
- 邊沿觸發:echo “both” > edge
gpiochipN目錄
用來管理和控制一組gpio端口的控制器
base
和N相同,表示控制器管理的最小的端口編號。lable
診斷使用的標志(並不總是唯一的)-
ngpio
控制器管理的gpio端口數量(端口范圍是:N ~ N+ngpio-1) -
用戶態使用gpio監聽中斷
-
比如我想監聽PA7上的電平變化(也就是邊沿觸發),那么應該先向“/sys/class/gpio/gpio7/direction”寫入“in”,然后向“/sys/class/gpio/gpio7/edge”寫入“both”,然后對”/sys/class/gpio/gpio7/value”執行select/poll操作。
代碼如下:
poll_test.c
#include <stdio.h> #include <fcntl.h> #include <poll.h> #include <unistd.h> int main() { int fd=open("/sys/class/gpio/gpio7/value",O_RDONLY); if(fd<0) { perror("open '/sys/class/gpio/gpio7/value' failed!\n"); return -1; } struct pollfd fds[1]; fds[0].fd=fd; fds[0].events=POLLPRI; while(1) { if(poll(fds,1,0)==-1) { perror("poll failed!\n"); return -1; } if(fds[0].revents&POLLPRI) { if(lseek(fd,0,SEEK_SET)==-1) { perror("lseek failed!\n"); return -1; } char buffer[16]; int len; if((len=read(fd,buffer,sizeof(buffer)))==-1) { perror("read failed!\n"); return -1; } buffer[len]=0; printf("%s",buffer); } } return 0; }
這個小程序的作用就是就是不斷poll(“/sys/class/gpio/gpio7/value”)。一旦poll()返回,就輸出PA7的值。
假設代碼放在~目錄下,然后輸入如下命令:
cd ~ gcc poll_test.c -o poll_test echo in > /sys/class/gpio/gpio7/direction echo both > /sys/class/gpio/gpio7/edge ./poll_test
用1K電阻把PA7上拉到VCC,然后用一根導線把PA7與GND連接又斷開,會發現不斷輸出1和0(當PA7連上GND的瞬間輸出0,與GND斷開的瞬間輸出1)。說明poll()確實能檢測到電平變化。
-
Linux應用]通過sysfs在用戶空間使用GPIO中斷
-
通過使用sysfs,Linux GPIO可以支持在用戶空間進行GPIO的控制或獲取狀態。這樣可以使用簡單的工具,比如“echo”來設置輸出GPIO的電平或使用“cat”來讀取輸入GPIO的當前值。1、配置內核中sysfs下的GPIO支持要想在用戶空間訪問GPIO,需要在sysfs中使能GPIO支持。Symbol: GPIO_SYSFS [=n]Type : booleanPrompt: /sys/class/gpio/... (sysfs interface)Defined at drivers/gpio/Kconfig:51Depends on: GPIOLIB [=y] && SYSFS [=y] && EXPERIMENTAL [=y]Location:-> Device Drivers-> GPIO Support (GPIOLIB [=y])2、在用戶空間是能GPIO即將GPIO導出到用戶空間之中。------------------------------------GPIO = 22cd = /sys/class/gpiolsecho $GPIO > exportls------------------------------------注意:開始ls時,gpio22並不存在,第二個ls時,gpio22才存在。設置為輸入並獲取當前值:------------------------------------cd /sys/class/gpio/gpio$GPIOecho "in" > directioncat value------------------------------------設置為輸出並設置值:------------------------------------cd /sys/class/gpio/gpio$GPIOecho "out" > directionecho 1 > value 或 echo 0 > value------------------------------------3、用作中斷先將GPIO配置為輸入,然后使用poll()來阻塞程序直到GPIO的輸入電平發生改變,關鍵是使用POLLPRI而不是POLLIN來偵聽事件;或者使用select()。4、查看GPIO配置配置內核來使能debugfsSymbol: DEBUG_FS [=y]Type : booleanPrompt: Debug FilesystemDefined at lib/Kconfig.debug:77Location:-> Kernel hacking啟動目標硬件並掛載debugfsmount -t debugfs none /sys/kernel/debug查看引腳配置cat /sys/kernel/debug/gpiopoll示例:memset((void *)xfds, 0, sizeof(xfds));xfds[0].fd = fd;xfds[0].events = POLLPRI;ret = poll(xfds, 1, -1);if(ret <= 0)ERREXIT("poll value");if(xfds[0].revents & POLLPRI){/* get value */ret = lseek(fd, 0, SEEK_SET);if(ret < 0)ERREXIT("lseek value");ret = read(fd, buf, 2);buf[1] = '\0';printf("read ret = %d, value = %s\n", ret, buf);if(ret != 2)ERREXIT("read value");}select示例:FD_ZERO(&exceptfds);FD_SET(fd, &exceptfds);ret = select(fd+1,NULL,NULL,&exceptfds,NULL);if(ret < 0)ERREXIT("select value");//else if(ret > 0)if(ret > 0){/* get value */ret = lseek(fd, 0, SEEK_SET);if(ret < 0)ERREXIT("lseek value");ret = read(fd, buf, 2);buf[1] = '\0';printf("read ret = %d, value = %x\n", ret, buf[0]);if(ret != 2)ERREXIT("read value");}
-