簡體   English   中英

使用Python Asyncio等待GPIO中斷

[英]Use Python Asyncio to wait for GPIO interrupt

我試圖將程序從Tornado轉換為Asyncio,第一步是使用實際的asyncio eventloop,如此處所述

該應用程序在嵌入式Linux機器上運行,我正在通過sysfs / gpio子系統使用GPIO,並且在其中一些GPIO上我正在等待中斷。 通過執行以下操作,我能夠將其直接集成到Tornado IOLoop中:

# Register with the queue
self.io_loop.add_handler(
    self.gpio._value_file, self._handle_interrupt, self.io_loop._EPOLLPRI | self.io_loop.ERROR
)

在代碼段中, _value_file是可讀取GPIO的文件的文件句柄。 只要該GPIO上的中斷可用,就會觸發事件EPOLLPRI。 在龍卷風上,這非常有效。 中斷發生后不久,它將調用_handle_interrupt函數。

我的問題是,我無法將其轉換為本地asyncio事件循環。 用於監視文件描述符的文檔中,我僅找到添加讀取器和寫入器的函數,而沒有觀察文件描述符上的通用事件掩碼的函數。 我無法深入研究代碼,因為那是進入C的。但是,查看Tornado層,將來自Tornado IOLoop的調用轉換為asyncio IOLoop似乎是這種情況:

def add_handler(self, fd, handler, events):
    fd, fileobj = self.split_fd(fd)
    if fd in self.handlers:
        raise ValueError("fd %s added twice" % fd)
    self.handlers[fd] = (fileobj, stack_context.wrap(handler))
    if events & IOLoop.READ:
        self.asyncio_loop.add_reader(
            fd, self._handle_events, fd, IOLoop.READ)
        self.readers.add(fd)
    if events & IOLoop.WRITE:
        self.asyncio_loop.add_writer(
            fd, self._handle_events, fd, IOLoop.WRITE)
        self.writers.add(fd)

僅READ和WRITE標志被轉換,所有其他標志被忽略。

有人可以確認除READ和WRITE事件外,當前無法使用asyncio監視文件描述符上的任何事件嗎? 還是我做錯了什么,實際上有辦法嗎?

我現在自己已經找到了解決方案。 我的主要信息來源是Python-tulip組中的該線程以及我不得不稍微采用的這段代碼

主要的見解是,可以用來監視POLLPRI事件的epoll本身就是文件描述符。 每當epoll監視的FD上發生事件時,epoll文件描述符都會生成POLLIN事件,可以使用asyncio和add_reader對其進行add_reader 因此,我們不是直接注冊,而是手動創建了epoll結構,並使用ioloop將其注冊,如下所示:

self.epoll = select.epoll()
self.io_loop.add_reader(self.epoll.fileno(), self._handle_interrupt)

然后將實際的中斷事件注冊到epoll結構中

self.epoll.register(self.gpio._value_file, select.POLLPRI)

此時, _handle_interrupt函數將接收中斷事件。 確保在事件處理程序中實際輪詢epoll結構,否則它將持續生成讀取事件

def _handle_interrupt(self):
    self.epoll.poll(0)
    ...

重要的是使用低級select功能而不是高級selectors因為它們執行與asyncio類似的事件標志過濾。 下面的代碼片段是來自selectors.EpollSelector

def register(self, fileobj, events, data=None):
    key = super().register(fileobj, events, data)
    epoll_events = 0
    if events & EVENT_READ:
        epoll_events |= select.EPOLLIN
    if events & EVENT_WRITE:
        epoll_events |= select.EPOLLOUT
    try:
        self._epoll.register(key.fd, epoll_events)
    except BaseException:
        super().unregister(fileobj)
        raise
    return key

可以看出,除了READ和WRITE之外的所有事件都被過濾。 因此,您不能使用高級界面來監視POLLPRI事件。 因此,請使用低級接口。

我希望這可以幫助人們解決這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM