寫在前頭
*.版權聲明:本篇文章爲原創,可隨意轉載,轉載請註明出處,謝謝!另我創建一個QQ羣82642304,歡迎加入!
獲取中斷
- GPIO中斷在嵌入式開發中經常用到,到了linux下,處理GPIO的中斷就沒有裸機那麼簡單了。 Linux內核中有一套GPIO框架,管理和控制芯片上的GPIO管教,包括配置輸入輸出,配置電平高低(輸出)和獲取電平高低(輸入),中斷管理。
- CPU廠家需要按照GPIO框架的接口,實現底層的具體控制。一般的話,廠家提供的SDK都已經開發好了,不需要客戶去開發。
- 應用層上控制GPIO網上有許多資料,但是獲取中斷,網上的大部分資料都是需要開發底層驅動,由底層驅動獲取到GPIO的中斷然後再通知應用層。
- 對於不想開發驅動的我來說,還有另外一種方法可以直接從應用層上獲取到GPIO的中斷,Linux內核文檔Documentation/gpio/gpio-legacy.txt:691中提到的:
691 "value" ... reads as either 0 (low) or 1 (high). If the GPIO
692 is configured as an output, this value may be written;
693 any nonzero value is treated as high.
694
695 If the pin can be configured as interrupt-generating interrupt
696 and if it has been configured to generate interrupts (see the
697 description of "edge"), you can poll(2) on that file and
698 poll(2) will return whenever the interrupt was triggered. If
699 you use poll(2), set the events POLLPRI and POLLERR. If you
700 use select(2), set the file descriptor in exceptfds. After
701 poll(2) returns, either lseek(2) to the beginning of the sysfs
702 file and read the new value or close the file and re-open it
703 to read the value.
704
705 "edge" ... reads as either "none", "rising", "falling", or
706 "both". Write these strings to select the signal edge(s)
707 that will make poll(2) on the "value" file return.
708
709 This file exists only if the pin can be configured as an
710 interrupt generating input pin.
就是說可以通過讀取/sys/class/gpio/gpioN/value的值來獲取中斷。
5. 但是不是簡單的read,而是通過epoll、poll、select等這些IO複用函數來控制,對於epoll或者poll,需要監聽的事件是EPOLLPRI或POLLPRI,而不是EPOLLIN或POLLIN,對於select,需要將文件描述符放在exceptfds中,而且文件描述符被觸發時需要通過調用read讀取數據,還要通過lseek將文件流指針置迴文件開頭。
例如
對於epoll來說,僞代碼如下
...
#include <sys/epoll.h>
...
int main(int argc,char * argv[]){
struct epoll_event evd;
struct epoll_event * events;
...
int epollfd = epoll_create(10);
...
events = calloc (10, sizeof(struct epoll_event));
evd.data.fd = fd; //fd 即爲open /sys/class/gpio/gpioN/value返回的句柄
evd.events = EPOLLPRI;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&evd);
while (1) {
n = epoll_wait(epollfd,events,10,-1);
for (i = 0;i < n;i++) {
if (events[i].events & EPOLLPRI) {
memset(buf,0x00,sizeof(buf));
read(events[i].data.fd,buf,sizeof(buf));
lseek(events[i].data.fd,0,SEEK_SET);
//do yourself
}
}
}
}
對於poll,僞代碼如下
...
#include <sys/poll.h>
...
int main(int argc,char* argv[]){
struct pollfd fdset;
unsigned char buf[128];
while (1) {
memset(&fdset,0x00,sizeof(struct pollfd));
fdset.fd = fd; //fd 即爲open /sys/class/gpio/gpioN/value返回的句柄
fdset.events = POLLPRI;
poll(&fdset,1,3000);
if (fdset.events & POLLPRI) {
read(fdset.fd,buf,sizeof(buf));
lseek(fdset.fd,0,SEEK_SET);
//do yourself
}
}
}
select的話我就不寫了,一樣的做法。
另,我實際測試,使用epoll或者poll時監聽事件(POLLIN | POLLET)也是可以的。