01 |
Содержание:
|
|
02 |
Данная статья является полным руководством по работе с одной из самых простых и популярных библиотек по взаимодействию с GPIO-выходами Raspberry Pi 3 — RPi.GPIO. В данной статье подробно разбираются способы управления пинами, генерация ШИМ-сигналов, обработка прерываний, отладка в среде разработке Python, а также подводятся итоги и делаются выводы о преимуществах и недостатках библиотеки RPi.GPIO. |
|
03 |
У данной статьи есть видеоверсия: Подписывайтесь на канал , чтобы быть в курсе обновлений! |
|
04 | На заметку: |
Документацию по использованию библиотеки RPi.GPIO для Python можно найти на официальном ресурсе https://sourceforge.net/projects/raspberry-gpio-python/.
Ссылка на раздел официального ресурса языка Python — https://pypi.python.org/pypi/RPi.GPIO. |
|
05 | На заметку: |
По умолчанию, в актуальной версии Raspbian OS библиотека RPi.GPIO для Python предустановлена.
|
|
06 |
В предыдущей статье было обосновано использование следующего шаблона на написания любых программ по работе c GPIO на Питоне: |
|
07 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
try:
# === Инициализация пинов ===
#pin=5
#GPIO.setmode(GPIO.BCM)
#GPIO.setup(pin, GPIO.OUT, initial=1)
# Здесь размещаем основной рабочий код
# ...
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
finally:
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
08 |
Поскольку это вторая часть о GPIO, которая предполагает самостоятельные эксперименты с полученными знаниями, необходимо немного модифицировать предложенный ранее основной шаблон: |
|
09 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
try:
# === Инициализация пинов ===
#pin=5
#GPIO.setmode(GPIO.BCM)
#GPIO.setup(pin, GPIO.OUT, initial=1)
# Здесь размещаем основной рабочий код
# ...
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except Exception as e:
# ...
print("Other Exception") # Прочие исключения
print("- Exception message: "+str(e)) # Подробности исключения
finally:
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
10 |
Это необходимо сделать для простоты отладки. Дело в том, что среда разработки может указать лишь на синтаксическую ошибку, а в остальном программа будет вылетать с общим исключением. Именно внесенные изменения, помогут понять, что послужило причиной. Например, если умышленно добавить строку, которая вызовет исключение print(2/0): |
|
11 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
try:
print(2/0)
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except Exception as e:
# ...
print("Other Exception") # Прочие исключения
print("- Exception message: "+str(e)) # Подробности исключения
finally:
# Комментируем cleanup() так как не трогаем пины
#print("CleanUp") # Информируем о сбросе пинов
#GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
12 |
То при исполнении появится сообщение с описанием исключения: |
|
14 |
Но как видно, сообщение далеко не полное и не указывает на место в скетче, которое вызвало исключение. Для получения полной информации необходимо воспользоваться объектом traceback: |
|
15 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
print(2/0)
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
# Комментируем cleanup() так как не трогаем пины
#print("CleanUp") # Информируем о сбросе пинов
#GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
17 |
Теперь получена вся необходимая информация — тип исключения, сообщение и место в скетче, спровоцировавшее исключение. |
|
18 |
И в завершение добавим бесконечный цикл while(), для того, чтобы программа не завершилась сразу после исполнения: |
|
19 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# === Инициализация пинов ===
#GPIO.setmode(GPIO.BCM)
# ...
# Здесь размещаем основной рабочий код
# ...
while 1:
# Этот цикл нужен, чтобы программа не завершилась сразу после запуска
i=0 # Бессмысленная переменная, чтобы у цикла while было тело.
# Если есть код для цикла while, предыдущую строку можно удалить
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем о сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
20 |
Испытательный стенд В данной статье, все примеры будут приводиться на следующем стенде (принципиальная и наглядная схемы): |
|
|
22 | Внимание: |
При использовании в макетировании T-образного 40-пинового борда, после сборки схемы, не подключая питание, рекомендуется путем прозвонки мультиметром убедиться в корректности соединения — в соответствии пинов Raspberry Pi 3 и подключенного Т-образного борда.
|
|
24 | Python |
1 2 3 import RPi.GPIO as GPIO
GPIO.RPI_INFO # Информация об устройстве
GPIO.VERSION # Информация о версии библиотеки |
|
26 |
Получить конкретное свойство можно, обратившись к нему напрямую: |
|
27 | Python |
1 2 GPIO.RPI_INFO['PROCESSOR'] # 'BCM2837'
GPIO.RPI_INFO['P1_REVISION'] # 3. Варианты: 0 = Compute Module, 1 = Rev 1, 2 = Rev 2, 3 = Model B+/A+ |
|
28 |
Программно узнать версию языка Python можно так: |
|
29 | Python |
1 2 3 4 import sys
sys.version
# или
import sys; sys.version |
|
30 |
Хотя вся эта информация отображается при запуске оболочки Python Shell: |
|
32 |
В случае невозврата пинов в исходное состояние функцией cleanup(), при запуске нового скетча библиотека RPi.GPIO сгенерирует предупреждение об этом. Такого рода уведомления можно отключить функцией setwarnings(): |
|
33 | Python |
1 2 GPIO.setwarnings(False) # Отключить предупреждения - не рекомендуется
GPIO.setwarnings(True) # Включить предупреждения |
|
34 |
Инициализация пинов Базовым навыком в работе с любым МК является манипуляция состояниями пинов и чтение этих состояний. |
|
35 |
Перед всеми манипуляциями, необходимо настроить режим нумерации пинов — BCM или BOARD. Делается это при помощи функции GPIO.setmode(): |
|
36 | Python |
1 2 3 GPIO.setmode(GPIO.BOARD)
# или
GPIO.setmode(GPIO.BCM) # Предпочтительно |
|
37 |
Узнать, какой режим установлен, можно при помощи функции GPIO.getmode(): |
|
38 | Python |
1 mode = GPIO.getmode() # Вернет GPIO.BOARD, GPIO.BCM или None |
|
39 | На заметку: |
Таким образом, в используемой схеме, при нумерации GPIO.BCM светодиоды подключены к пинам 14, 15, 18, а кнопки к пинам 25, 8, 7.
|
|
40 |
После установки режима нумерации, можно переходить к установке режима работы пина. Как обычно, возможны 2 варианта — INPUT (режим чтения состояния) и OUTPUT (режим установки состояния): |
|
41 | Python |
1 2 3 4 pin=14
GPIO.setup(pin, GPIO.IN) # Режим INPUT - чтение состояния
# или
GPIO.setup(pin, GPIO.OUT) # Режим OUTPUT - установка состояния |
|
42 |
Поскольку каждый из GPIO-пинов Raspberry Pi 3 снабжен встроенным стягивающим/подтягивающим резистором, при инициализации пина в режиме INPUT, можно в этой же команде осуществить подтяжку/стяжку этого пина, а также отключить подтягивание/стягивание: |
|
43 | Python |
1 2 3 4 5 6 pin=25
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # При установке режима пина, стягиваем его к нулю
# или
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # При установке режима пина, подтягиваем его к единице
# или
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_OFF) # При установке режима пина, отключаем подтягивание/стягивание |
|
44 |
В случае установки пину режима OUTPUT, функция setup() во время инициализации, позволяет задать начальное состояние пина: |
|
45 | На заметку: |
Актуальная версия библиотеки RPi.GPIO (0.6.3) позволяет в качестве значения аргумента initial, вместо констант 1/0 также использовать GPIO.HIGH/GPIO.LOW или True/False.
|
|
46 | Python |
1 2 3 4 pin=14
GPIO.setup(pin, GPIO.OUT, initial=GPIO.HIGH) # Инициализация пина в режим OUTPUT со значением логическая единица HIGH
# или
GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW) # Инициализация пина в режим OUTPUT со значением логический ноль LOW |
|
47 |
Чтение и установка состояний, списки и кортежи Для того, чтобы узнать состояние пина, установленного в режим INPUT можно воспользоваться функцией input(): |
|
48 | Python |
1 2 pin=25
value=GPIO.input(pin) # В переменную value будет записано состояние пина 25 |
|
49 |
Задать состояние пина, инициализированного в режим OUTPUT, можно функцией output() |
|
50 | Python |
1 2 pin=14
GPIO.output(pin, GPIO.HIGH) # Установка на 14 пине логической единицы |
|
51 |
В качестве второго аргумента функции GPIO.output() можно использовать 0/GPIO.LOW/False или 1/GPIO.HIGH/True. |
|
52 |
Ну и напоследок, немного синтаксического сахара. Одним вызовом функции output() можно установить значения произвольного количества пинов. Для этого, в качестве первого аргумента, в эту функцию достаточно передать массив пинов. |
|
53 | На заметку: |
В Python массивы (array) в классическом понимании используются крайне редко. Вместо них используются списки (list) и кортежи (tuple), которые, в отличие от массивов, могут содержать объекты разных типов. Отличие между списками и кортежами заключается в том, что кортеж, это по сути неизменяемый список, и поэтому кортеж занимает меньше памяти.
Инициализация списка осуществляется при помощи квадратных скобок (a=[1,2]), а кортежа — при помощи круглых (a=(1,2)). Подробно — Python Documentation — Data Structures. |
|
54 | Python |
1 2 3 4 5 pins=[14, 15, 18] # Инициализация списка list
# или
pins=(14, 15, 18) # Инициализация кортежа tuple - так тоже корректно
GPIO.setup(pins, GPIO.OUT) # setup() также работает для списка пинов
GPIO.output(pins, GPIO.HIGH) # Установка логической единицы на пинах 14, 15, 18 |
|
55 |
Также, одним вызовом функции output() можно присвоить разные значения разным пинам, передав в качестве второго аргумента список значений: |
|
56 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 GPIO.setmode(GPIO.BCM)
pins=[14, 15, 18] # Инициализация списка пинов
GPIO.setup(pins, GPIO.OUT) # Установка режима работы пинов
values=[GPIO.LOW, GPIO.HIGH, GPIO.HIGH] # Список значений
# или
values=[0, 1, 1]
# или
values=[False, True, True]
# или
values=[GPIO.LOW, 1, True]
GPIO.output(pins, values) # Устанавливаем пинам из списка, значения из списка |
|
57 |
И, в конце концов, созданный ранее список (не кортеж) пинов или значений можно изменять: |
|
58 | Python |
1 2 3 4 5 6 7 8 9 10 GPIO.setmode(GPIO.BCM)
pins=[10, 15, 18] # Ошибочно заданный пин для демонстрации примера
pins[0]=14 # Исправляем ошибку
print(pins) # Выводим список и видим, что все изменения отразились в списке
values=[1, 0, 1] # Задаем значения для пинов
GPIO.setup(pins, GPIO.OUT) # Все пины в режим OUTPUT
GPIO.output(pins, values) # 14 и 18 пины в HIGH, 15 пин - LOW) |
|
59 |
Также в функцию output() можно сразу передавать параметры — списки/кортежи: |
|
60 | Python |
1 2 3 GPIO.setmode(GPIO.BCM)
GPIO.setup([14, 15, 18], GPIO.OUT) # Все пины в режим OUTPUT
GPIO.output([14, 15, 18], [1, 0, 1]) # 14 и 18 пины в HIGH, 15 пин - LOW |
|
61 |
Для установки состояния пина, можно использовать функцию чтения состояния пина: |
|
62 | Python |
1 2 3 4 5 GPIO.setmode(GPIO.BCM)
GPIO.setup(14, GPIO.OUT) # Все пины в режим OUTPUT
while 1:
GPIO.output(14, not GPIO.input(14)) # Инвертируем состояние пина
time.sleep(0.2) # С паузой в 0.2 секунды |
|
63 |
Функция cleanup(), предназначенная для возврата пинов в исходное состояние, может применяться не только ко всем пинам, но и к одному или нескольким конкретным пинам. Для этого, достаточно передать аргументом, номер одного пина или список пинов: |
|
64 | Python |
1 2 3 4 5 GPIO.cleanup() # Вернуть все пины в исходное состояние
# или
GPIO.cleanup(14) # Вернуть в исходное состояние один пин
# или
GPIO.cleanup([14, 15, 18]) # Вернуть в исходное состояние несколько пинов |
|
65 |
Теперь можно применить полученную информацию к собранной схеме (обратите внимание на 17 строку скетча — здесь применена новая структура — словарь (dictionary)): |
|
66 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# === Инициализация пинов ===
GPIO.setmode(GPIO.BCM)
pinsLED=[14, 15, 18] # Три светодиода
pinsBtnsPullUp=[25, 8] # Две кнопки замыкаются на ноль и подтянуты к лог. единице
pinsBtnsPullDown=[7] # Одна кнопка замыкается на единицу и стягивается к нулю
GPIO.setup(pinsLED, GPIO.OUT, initial=0) # Все пины со светодиодами в режим OUTPUT
GPIO.setup(pinsBtnsPullUp, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопки в режим INPUT, к нулю с подтяжкой к единице
GPIO.setup(pinsBtnsPullDown, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Кнопка в режим INPUT, к единице со стяжкой к нулю
dictLED={14:25,
15:8,
18:7} # Создаем словарь для установки соответствия кнопок светодиодам
while 1:
for i in pinsLED: # Перебираем светодиоды
# Далее находим в словаре dictLED кнопку соответствующую светодиоду
# и проверяем её состояние - если кнопки, подтянутые к земле не нажаты, то светодиоды горят, иначе - гаснут
# если кнопка, подтянутая к единице, не нажата, то светодиод не горит, иначе - загоряется
if GPIO.input(i)!=GPIO.input(dictLED[i]):
GPIO.output(i, GPIO.input(dictLED[i]))
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
67 |
Программный ШИМ (PWM) и RPi.GPIO Начиная с версии 0.5.2а библиотеки RPi.GPIO (актуальная на момент написания версия — 0.6.3), на всех GPIO-пинах доступна программная реализация ШИМ. |
|
68 |
Качество программной реализации ШИМ при помощи библиотеки RPi.GPIO оставляет желать лучшего. Причин тому несколько:
|
Джиттер (англ. jitter — дрожание) или фазовое дрожание цифрового сигнала данных — нежелательные фазовые и/или частотные случайные отклонения передаваемого сигнала.
|
69 |
Автору тяжело предположить ситуации, при которых можно советовать пользоваться данной возможностью. По сути, при такой ситуации, сложно даже светодиодом нормально плавно помигать. Тем не менее... |
|
70 | На заметку: |
Поскольку Linux, во-первых, является многозадачной, а во-вторых, не является операционной средой реального времени, не рекомендуется использовать ШИМ на пинах GPIO в ситуациях, когда требуется высокоточное ШИМ-управление оборудованием (например, сервоприводами).
|
|
71 |
Для того, чтобы сгенерировать ШИМ на пине нужно воспользоваться объектом PWM и двумя его методами ChangeDutyCycle (изменить скважность) и ChangeFrequency (изменить частоту): |
|
72 | Python |
1 2 3 4 5 6 7 8 9 10 11 GPIO.setmode(GPIO.BCM) # Выбираем режим нумерации пинов
pinPWM=14
GPIO.setup(pinPWM, GPIO.OUT) # Устанавливаем pinPWM в режим OUTPUT
pwm = GPIO.PWM(pinPWM, 50) # Создаем ШИМ-объект для пина pinPWM с частотой 50 Гц
pwm.start(50) # Запускаем ШИМ на пине со скважностью 50% (0-100%)
# Можно использовать данные типа float - 49.5, 2.45
pwm.ChangeDutyCycle(80) # Изменяем скважность до 80%
pwm.ChangeFrequency(1000) # Изменяем частоту до 1000 Гц (также можно float)
pwm.stop() # Останавливаем ШИМ |
|
73 |
Пример плавного изменения яркости горения (мигания) светодиода представлен в скетче (по описанным выше причинам нормально не работает): |
|
74 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# === Инициализация пинов ===
GPIO.setmode(GPIO.BCM) # Выбираем режим нумерации пинов
pinPWM=14
GPIO.setup(pinPWM, GPIO.OUT) # Устанавливаем pinPWM в режим OUTPUT
pwm = GPIO.PWM(pinPWM, 100) # создаем ШИМ-объект для пина pinPWM с частотой 100 Гц
pwm.start(10) # Запускаем ШИМ на пине со скважностью 10% (0-100%)
# Можно использовать данные типа float - 49.5, 2.45
pwm.ChangeFrequency(1000) # Изменяем частоту до 10 КГц (также можно float)
while 1:
for i in range(0,101):
pwm.ChangeDutyCycle(i) # Изменяем коэффициент запонения (скважность) от 0 до 100%
time.sleep(0.2) # Пауза на 0,1 сек
time.sleep(5) # Пауза на 5 сек, для осциллографа
for i in range(100,-1,-1):
pwm.ChangeDutyCycle(i) # Изменяем коэффициент запонения (скважность) от 100 до 0%
time.sleep(0.2) # Пауза на 0,1 сек
time.sleep(5) # Пауза на 5 сек, для осциллографа
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
pwm.stop() # Останавливаем ШИМ - необязательно
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
75 |
Тестирование ШИМ можно посмотреть в видео: Подписывайтесь на канал , чтобы быть в курсе обновлений! |
|
76 |
Прерывания (interrupts) Начиная с версии 0.5.1 библиотеки RPi.GPIO, работа с прерываниями стала очень простой и доступной. Работа с прерываниями в библиотеке RPi.GPIO представлена 3 основными вариантами:
|
|
77 |
Теперь о каждом из них подробнее. |
|
78 |
wait_for_edge() |
|
79 |
|
|
80 |
Примеры использования: |
|
81 | Python |
1 2 3 4 5 pin=25
GPIO.wait_for_edge(pin, GPIO.RISING)
# ...
# прочий код, который не исполнится, пока на 25 пине
# логический ноль не сменится логической единицей |
|
82 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 pin=25
GPIO.wait_for_edge(pin, GPIO.RISING, timeout=10000)
# ...
# прочий код, который не исполнится, пока на 25 пине
# логический ноль не сменится логической единицей
# или не пройдет 10 секунд
# контроль того, что вывело из функции wait_for_edge() осуществляется так:
pin_wait = GPIO.wait_for_edge(pin, GPIO.RISING, timeout=5000)
if pin_wait is None:
print('Timeout!')
else:
print('Edge detected on pin ', pin_wait) |
|
83 |
Пример для общей схемы статьи. При запуске загорается первый светодиод (пин 14) и горит до тех пор, пока не будет отжата первая кнопка (пин 25). Далее светодиод гаснет на 1 секунду и загорается снова. Теперь он погаснет при наступлении одного из двух событий — если первая кнопка будет нажата или пройдет отведенное время (5 сек заданные в таймауте): |
|
84 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# === Инициализация пинов ===
GPIO.setmode(GPIO.BCM)
pinLED=14 # Пин светодиода
pinBtn=25 # Пин кнопки
GPIO.setup(pinLED, GPIO.OUT, initial=1) # Пин со светодиодом в режим OUTPUT и включаем
GPIO.setup(pinBtn, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопку в режим INPUT, с подтяжкой к единице (замыкается на ноль)
GPIO.wait_for_edge(pinBtn, GPIO.RISING) # Программа не будет исполняться дальше, пока с кнопки не придет фронт (отжатие, 0->1)
GPIO.output(pinLED, 0) # Выключаем светодиод
time.sleep(1) # Пауза на секунду, чтобы увидеть, что сработало)))
# У это части задан таймаут - 5 секунд: светодиод погаснет или по нажатию кнопки, или по прошествии 5 секунд
GPIO.output(pinLED, 1) # Включаем светодиод снова
# Программа не будет исполняться дальше, пока с кнопки не придет спад (нажатие, 1->0)
if GPIO.wait_for_edge(pinBtn, GPIO.FALLING, timeout=5000) is None: # wait_for_edge() cразу добавили в условие
print('wait_for_edge() - Timeout!') # Событие не наступило - выход по таймауту
else: # Событие наступило - кнопка нажата
print('wait_for_edge() - Edge detected on pin ', pinBtn)
GPIO.output(pinLED, 0) # Выключаем светодиод
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
85 |
event_detected() |
|
86 |
|
|
87 |
Примеры использования: |
|
88 | Python |
1 2 3 4 5 pin=14
GPIO.add_event_detect(pin, GPIO.RISING) # Добавляем детектирование восходящего импульса - фронта на 5 пине
# любой другой код, можно добавить сюда цикл на 5 сек для демонстрации
if GPIO.event_detected(pin): # Если событие было зафиксировано, то:
print('Button pressed') |
|
89 |
Пример для общей схемы статьи. Пример демонстрирует, как можно детектировать событие в момент исполнения прочего фрагмента кода: |
|
90 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# === Инициализация пинов ===
GPIO.setmode(GPIO.BCM)
pinLED1=14 # Пин светодиода
pinLED2=15 # Пин светодиода
pinBtn=25 # Пин кнопки замыкается на ноль и подтянут к лог. единице
GPIO.setup([pinLED1, pinLED2], GPIO.OUT, initial=0) # Пины со светодиодом в режим OUTPUT, выключены
GPIO.setup(pinBtn, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопку в режим INPUT, к нулю с подтяжкой к единице
GPIO.add_event_detect(pinBtn, GPIO.FALLING) # Добавляем детектирование события - нажатие кнопки
# Делаем произвольные манипуляции - мигаем первым светодиодом
for i in range(0,10): # Если во время этого цикла (мигания светодиодом) нажать кнопку, то после этого загорится второй светодиод
GPIO.output(pinLED1, 1)
time.sleep(0.2)
GPIO.output(pinLED1, 0)
time.sleep(0.2)
if GPIO.event_detected(pinBtn): # Если за время цикла мигания было нажатие, то загорится 2 светодиод
GPIO.output(pinLED2, 1)
GPIO.wait_for_edge(pinBtn, GPIO.FALLING, timeout=5000) # Выход по нажатию кнопки или через 5 секунд
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
91 |
Threaded callbacks |
|
92 | Важно: |
В скетчах на языке Python, при работе с прерываниями библиотеки RPi.GPIO, функция паузы sleep() не блокирует обработку прерываний так, как это происходит у Arduino с функцией delay().
|
|
93 |
Для работы с этим типом прерываний используются следующие функции: |
|
94 |
|
|
95 |
Для того, чтобы вызвать функцию-обработчик, её нужно написать. Делается это так (имена могут быть произвольными, но в соответствии с правилами именования функций в Python): |
|
96 | Python |
1 2 3 4 5 def callback_func1(pin):
print("Вызывается первая функция-обработчик на пине: "+str(pin))
def callback_func2(pin):
print("Вызывается вторая функция-обработчик на пине: "+str(pin)) |
|
97 |
Схематичный пример работы с прерываниями с их обработкой в функциях обратного вызова: |
|
98 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def callback_func1(pin): # Объявляем функцию-обработчик callback_func1
print("Вызывается первая функция-обработчик на пине: "+str(pin))
def callback_func2(pin): # Объявляем функцию-обработчик callback_func2
print("Вызывается вторая функция-обработчик на пине: "+str(pin))
# Инициализация пинов
# ...
# Можно сразу с детектированием, назначить функцию-обработчик
GPIO.add_event_detect(pin, GPIO.RISING, callback=callback_func1) # Добавляем детектирование события с функцией-обработчиком callback_func1
# А потом функцией add_event_callback() добавлять дополнительные функции-обработчики на этот пин
GPIO.add_event_callback(pin, callback=callback_func2) # Добавляем дополнительный обработчик события - функцию callback_func2
# А можно сначала добавить детектирование, а потом прикрутить обработчики
GPIO.add_event_detect(pin, GPIO.FALLING) # Добавляем детектирование события - например, нажатие кнопки
GPIO.add_event_callback(pin, callback=callback_func1) # Добавляем обработчик события - функцию callback_func1
GPIO.add_event_callback(pin, callback=callback_func2) # Добавляем обработчик события - функцию callback_func1
# Удалить обработчики событий конкретного пина можно функцией remove_event_detect()
GPIO.remove_event_detect(pin) # Удаляем все обработчики пина |
|
99 |
В качестве аргумента callback можно передавать lambda-функцию (лямбда). В данном случае необходимо иметь ввиду, что во-первых, в lambda-функцию можно записать только одно выражение и во-вторых, исполняться она будет быстрее, чем внешняя функция: |
|
100 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pinLED=14
pinBtn=25 # Кнопка замыкается на ноль и подтянута к лог. единице
GPIO.setup(pinLED, GPIO.OUT, initial=0) # Пин со светодиодом в режим OUTPUT
GPIO.setup(pinBtn, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопку в режим INPUT, к нулю с подтяжкой к единице
GPIO.add_event_detect(25, GPIO.BOTH, lambda pin: GPIO.output(pinLED, not GPIO.input(pin))) # При нажатии кнопки светодиод будет загораться
while 1:
time.sleep(0.1)
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
101 |
Устранение дребезга в обработке прерываний |
|
102 |
|
|
103 |
Примеры использования: |
|
104 | Python |
1 2 3 4 5 6 GPIO.add_event_detect(pin, GPIO.RISING, callback=callback_func1, bouncetime=50) # Программное устранение дребезга 50 мс
GPIO.add_event_callback(pin, callback=callback_func2, bouncetime=50) # Программное устранение дребезга 50 мс
# или
GPIO.add_event_detect(pin, GPIO.FALLING) # Добавляем детектирование события - например, нажатие кнопки
GPIO.add_event_callback(pin, callback=callback_func1, bouncetime=500) # Программное устранение дребезга 500 мс
GPIO.add_event_callback(pin, callback=callback_func2, bouncetime=100) # Программное устранение дребезга 100 мс |
|
105 |
Но при применении программного устранения дребезга при нажатиях кнопок, есть вероятность получить не то поведение, на которое рассчитывалось. Особенно это актуально при применении отслеживания событий по фронту и по спаду — GPIO.RISING и GPIO.FALLING. Дело в том, что борьба с дребезгом начинается после наступления события, а это значит, что с большой долей вероятности следующее прерывание будет отработано не так как нужно: |
|
107 |
О этом нужно помнить и логику программы продумывать с учетом этого. Здесь можно посоветовать заменить событие на GPIO.BOTH и в функции реализовать уже проверку состояния пина — в этом случае программное устранение дребезга будет полезным (даже при таком подходе происходят сбои в работе): |
|
108 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
def callback_func1(pin): # Обявляем функцию-обработчик callback_func2
if not GPIO.input(pin): # Если кнопка нажата (на пине 0), то...
GPIO.output(pinLED, not GPIO.input(pinLED)) # ...меняем состояние светодиода
else:
i=0 # Ничего не делаем
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pinLED=14
pinBtn=25 # Кнопка замыкается на ноль и подтянута к лог. единице
GPIO.setup(pinLED, GPIO.OUT, initial=0) # Пин со светодиодом в режим OUTPUT
GPIO.setup(pinBtn, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопку в режим INPUT, к нулю с подтяжкой к единице
GPIO.add_event_detect(25, GPIO.BOTH, callback=callback_func1, bouncetime=200) # Устранение дребезга 200 мс
while 1:
time.sleep(0.1)
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
109 |
Пример для общей схемы статьи. Первый светодиод мигает с частотой 2 Гц. Первая кнопка включает/выключает мигание первого светодиода. Вторая кнопка, в случае мигания первого светодиода, при нажатии изменяет частоту его мигания до 20 Гц. При отпускании второй кнопки, частота мигания возвращается к 2 Гц. Третий светодиод мигает с частотой 0,25 Гц — он демонстрирует, что функция паузы sleep() никак не влияет на работу прерываний. Третья кнопка, в зависимости от закомментированной строки, при нажатии либо удаляет обработчики событий, либо нажатием управляет вторым светодиодом — нажата/горит или не нажата/не горит: |
|
110 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
flagPWM=True # Флаг для контроля состояния ШИМ светодиода
def change_blink1(pin): # Объявляем функцию-обработчик change_blink1
global flagPWM # Даем понять функции, что переменная не локальная, а глобальная
print("Вызывается первая функция-обработчик на пине: "+str(pin))
if not flagPWM: # Если ШИМ выключен, включаем его
print("Включаем мигание на пине: "+str(pin))
pwm.start(50) # Коэффициент заполнения (скважность) 50%
pwm.ChangeFrequency(2) # Частота 2 Гц (2 раза в сек)
else:
print("Выключаем мигание на пине: "+str(pin))
pwm.stop() # Выключаем ШИМ
flagPWM=not flagPWM # Инвертируем флаг
def change_blink2(pin): # Объявляем функцию-обработчик change_blink2
print("Вызывается вторая функция-обработчик на пине: "+str(pin))
if not GPIO.input(pin):
print("Увеличиваем частоту на пине: "+str(pin)+" до 20 Гц")
pwm.ChangeFrequency(20)
else:
print("Возвращаем частоту на пине: "+str(pin)+" до 2 Гц")
pwm.ChangeFrequency(2)
def remove_all_callbacks(pin): # Объявляем функцию-обработчик remove_all_callbacks
print("Удаляем все обработчики")
GPIO.remove_event_detect(25) # Удалаяем все обработчики пина
GPIO.remove_event_detect(8) # Удалаяем все обработчики пина
GPIO.remove_event_detect(7) # Удалаяем все обработчики пина
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pinsLED=[14, 15, 18] # Три светодиода
pinsBtnsPullUp=[25, 8] # Две кнопки замыкаются на ноль и подтянуты к лог. единице
pinsBtnsPullDown=[7] # Одна кнопка замыкается на единицу и стягивается к нулю
GPIO.setup(pinsLED, GPIO.OUT, initial=0) # Все пины со светодиодами в режим OUTPUT
GPIO.setup(pinsBtnsPullUp, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Кнопки в режим INPUT, к нулю с подтяжкой к единице
GPIO.setup(pinsBtnsPullDown, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Кнопка в режим INPUT, к единице со стяжкой к нулю
pwm = GPIO.PWM(14, 2) # Создаем ШИМ-объект для пина 14 с частотой 2 Гц
pwm.start(50) # Запускаем ШИМ на пине со скважностью 10% (0-100%)
GPIO.add_event_detect(25, GPIO.BOTH, callback=change_blink1, bouncetime=500) # Добавляем детектирование события с функцией-обработчиком change_blink1
GPIO.add_event_detect(8, GPIO.BOTH, callback=change_blink2) # Добавляем детектирование события с функцией-обработчиком change_blink2
GPIO.add_event_detect(7, GPIO.BOTH, lambda pin: GPIO.output(15, not GPIO.input(pin))) # Добавляем детектирование события с lambda-функцией
# или
#GPIO.add_event_detect(7, GPIO.RISING, callback=remove_all_callbacks) # Добавляем детектирование события с функцией-обработчиком remove_all_callbacks
while 1:
GPIO.output(18, not GPIO.input(18)) # Мигаем светодиодом раз в 2 секунды
time.sleep(2)
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
111 |
gpio_function() или как узнать настройки порта В библиотеке RPi.GPIO есть функция gpio_function(), которая позволяет узнать в каком режиме работает конкретный пин: |
|
112 | Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import RPi.GPIO as GPIO # Импортируем библиотеку по работе с GPIO
import time # Импортируем класс для работы со временем
import sys, traceback # Импортируем библиотеки для обработки исключений
try:
# Инициализация пинов
GPIO.setmode(GPIO.BCM)
pins = [2, 3, 4, 17, 27, 22, 10, 9, 11, 5, 6, 13, 19, 26, 14, 15, 18, 23, 24, 25, 8, 7, 12, 16, 20, 21]
# Для простоты восприятия создадим словарь с возможными значениями
pin_states = {0:"GPIO.OUT",
1:"GPIO.IN",
40:"GPIO.SERIAL",
41:"GPIO.SPI",
42:"GPIO.I2C",
43:"GPIO.HARD_PWM",
-1:"GPIO.UNKNOWN"}
for pin in pins:
state = GPIO.gpio_function(pin)
print ("%s %d status: %s" % ("pin", pin, pin_states[state]))
print ("Change pins mode")
GPIO.setup([2,3,4], GPIO.OUT)
for pin in [2,3,4]:
state = GPIO.gpio_function(pin)
print ("%s %d status: %s" % ("pin", pin, pin_states[state]))
except KeyboardInterrupt:
# ...
print("Exit pressed Ctrl+C") # Выход из программы по нажатию Ctrl+C
except:
# ...
print("Other Exception") # Прочие исключения
print("--- Start Exception Data:")
traceback.print_exc(limit=2, file=sys.stdout) # Подробности исключения через traceback
print("--- End Exception Data:")
finally:
print("CleanUp") # Информируем сбросе пинов
GPIO.cleanup() # Возвращаем пины в исходное состояние
print("End of program") # Информируем о завершении работы программы |
|
114 |
Отладка (Debugging) Несмотря на кажущуюся простоту, среда разработки Python предоставляет инструменты по отладке написанного кода. Для того, чтобы воспользоваться средствами отладки, необходимо расставить точки останова (breakpoints) — строки кода, в которых хотелось бы посмотреть на состояние переменных. Для этого в среде Python необходимо кликнуть правой кнопкой мыши на необходимую строку кода и в выпавшем контекстом меню выбрать пункт Set Breakpoint. После этого строка будет подсвечена желтым цветом: |
|
116 |
После того, как все необходимые места обозначены, перед запуском, необходимо запустить Debugger (средство отладки). В оболочке Shell меню Debug → Debugger: |
|
118 |
После того как Debugger запущен, можно запускать код — F5. Теперь, при достижении точки останова, исполнение программы остановится и в окне Debugger можно будет увидеть состояния всех переменных: |
|
120 |
При нажатии на кнопку Go выполнение программы продолжится до перехода к следующей точке останова. |
|
122 |
Похожие запросы:
|
|