简体   繁体   English

使用Python Asyncio等待GPIO中断

[英]Use Python Asyncio to wait for GPIO interrupt

I am trying to convert a program from Tornado to Asyncio and the first step would be to use the actual asyncio eventloop as described here . 我试图将程序从Tornado转换为Asyncio,第一步是使用实际的asyncio eventloop,如此处所述

This application runs on an embedded Linux machine, I am using GPIOs through the sysfs/gpio subsystem and on some of these GPIOs I am waiting for interrupts. 该应用程序在嵌入式Linux机器上运行,我正在通过sysfs / gpio子系统使用GPIO,并且在其中一些GPIO上我正在等待中断。 I was able to directly integrate this into the Tornado IOLoop by doing: 通过执行以下操作,我能够将其直接集成到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
)

In the code piece, _value_file is the file handle to the file that the GPIO can be read from. 在代码段中, _value_file是可读取GPIO的文件的文件句柄。 The event EPOLLPRI is fired, whenever an interrupt on that GPIO is available. 只要该GPIO上的中断可用,就会触发事件EPOLLPRI。 On Tornado, this works very well. 在龙卷风上,这非常有效。 It would call the _handle_interrupt function shortly after the interrupt comes. 中断发生后不久,它将调用_handle_interrupt函数。

My issue is, that I have not been able to translate this to the native asyncio event loop. 我的问题是,我无法将其转换为本地asyncio事件循环。 In the documentation for watching file descriptors I only find functions to add readers and writers but nothing to watch for a generic event mask on a file descriptor. 用于监视文件描述符的文档中,我仅找到添加读取器和写入器的函数,而没有观察文件描述符上的通用事件掩码的函数。 I can't dive into the code since that goes into C. However, looking at the Tornado layer to translate calls from the Tornado IOLoop into the asyncio IOLoop it seems like this is the case: 我无法深入研究代码,因为那是进入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)

Only READ and WRITE flags are translated, all other flags are ignored. 仅READ和WRITE标志被转换,所有其他标志被忽略。

Could someone confirm that it is currently not possible to use asyncio to watch for any events on file descriptors except READ and WRITE events? 有人可以确认除READ和WRITE事件外,当前无法使用asyncio监视文件描述符上的任何事件吗? Or am I doing something wrong and there actually is a way? 还是我做错了什么,实际上有办法吗?

I have found the solution for this myself now. 我现在自己已经找到了解决方案。 My main source of information was this thread in the Python-tulip group and this piece of code which I had to adopt slightly. 我的主要信息来源是Python-tulip组中的该线程以及我不得不稍微采用的这段代码

The main insight is that an epoll that can be used to watch for POLLPRI events is itself a file descriptor. 主要的见解是,可以用来监视POLLPRI事件的epoll本身就是文件描述符。 Whenever an event occurs on an FD that the epoll watches, the epoll file descriptor will generate a POLLIN event which can be watched using asyncio with add_reader . 每当epoll监视的FD上发生事件时,epoll文件描述符都会生成POLLIN事件,可以使用asyncio和add_reader对其进行add_reader So instead of registering directly, we manually create an epoll structure and register it with the ioloop like so: 因此,我们不是直接注册,而是手动创建了epoll结构,并使用ioloop将其注册,如下所示:

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

The actual interrupt event is then registered to the epoll structure 然后将实际的中断事件注册到epoll结构中

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

At this point, interrupt events will be received in the _handle_interrupt function. 此时, _handle_interrupt函数将接收中断事件。 Make sure to actually poll the epoll structure in the event handler, or it will continuously generate read events 确保在事件处理程序中实际轮询epoll结构,否则它将持续生成读取事件

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

It is important to use the low-level select functionality instead of the high-level selectors since they do a similar event flag filtering as in asyncio . 重要的是使用低级select功能而不是高级selectors因为它们执行与asyncio类似的事件标志过滤。 The following code snipped is from selectors.EpollSelector : 下面的代码片段是来自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

It can be seen, that all events except READ and WRITE are filtered. 可以看出,除了READ和WRITE之外的所有事件都被过滤。 Hence, you can't use the high level interface to watch for POLLPRI events. 因此,您不能使用高级界面来监视POLLPRI事件。 Therefore, use the low-level interface. 因此,请使用低级接口。

I hope this helps people stumbling over this issue. 我希望这可以帮助人们解决这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM