Это старая версия документа!


Эксперимент 35. Прерывания

В предыдущих уроках, мы писали достаточно простые программы. Их простота заключалась в линейности логики алгоритма. В главном цикле проверялось состояние кнопки и сразу же изменялось состояние светодиода. Задержка, если необходимо, вносилась с помощью функции time.sleep(). Этот пример отлично работает, с небольшими схемами и примитивной логикой. Но если хочется чего-то большего, то старыми средствами уже не обойтись и от использования time.sleep() придется отказаться.

Ранее, чтобы проверить состояние вывода микроконтроллера мы использовали команду вроде value_a = encA.value(). Чтобы узнать изменилось ли состояние по сравнению с предыдущей проверкой мы хранили ее результат в переменной и сравнивали. А если нам нужно отслеживать короткие изменения состояния пинов, то проверять их состояние нужно как можно чаще. Все это заставляет микроконтроллер тратить процессорное время на простую работу с сигналом.

Прерывания

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

Прерывание — это сигнал (событие), который заставляет контроллер прекратить выполнение текущей задачи и приступить к исполнению другой, имеющей более высокий приоритет. После выполнения высокоприоритетной задачи, контроллер возвращается к той, которой был занят до прерывания.

Представь, что что ты — это микрокоонтроллер и ты очень занят важным делом — читаешь эту статью. Но внезапно начинает звонить твой телефон. Ты откладываешь статью и занимаешься обработкой этого прерывания — отвечаешь на звонок и ведешь беседу. Как только беседа завершена, ты возвращаешься к чтению статьи с того места, где прервался. Примерно так устроена обработка прерываний в микроконтроллере.

Эксперимент

Попробуем применить прерывания для работы с энкодером. Нам потребуется сделать две вещи — настроить прерывание и написать функцию- обработчик прерывания.

Схема эксперимента

Оставим схему прошлого эксперимента без изменений. Всё новое будет заключаться в программе. Рисунок 1. Электрическая принципиальная схема эксперимента

Рисунок 2. Монтажная схема эксперимента

Программный код эксперимента

Exp35
  1. from machine import I2C, Pin
  2. from esp8266_i2c_lcd import I2cLcd
  3. _init()
  4.  
  5. DEFAULT_I2C_ADDR = 0x3F
  6.  
  7. encA = Pin(13, Pin.IN)
  8. encB = Pin(12, Pin.IN)
  9.  
  10. count = 0
  11.  
  12.  
  13. def print_lcd(data):
  14. lcd.clear()
  15. lcd.putstr(str(data))
  16.  
  17. def callback(p):
  18. global count
  19. global encB
  20. global encA
  21.  
  22. print('start')
  23.  
  24. value_a = encA.value()
  25. value_b = encB.value()
  26.  
  27. if (value_a and value_b) or (not value_a and not value_b):
  28. print('+')
  29. count += 1
  30. print_lcd(count)
  31. elif (not value_a and value_b) or (value_a and not value_b):
  32. print('-')
  33. count -= 1
  34. print_lcd(count)
  35. print('stop')
  36.  
  37.  
  38. i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
  39. lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16)
  40. lcd.backlight_on()
  41.  
  42. encA.irq(trigger=Pin.IRQ_FALLING, handler=callback)
  43.  
  44.  
  45. while True:
  46. pass

Сначала настраиваем прерывание:

  1. encA.irq(trigger=Pin.IRQ_FALLING, handler=callback)

Мы настроили, что при возникновении события изменения сигнала на выводе encA с высокого на низкий (Pin.IRQ_FALLING) будет вызываться обработчик прерывания — функция callback.

Какие могут быть события:

  • Pin.IRQ_FALLING изменение с 1 на 0
  • Pin.IRQ_RISING изменение с 0 на 1
  • Pin.IRQ_LOW_LEVEL наличие логического 0
  • Pin.IRQ_HIGH_LEVEL наличие логической 1

События можно комбинировать. Например, если нужна реакция и на событие Pin.IRQ_FALLING и Pin.IRQ_RISING, то можно записать так: trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING).