5. Переключатель, обратные вызовы и прерывания

На pyboard есть две небольшие кнопки, называющиеся USR и RST.

RST переключатель делает аппаратную перезагрузку (hard-reset) и, если вы на него - pyboard перезагрузится; аналогично выключению и включению микроконтроллера.

USR переключатель для общего пользования и управляется с помощью объекта Switch. Создание объекта переключателя:

>>> sw = pyb.Switch()

Помните, вам скорее всего потребуется подключить pyb: import pyb, если вы получите ошибку “the name pyb does not exist”.

С помощью переключателя вы можете получить свой статус:

>>> sw()
False

Будет получено False если переключатель не нажат, или True если он нажат. Попробуйте удерживать USR пока выполняется вышеуказанная команда.

5.1. Переключение функции обратного вызова (callback)

Переключатель очень простой объект, но у него одно расширение функционала: функция sw.callback(). Функция обратного вызова устанавливает алгоритм действий когда кнопка нажата и использует прерывания. Как работают прерывания, наверное, лучше всего понять на примере. Попробуйте запустить следующий код:

>>> sw.callback(lambda:print('press!'))

Каждый раз при нажатии на USR выводится press! Пойдём далее и нажмём USR переключатель - смотрите что выводится на экран. Обратите внимание что этот вывод прерывает всё, что вы вводите: пример асинхронных прерываний.

В качестве другого примера, попробуйте:

>>> sw.callback(lambda:pyb.LED(1).toggle())

Кнопка USR будет включать и выключать красный светодиод. И это будет работать даже вовремя выполнения другого кода.

Чтобы отключить коллбэк - передайте в него None:

>>> sw.callback(None)

Вы можете передать в коллбэк любую функцию, которая не содержит аргументов. Выше мы использовали lambda - особенная функция для создания анонимных функций налету. Вместо неё мы могли бы сделать:

>>> def f():
...   pyb.LED(1).toggle()
...
>>> sw.callback(f)

Здесь создаётся функция f и передаётся коллбэку переключателя USR. Такой способ имеет смысл, если ваша функция более сложная, чем позволяет сделать lambda.

Обратите внимание, что ваши функции обратного вызова не должны выделять никакую память (например они не могут создать кортеж или список). Функции обратного вызова должны быть относительно простыми. Если вам нужно создать список - сделайте это заранее и сохраните его в глобальной переменной (или создайте его внутри коллбэка и в нём же обязательно удалите). Если вам нужны долгие сложные расчёты - используйте коллбэк-функцию чтобы установить флаг (ссылку) на код, где будут реализованы требуемые расчёты.

5.2. Технические детали прерываний

Давайте рассмотрим подробнее что происходит с переключателем обратного вызова. Когда вы передаёте функцию в sw.callback() - переключатель устанавливает внешний триггер (falling edge) прерывания на пин (pin), который соединён с кнопкой USR. Это означает, что микроконтроллер слушает этот пин и отлавливает любые изменения. Происходит следующее:

  1. При нажатии на кнопку происходит изменение на контакте (пине): замыкание; и микроконтроллер регистрирует это.
  2. Микроконтроллер завершает выполнение текущей машинной инструкции, останавливает выполнение и сохраняет текущее состояние (записывает регистры в стек). Это прерывает выполнение любого скрипта на плате.
  3. Микроконтроллер начинает выполнять специальный обработчик прерывания, связанного с внешним триггером переключателя USR. Этот обработчик прерываний получает функцию, которую вы передали в sw.callback(), и выполняет её.
  4. Функция обратного вызова выполняется пока не закончит; затем вернёт управление обработчику прерывания.
  5. Обработчик прерывания сообщает микроконтроллеру, что прерывание завершилось.
  6. Микроконтроллер восстанавливает состояние, сохранённое на шаге 2.
  7. Выполнение программы продолжается с того места, на котором остановилось. Пауза на выполнении кода никак не скажется.

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