Блокировка чтения на 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() блокировался правильно между нарастающими фронтами на входе).