| 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 | 
                     Похожие запросы: 
  | 
    
         |