2016-09-30 6 views
12

я в настоящее время есть 2 кнопки подключили к моей Raspberry Pi (это те, с кольцом светодиодов в них), и я пытаюсь выполнить этот кодфункции кнопок Python странно не делать то же самое

#!/usr/bin/env python 
import RPi.GPIO as GPIO 
import time 

GPIO.setmode(GPIO.BCM) 
GPIO.setwarnings(False) 
GPIO.setup(17, GPIO.OUT) #green LED 
GPIO.setup(18, GPIO.OUT) #red LED 
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP) #green button 
GPIO.setup(27, GPIO.IN, GPIO.PUD_UP) #red button 

def remove_events(): 
     GPIO.remove_event_detect(4) 
     GPIO.remove_event_detect(27) 

def add_events(): 
     GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800) 
     GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800) 

def red(pin): 
     remove_events() 
     GPIO.output(17, GPIO.LOW) 
     print "red pushed" 
     time.sleep(2) 
     GPIO.output(17, GPIO.HIGH) 
     add_events() 

def green(pin): 
     remove_events() 
     GPIO.output(18, GPIO.LOW) 
     print "green pushed" 
     time.sleep(2) 
     GPIO.output(18, GPIO.HIGH) 
     add_events() 

def main(): 
    while True: 
     print "waiting" 
     time.sleep(0.5) 

GPIO.output(17, GPIO.HIGH) 
GPIO.output(18, GPIO.HIGH) 
GPIO.add_event_detect(4, GPIO.FALLING, callback=green, bouncetime=800) 
GPIO.add_event_detect(27, GPIO.FALLING, callback=red, bouncetime=800) 

if __name__ == "__main__": 
    main() 

На на поверхности он выглядит как довольно простой скрипт. При обнаружении нажмите кнопку:

  1. удалить событиях,
  2. печатать сообщение
  3. подождать 2 секунды, прежде чем добавлять события и поворачиваясь светодиода на

которое обычно работает здорово, когда Я нажимаю зеленую кнопку. Я пробовал его несколько раз подряд, и он работает непременно. С красным, однако, он работает хорошо в первый раз, а во второй раз, но после того, как он завершил второй цикл (цикл), сценарий просто останавливается.

Учитывая, что оба события довольно схожи, я не могу объяснить, почему он терпит неудачу в конце второй красной кнопки.

РЕДАКТИРОВАТЬ: Я изменил контакты с красного и зеленого соответственно (либо на разные контакты, либо на их замену). В любом случае, всегда красный код кнопки (на самом деле теперь зеленая кнопка) вызывает ошибку. Поэтому кажется, что это не «проблема с физическими красными кнопками, ни проблема с штырем», это просто оставляет код виноватым ...

+0

Возможно, один из вызовов «GPIO.output» вызвал исключение, а затем 'add_events()' никогда не вызывался снова? – zvone

+0

Спасибо за вашу мысль по этому вопросу. Я добавил кроме предложений, но они не были вызваны. Похоже, это не так. – user5740843

+0

Это также не объясняет, почему он работает хорошо один раз, но всегда терпит неудачу в конце второго цикла ... – user5740843

ответ

9

Я смог воспроизвести вашу проблему на моей малине Pi 1, модель B на запуск вашего скрипта и подключение кабеля перемычки между землей и GPIO27 для имитации красных нажатий кнопок. (Это штыри 25 и 13 на моей конкретной модели Pi.)

Интерпретатор python разбивается с ошибкой сегментации в потоке, предназначенном для опроса событий GPIO после того, как red возвращается с обработки нажатия кнопки. Изучив реализацию модуля Python GPIO, мне ясно, что из-за обратного вызова обработчика события небезопасно вызывать remove_event_detect, и это приводит к сбою. В частности, удаление обработчика событий, в то время как этот обработчик событий в настоящее время работает, может привести к повреждению памяти, что приведет к сбоям (как вы видели) или другим странным поведением.

Я подозреваю, что вы удаляете и повторно добавляете обработчики событий, потому что вы обеспокоены получением обратного вызова в то время, когда вы передаете нажатие кнопки. Нет необходимости делать это. Модуль GPIO объединяет один поток опроса для мониторинга событий GPIO и будет ожидать возврата одного обратного вызова перед вызовом другого, независимо от количества просматриваемых событий GPIO.

Я предлагаю вам просто позвонить в add_event_detect по мере запуска вашего скрипта и никогда не удалять обратные вызовы. Просто удалите add_events и remove_events (и их призывы) из вашего скрипта исправит проблему.

Если вас интересуют подробные сведения о проблеме в модуле GPIO, вы можете посмотреть на C source code for that module. Взгляните на run_callbacks и remove_callbacks в файл RPi.GPIO-0.6.2/source/event_gpio.c. Обратите внимание, что обе эти функции используют глобальную цепочку узлов struct callback. run_callbacks проходит цепочку обратного вызова, захватывая один узел, вызывая обратный вызов, а затем следуя ссылке этого узла на следующий обратный вызов в цепочке. remove_callbacks будет выполнять одну и ту же цепочку обратного вызова и освободить память, связанную с обратными вызовами на конкретном выводе GPIO.Если remove_callbacks вызывается в середине run_callbacks, узел, удерживаемый в настоящее время run_callbacks, может быть освобожден (и его память может быть повторно использована и перезаписана) до того, как будет следовать указатель на следующий узел.

Причины вы видите эту проблема только для красной кнопки, скорее всего, связанно с порядком вызова к add_event_detect и remove_event_detect приводит к памяти, ранее используемый узел обратного вызова для красной кнопки, чтобы быть утилизирована для других целей и перезаписана ранее чем память, используемая из узла обратного вызова зеленой кнопки, аналогично восстановлена. Однако будьте уверены, что проблема существует для обеих кнопок - просто удача в том, что память, связанная с обратным вызовом зеленой кнопки, не изменяется до того, как будет следовать указатель на следующий узел обратного вызова.

В целом, существует относительно отсутствия синхронизации потоков вокруг использования обратного вызова цепи в модуле GPIO в целом, и я подозреваю, что подобные проблемы могут возникнуть, если remove_event_detect или add_event_detect называется в то время как обработчик событий работает, даже если события удаляются из другого потока! Я бы предположил, что автор модуля RPi.GPIO должен использовать некоторую синхронизацию, чтобы гарантировать, что цепочка обратного вызова не может быть изменена при выполнении обратных вызовов. (Может быть, в дополнении к проверке того, цепь модифицируются на самом избирательном потоке, pthread_mutex_lock и pthread_mutex_unlock могут быть использованы для предотвращения других потоков от изменения обратного вызова цепи в то время как он используется по избирательному потоку.)

К сожалению, , в настоящее время это не так, и по этой причине я предлагаю вам полностью отказаться от remove_event_detect, если вы можете избежать этого.

+0

Хорошая отладка. Вы могли бы упомянуть, как вы смогли поймать интерпретатора в segfault, но в остальном это очень полная информация. Отчасти потому, что GPIO обслуживает только одно событие за раз, не рекомендуется спать в обработчике событий. Решение состоит в том, чтобы установить время окончания события, а затем продолжить сканирование. Способ избежать нескольких событий заключается в использовании команд проверки и замены, которые на ARMv6 (Raspberry Pi 1), а затем STREX и LDREX: http://infocenter.arm.com/help/topic/com.arm. doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf Возможно, для Pi и Python существует оболочка. –

Смежные вопросы