Блокировка чтения на GPIO из Python: как заблокировать с помощью epoll() и выбрать.EPOLLET

Я экспериментирую с доступом к GPIO из Python на встроенной системе (ядро ARM), на которой работает Linux, созданный с помощью Buildroot (ядро 4.1.15).

Я хочу, чтобы мой код BLOCK ожидал смены контакта на GPIO2 (то есть я не хочу опрашивать контакт, вызывая "read" несколько раз). Я пытаюсь сделать это, используя "epoll" в режиме с триггером:

Посмотрите документы Python для epoll. Флаг select.EPOLLET используется для получения запуска края для epoll. Смотрите также Linux документы для epoll.

Для простоты я уже настроил свой вывод GPIO с консоли, используя sysfs:

# cat /sys/class/gpio/gpio2/direction 
in
# cat /sys/class/gpio/gpio2/edge
rising

Вот мой код Python:

#!/usr/bin/env python
# coding=utf8

from time import sleep
import select
import sys

if __name__ == '__main__':

    try:
        pinIn = open("/sys/class/gpio/gpio2/value", "r")
    except IOError:
        print("Error setting up GPIO pin")
        sys.exit()

    myPoll = select.epoll()
    myPoll.register(pinIn.fileno(), select.EPOLLPRI | select.EPOLLET)

    while(1):

        events = myPoll.poll(4)
        print("EPoll result: %s" % (str(events),))

        for fd, event_type in events:
            print("FD: %d; Events: %d" % (fd, event_type))
            if event_type & select.EPOLLIN:
                print("-EPOLLIN!")
            if event_type & select.EPOLLPRI:
                print("-EPOLLPRI!")
            if event_type & select.EPOLLERR:
                print("-EPOLLERR!")

        value = pinIn.read(1)
        pinIn.seek(0)
        print("--> %s" % (str(value),))
        sleep(1)

Для тестирования я подаю на входной вывод прямоугольную волну от генератора сигналов со скоростью около 2 секунд за цикл, чтобы я мог видеть, когда вывод меняется.

Когда я запускаю это на моей встроенной системе, я получаю это:

# python3 /usr/sbin/test-gpio-python.py 
EPoll result: [(3, 10)]
FD: 3; Events: 10
-EPOLLPRI!
-EPOLLERR!
--> 0

Код спит в течение 1 секунды, затем на следующей итерации poll() немедленно возвращается и не блокируется. Он должен блокироваться, поскольку мой вход работает только с одним нарастающим фронтом в 2 секунды.

Почему не блокирует poll()?

==== РЕДАКТИРОВАТЬ: ====

Первоначально код вызвал странную ошибку, когда я попытался использовать "select.EPOLLET":

OverflowError: can't convert negative value to unsigned int

Однако я обнаружил, что я случайно использовал myPoll = select.poll() вместо epoll(). Код теперь исправлен.

1 ответ

Я решил проверить информацию о прерываниях, посмотрев на / proc / interrupts.

Вот только через пару секунд после установки фронта на "повышение" для вывода GPIO:

# cat /proc/interrupts 
...
 33:        421  gpio-mxc   2 Edge      gpiolib
...

Хм, уже произошло 421 прерывание!

Две секунды спустя:

# cat /proc/interrupts 
...
 33:        852  gpio-mxc   2 Edge      gpiolib
...

Это могло бы объяснить это. Прерывания накапливаются со скоростью ~400 в секунду, определенно быстрее, чем я их обрабатываю в Python.

Дальнейшее исследование с использованием области применения показало, что генератор сигналов подавал только около 1,6 В, и похоже, что шум вызывал входную цепь на устройстве.

Когда я переключился на правильный выход генератора сигналов и получил чистый сигнал на выводе GPIO, я начал получать ожидаемое количество прерываний, и код python работал правильно (то есть poll() блокировался правильно между нарастающими фронтами на входе).

Другие вопросы по тегам