Linux無需開發底層驅動,從應用層獲取GPIO中斷

寫在前頭

*.版權聲明:本篇文章爲原創,可隨意轉載,轉載請註明出處,謝謝!另我創建一個QQ羣82642304,歡迎加入!


獲取中斷

  1. GPIO中斷在嵌入式開發中經常用到,到了linux下,處理GPIO的中斷就沒有裸機那麼簡單了。 Linux內核中有一套GPIO框架,管理和控制芯片上的GPIO管教,包括配置輸入輸出,配置電平高低(輸出)和獲取電平高低(輸入),中斷管理。
  2. CPU廠家需要按照GPIO框架的接口,實現底層的具體控制。一般的話,廠家提供的SDK都已經開發好了,不需要客戶去開發。
  3. 應用層上控制GPIO網上有許多資料,但是獲取中斷,網上的大部分資料都是需要開發底層驅動,由底層驅動獲取到GPIO的中斷然後再通知應用層。
  4. 對於不想開發驅動的我來說,還有另外一種方法可以直接從應用層上獲取到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)也是可以的。

發佈了39 篇原創文章 · 獲贊 19 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章