Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
products:laboratory_iot:exp36 [2020/05/22 14:06] – [Программный код эксперимента] labuser29products:laboratory_iot:exp36 [2024/11/17 16:27] (текущий) – [Программный код эксперимента] labuser30
Строка 12: Строка 12:
  
 ==== Прерывания ==== ==== Прерывания ====
-Оказывается есть отличное решение этой проблемы. Микроконтроллер умеет аппаратно отслеживать изменения состояния выводов. Пока процессор занят чем-то по-настоящему важным за состоянием пинов слежит специальная электронная схема. Как только изменение происходит, схема сигнализирует об этом процессору, который прерывает исполнение основной программы для того, чтобы обработать это прерывание — исполнить небольшой код, реагирующий на событие.+Оказывается есть отличное решение этой проблемы. Микроконтроллер умеет аппаратно отслеживать изменения состояния выводов. Пока процессор занят чем-то по-настоящему важным за состоянием пинов следит специальная электронная схема. Как только изменение происходит, схема сигнализирует об этом процессору, который прерывает исполнение основной программы для того, чтобы обработать это прерывание — исполнить небольшой код, реагирующий на событие.
  
 //Прерывание// — это сигнал (событие), который заставляет контроллер прекратить выполнение текущей задачи и приступить к исполнению другой, имеющей более высокий приоритет. После выполнения высокоприоритетной задачи, контроллер возвращается к той, которой был занят до прерывания. //Прерывание// — это сигнал (событие), который заставляет контроллер прекратить выполнение текущей задачи и приступить к исполнению другой, имеющей более высокий приоритет. После выполнения высокоприоритетной задачи, контроллер возвращается к той, которой был занят до прерывания.
  
-Представь, что что ты — это микрокоонтроллер и ты очень занят важным делом — читаешь эту статью. Но внезапно начинает звонить твой телефон. Ты откладываешь статью и занимаешься обработкой этого прерывания — отвечаешь на звонок и ведешь беседу. Как только беседа завершена, ты возвращаешься к чтению статьи с того места, где прервался. Примерно так устроена обработка прерываний в микроконтроллере.+Представь, что ты — это микроконтроллер и ты очень занят важным делом — читаешь эту статью. Но внезапно начинает звонить твой телефон. Ты откладываешь статью и занимаешься обработкой этого прерывания — отвечаешь на звонок и ведешь беседу. Как только беседа завершена, ты возвращаешься к чтению статьи с того места, где прервался. Примерно так устроена обработка прерываний в микроконтроллере.
  
 ==== Эксперимент ==== ==== Эксперимент ====
-Попробуем применить прерывания для работы с энкодером. Нам потребуется сделать две вещи — настроить прерывание и написать функцию- обработчик прерывания.+Попробуем применить прерывания для работы с энкодером. Нам потребуется сделать две вещи — настроить прерывание и написать функцию-обработчик прерывания.
  
 ==== Схема эксперимента ==== ==== Схема эксперимента ====
Строка 31: Строка 31:
 ==== Программный код эксперимента ==== ==== Программный код эксперимента ====
  
-<file python Exp35[enable_line_numbers="2", start_line_numbers_at="1"]>+<file python Exp36.py[enable_line_numbers="2", start_line_numbers_at="1"]>
 from machine import I2C, Pin from machine import I2C, Pin
 from esp8266_i2c_lcd import I2cLcd from esp8266_i2c_lcd import I2cLcd
Строка 37: Строка 37:
 _init() _init()
  
-DEFAULT_I2C_ADDR = 0x3F+DEFAULT_I2C_ADDR = 0x3F # Или 0x27 в зависимости от модели микросхемы на плате
  
 encA = Pin(13, Pin.IN) encA = Pin(13, Pin.IN)
Строка 57: Строка 57:
     lcd.putstr(str(data))     lcd.putstr(str(data))
  
-def callback2(p):+def callback(p):
     global count     global count
     global encB     global encB
Строка 87: Строка 87:
 lcd.backlight_on() lcd.backlight_on()
  
-encA.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING, handler=callback2+encA.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=callback
-encB.irq(trigger=Pin.IRQ_FALLING|Pin.IRQ_RISING, handler=callback2)+encB.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=callback)
  
  
Строка 95: Строка 95:
 </file> </file>
  
-Сначала настраиваем прерывание+Сначала настраиваем прерывания
-<code python[enable_line_numbers="2", start_line_numbers_at="39"]> +<code python[enable_line_numbers="2", start_line_numbers_at="56"]> 
-encA.irq(trigger=Pin.IRQ_FALLING, handler=callback)+encA.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=callback) 
 +encB.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=callback)
 </code> </code>
  
-Мы настроили, что при возникновении события изменения сигнала на выводе encA с высокого на низкий (''Pin.IRQ_FALLING'') будет вызываться обработчик прерывания — функция ''callback''.+Мы настроили, что при возникновении события изменения сигнала на выводах encA и encB с высокого на низкий (''Pin.IRQ_FALLING'') или с низкого на высокий (''Pin.IRQ_RISING'') будет вызываться обработчик прерывания — функция ''callback''.
  
 Какие могут быть события: Какие могут быть события:
