繁体   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