Различия

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

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

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
products:laboratory_iot:exp35 [2020/05/22 11:19] labuser29products:laboratory_iot:exp35 [2024/11/16 11:25] (текущий) – [Программный код эксперимента] labuser30
Строка 1: Строка 1:
-===== Эксперимент 35. Прерывания =====+===== Эксперимент 35. Конечные автоматы =====
  
-В предыдущих уроках, мы писали достаточно простые программы. +Коне́чный автома́т — абстрактный автоматчисло возможных внутренних состояний которого конечно. Если говорить прощето с помощью конечного автомата описываются состояния какого либо объекта и переходы между этими состояниями. Например, светофор можно описать с помощью конечного автомата.
-Их простота заключалась в линейности логики алгоритма. В главном цикле проверялось состояние кнопки и сразу же изменялось +
-состояние светодиодаЗадержка, если необходимо, вносилась +
-с помощью функции time.sleep(). Этот пример отлично работает, с +
-небольшими схемами и примитивной логикой. Но если хочется +
-чего-то большегото старыми средствами уже не обойтись и от +
-использования time.sleep() придется отказаться.+
  
-Ранее, чтобы проверить состояние вывода микроконтроллера мы использовали команду вроде ''value_a = encA.value()''. Чтобы узнать изменилось ли состояние по сравнению с предыдущей проверкой мы хранили ее результат в переменной и сравнивали. А если нам нужно отслеживать короткие изменения состояния пинов, то проверять их состояние нужно как можно чаще. Все это заставляет микроконтроллер тратить процессорное время на простую работу с сигналом.+{{ :products:esp-iot:states.png?nolink |}}
  
-==== Прерывания ==== +Видно, что из состояния 1 (красного сигнала) светофор может перейти только в состояние 2 (красный + желтый), означающий скорое включение зеленого. Из состояния 2 светофор может перейти только в состояние 3 (зеленый сигнал). После зеленого всегда идет желтый сигнал остояние 4), который сменяется красным (состояние 1). Главное, что у данного конечного автомата есть 4 состояния и мы знаем из какого состояния в какое он может переходить.
-Оказывается есть отличное решение этой проблемы. Микроконтроллер умеет аппаратно отслеживать изменения состояния выводов. Пока процессор занят чем-то по-настоящему важным за состоянием пинов слежит специальная электронная схема. Как только изменение происходит, схема сигнализирует об этом процессору, который прерывает исполнение основной программы для тогочтобы обработать это прерывание — исполнить небольшой код, реагирующий на событие.+
  
-//Прерывание// — это сигнал (событие), который заставляет контроллер прекратить выполнение текущей задачи и приступить к исполнению другой, имеющей более высокий приоритетПосле выполнения высокоприоритетной задачи, контроллер возвращается к той, которой был занят до прерывания.+Конечные автоматы иногда очень полезны для описания состояний электроники. Возьмем тот же инкрементальный энкодер. Снова посмотрим на график сигналов от него: 
 +{{ :encoder.png?nolink |}}
  
-Представьчто что ты — это микрокоонтроллер и ты очень занят важным делом — читаешь эту статью. Но внезапно начинает звонить твой телефон. Ты откладываешь статью и занимаешься обработкой этого прерывания — отвечаешь на звонок и ведешь беседу. Как только беседа завершенаты возвращаешься к чтению статьи. Примерно так устроена обработка прерываний в микроконтроллере.+По графику видно, что состояния у энкодера не меняются хаотично. Они меняются только последовательно. Если крутить ручку в одну сторону, то состояния сменяются 0-1-2-3-0..., а если в другую, то 0-3-2-1-0... 
 + 
 +Зная это мы можем отфильтровывать ложные показания из-за дребезга контактов и точно отслеживать импульсы. Попробуем обрабатывать сигналы энкодера с помощью конечного автомата. 
 + 
 +==== Схема эксперимента ==== 
 +{{ :products:esp-iot:exp14_sch.png?nolink |}} 
 +//Рисунок 1. Электрическая принципиальная схема эксперимента// 
 + 
 +{{ :products:esp-iot:exp14_mon.png?direct&600 |}} 
 +//Рисунок 2. Монтажная схема эксперимента// 
 + 
 +==== Программный код эксперимента ==== 
 + 
 +<file python Exp35.py[enable_line_numbers="2", start_line_numbers_at="1"]> 
 +from time import sleep_ms, ticks_ms 
 +from machine import I2C, Pin 
 +from esp8266_i2c_lcd import I2cLcd 
 +_init() 
 + 
 +DEFAULT_I2C_ADDR = 0x3F # Или 0x27 в зависимости от модели микросхемы на плате 
 + 
 +encA = Pin(13, Pin.IN) 
 +encB = Pin(12, Pin.IN) 
 + 
 +states = ( 
 +    (0,0), 
 +    (1,0), 
 +    (1,1), 
 +    (0,1) 
 +
 + 
 +old_state = 0 
 + 
 +count = 0 
 + 
 + 
 +def print_lcd(data): 
 +    lcd.clear() 
 +    lcd.putstr(str(data)) 
 + 
 + 
 +i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000) 
 +lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16) 
 +lcd.backlight_on() 
 + 
 + 
 +while True: 
 +    value_a = encA.value() 
 +    value_b = encB.value() 
 + 
 +    state = states.index((value_a, value_b)) 
 +     
 +    if (state - old_state == 1) or (state == 0 and old_state == 3): 
 +        count += 1 
 +        print('+'); 
 +        print_lcd(count) 
 +        old_state = state 
 +    elif (state - old_state == -1)  or (state == 3 and old_state == 0): 
 +        count -= 1 
 +        print('-'); 
 +        print_lcd(count) 
 +        old_state = state 
 +</file> 
 + 
 +Описываем возможные состояния конечного автомата: 
 +<code python[enable_line_numbers="2", start_line_numbers_at="11"]> 
 +states = ( 
 +    (0,0), 
 +    (1,0), 
 +    (1,1), 
 +    (0,1) 
 +
 +</code> 
 +Список из 4 элементов. В каждом элементе первая цифра это состояние сигнала A, вторая — сигнала B. 
 +Номер состояния — это индекс элемента списка. Логика переключения состояний автомата простая — состояние может смениться только на соседнее: состояние 0 на 1, 1 на 2, 2 на 3, 3 на 0. И в обратном направленииСостояние не может измениться "перескочив" через другое. Эту логику мы опишем в программе далее. 
 + 
 +В переменной ''old_state'' будем хранить последнее состояние конечного автомата, чтобы  понимать какое состояние было и какое у него теперь будет. 
 + 
 +В основном цикле программы мы получаем данные о текущем состоянии линий А и B: 
 +<code python[enable_line_numbers="2", start_line_numbers_at="34"]> 
 +    value_a = encA.value() 
 +    value_b = encB.value() 
 +</code> 
 + 
 +И определяем номер состояния конечного автомата, соответствующего такому состоянию А и B: 
 +<code python[enable_line_numbers="2", start_line_numbers_at="37"]> 
 +    state = states.index((value_a, value_b)) 
 +</code> 
 + 
 +Формируем список с состояниями А и B ''(value_a, value_b)'' и  
 +с помощью оператора ''index'' мы получаем индекс такого состояния в ''states''
 + 
 +Теперь мы знаем индекс только что измеренного состояния линий. А индекс последнего состояния конечного автомата хранится в переменной ''old_state''. Теперь нам нужно понять можем ли мы из состояния записанного в ''old_state'' переключиться в новое состояние, индекс которого мы определили и записали в ''state''
 + 
 +<code python[enable_line_numbers="2", start_line_numbers_at="39"]> 
 +if (state - old_state == 1) or (state == 0 and old_state == 3): 
 +</code> 
 + 
 +Если индекс нового состояния на 1 больше старого или, если новое состояние 0, а старое 3, то регистрируем переход в новое состояние. Переводим конечный автомат в новое состояние ''old_state = state''. Увеличиваем на 1 счетчик ''count'', печатаем + в терминал и отображаем счетчик на дисплее. 
 + 
 +Аналогично для вращения в обратную сторону. Если индекс состояния уменьшился на один, то производим аналогичные действия. 
 + 
 +Когда ты запустишь эту программу в конструкторе, то увидишь, что значение на дисплее всегда меняется сразу на 2 и в терминале появляется по два или -. Дело в том, что наш энкодер имеет фиксацию положений (при вращении ощущаются толчки). И эта фиксация осуществляется не в каждом состоянии, а через один. Таким образом от щелчка до щелчка энкодер проходит 2 состояния. Поэтому и число изменяется на 2.