Строка 110: Строка 111:
 События можно комбинировать. Например, если нужна реакция и на событие ''Pin.IRQ_FALLING'' и ''Pin.IRQ_RISING'', то можно записать так: ''trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING)''. События можно комбинировать. Например, если нужна реакция и на событие ''Pin.IRQ_FALLING'' и ''Pin.IRQ_RISING'', то можно записать так: ''trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING)''.
  
-Рассмотрим функцию- обработчик прерывания: +Рассмотрим функцию-обработчик прерывания: 
-<code python[enable_line_numbers="2", start_line_numbers_at="17"]>+<code python[enable_line_numbers="2", start_line_numbers_at="26"]>
 def callback(p): def callback(p):
     global count     global count
     global encB     global encB
     global encA     global encA
-    +    global states 
 +    global old_state 
     value_a = encA.value()     value_a = encA.value()
     value_b = encB.value()     value_b = encB.value()
  
-    if (value_a and value_b) or (not value_a and not value_b): +    current_state = states.index((value_avalue_b)) 
-        print('+'+     
-        count += 1  +    if (current_state -  old_state == 1) or (current_state == 0 and old_state == 3): 
-        print_lcd(count) +        count += 1 
-    elif (not value_a and value_b) or (value_a and not value_b): +        if not (count % 2): 
-        print('-')+            print('+'); 
 +            print_lcd(int(count/2)
 +        old_state = current_state 
 +    elif (current_state -  old_state == -1 or (current_state == 3 and old_state == 0):
         count -= 1         count -= 1
-        print_lcd(count)+        if not (count % 2): 
 +            print('-'); 
 +            print_lcd(int(count/2)) 
 +        old_state = current_state
 </code> </code>
  
 Она начинается с  Она начинается с 
-<code python[enable_line_numbers="2", start_line_numbers_at="18"]>+<code python[enable_line_numbers="2", start_line_numbers_at="27"]>
     global count     global count
     global encB     global encB
     global encA     global encA
 +    global states
 +    global old_state
 </code> </code>
  
-Ключеваое слово ''global'' сообщает интерпретатору Python о том, что мы хотим использовать переменную, объявленную за пределами текущей функции. По умолчанию, все переменные, созданные внутри функции доступны (имеют //область видимости//) в пределах именно этой функции. Как только функция завершается такие переменные уничтожаются. Такие переменные называются //локальными//. Ключевое слово ''global'' дает возможность обратиться к //глобальной// переменной.+Ключевое слово ''global'' сообщает интерпретатору Python о том, что мы хотим использовать переменную, объявленную за пределами текущей функции. По умолчанию, все переменные, созданные внутри функции доступны (имеют //область видимости//) в пределах именно этой функции. Как только функция завершается такие переменные уничтожаются. Такие переменные называются //локальными//. Ключевое слово ''global'' дает возможность обратиться к //глобальной// переменной. 
 + 
 +Потом, как и раньше, мы определяем текущее состояние линий A и B энкодера. Определяем индекс состояния и проверяем можем ли мы сделать переход в данное состояние. 
 + 
 +Как мы заметили ранее, при одном щелчке энкодера происходит смена двух состояний энкодера. Чтобы одному щелчку соответствовало изменение счетчика на 1, мы просто проверяем счетчик на четность.  
 + 
 +<code python[enable_line_numbers="2", start_line_numbers_at="40"]> 
 +         if not (count % 2): 
 +            print('+'); 
 +            print_lcd(int(count/2)) 
 +</code> 
 + 
 +Если число четное (нет остатка от деления на 2) то пишем в терминал + и выводим число, деленное на 2 на дисплей. 
 + 
 +Теперь самое главное. Обратим внимание на основной цикл программы. Мы в нем ничего не делаем. Все действия, связанные с работой с энкодером, происходят в обработчике прерывания и обрабатываются независимо от основной программы. В основном цикле мы можем заняться чем-то другим. В этом и смысл использования прерываний. 
 + 
 +<WRAP center round important 60%> 
 +В этом эксперименте мы производили настройку аппаратных средств микроконтроллера, а именно настраивали прерывания. Как мы уже узнали, работа прерываний не зависит от программы и происходит автоматически. Это значит, что обработчик прерывания будет вызываться даже после завершения работы нашей программы, ведь настройки никуда не исчезли. Это может оказать влияние на следующие эксперименты, в которых не нужны эти прерывания. Они могут мешать. Для того, чтобы следующие эксперименты проходили нормально, **необходимо перезагрузить микроконтроллер**. Это можно сделать выдернув на некоторое время кабель USB или нажать на кнопку Reset на плате конструктора. 
 +</WRAP>
  
-Потом, как и раньше, мы определяем текущ