Общие сведения
Энкодер - это устройство, которое позволяет определять направление и скорость вращения. В нашем случае энкодер прикреплён к валу и помещён в корпус, похожий на потенциометр, но есть различия: в отличии от типичного потенциометра энкодер можно вращать бесконечно и у экнодера нет определённого значения, то есть в момент включения микроконтроллер не может знать был ли энкодер повёрнут до включения.
Энкодеры бывают разные: оптические, механические, магнитные - мы рассмотрим применение механического энкодера.
Для этого урока нам понадобится:
Для работы данных примеров в Pi Pico необходимо загрузить прошивку MicroPython и установить Thonny IDE, о том как это сделать можно прочитать в этой статье https://lesson.iarduino.ru/page/pi-pico-python-ide
Подключение
Подключим Trema-модуль Зуммер согласно таблице
Вывод модуля | Вывод Pi Pico |
---|---|
Vcc | 3V3(OUT) |
GND | GND |
S | GP15 |
Подключим Trema-модуль Энкодер согласно таблице
Вывод модуля | Вывод Pi Pico |
---|---|
Vcc | 3V3(OUT) |
GND | GND |
A | GP3 |
B | GP2 |
K | GP7 |
О том, что такое прерывание
Конечно, неплохо постоянно опрашивать состояние кнопки в цикле и надеяться, что микроконтроллер прочтёт его в нужное время. Но что если пока кнопка была нажата пока микроконтроллер был занят чем-то другим? Вот тут на помощь и приходят так называемые "Прерывания" или Interrupt Request (IRQ) или запрос на прерывание.
Уже из названия понятно, что что-то прерывается, и это что-то - основной код программы. Если назначить на какой-либо вывод Pi Pico прерывание, то при изменении состояния этого вывода (например нажатие кнопки) будет выполнен специальный код. Конечно, внутри прерывания не стоит громоздить целую новую программу - это может привести к нестабильной работе, но вот изменить какую-либо переменную или сделать быстрое вычисление - это как раз самое то.
На каждый вывод Pi Pico можно назначить прерывание со своей функцией. Прерывания могут срабатывать при изменении состояния вывода с логической единицы на логический ноль (IRQ_FALLING), или наоборот, с логического нуля на логическую единицу (IRQ_RISING).
Для начала введём в оболочку следующие строки:
Подключим библиотеки
>>> from machine import Pin >>> from time import sleep_ms as delay
Создадим объект вывода
>>> A = Pin("GP3")
Зададим прерывание на выводе
>>> A.irq(trigger=Pin.IRQ_RISING, handler=lambda Pin: print("don't thouch me!"))
trigger
- здесь задаётся при каком событии будет происходить прерывание (IRQ_RISING, IRQ_FALLING)handler
- это функция, которая будет вызвана при возникновении прерывания. В данном случае мы передали безымянную лямбда-функцию, но здесь может быть и любая другая функция, определённая в программе.
Теперь, если покрутить энкодер, в оболочку будет выводиться
сообщение! При этом микроконтроллер будет отвечать на ваши команды,
если, предположим, далее ввести print("Hello")
, то он с
радостью выполнит эту функцию.
>>> print("hello") hello >>> don't thouch me! don't thouch me! don't thouch me! don't thouch me!
Крутим энкодер в разные стороны
В данном примере в оболочку выводится переменная-счётчик при повороте вала энкодера в разные стороны
# Подключаем Выводы from machine import Pin # Подключаем функцию сна в миллисекундах и называем её from time import sleep_ms as delay # Создаём объекты выводов энкодера A = Pin("GP3", Pin.IN) B = Pin("GP2", Pin.IN) K = Pin("GP7", Pin.IN) # создаём переменные-счётчики last_counter = 0 counter = 0 # Функция-обработчик для прерывания вывода A энкодера def callbackA(Pin): global counter if not Pin.value(): counter += 1 # Функция-обработчик для прерывания вывода B энкодера def callbackB(Pin): global counter if not Pin.value(): counter -= 1 # Функция-обработчик для прерывания вывода K энкодера # Для просторы просто выводит сообщение в оболочку, # но сообщения в прерываниях лучше не выводить, потому # как многие микроконтроллеры могут использовать прерывания в функции `print` def callbackK(Pin): print("button pressed!") # Назначаем прерывания на выводы A.irq(trigger=Pin.IRQ_FALLING, handler=callbackA) B.irq(trigger=Pin.IRQ_FALLING, handler=callbackB) K.irq(trigger=Pin.IRQ_FALLING, handler=callbackK) # Бесконечный цикл while True: # Если изменился счётчик - выводим его в оболочку if last_counter != counter: last_counter = counter print(counter) delay(20)
Проект Музыкальная шкатулка
Это классический пример музыкальной шкатулки - мы крутим рычаг (в данном случае энкодер), шкатулка (в данном случае зуммер) играет мелодию. Не крутим рычаг - мелодия не играет.
from machine import Pin, PWM from time import sleep_ms as delay # Массив кортежей с нотами, # где первое значение - частота ноты в герцах, # второе - длительность в миллисекундах notes = [ (440, 200), (523, 200), (440, 200), (523, 400), (659, 200), (659, 400), (587, 600), (494, 200), (494, 400), (494, 400), (523, 200), (587, 600), ] # Создаём объект вывода А энкодера A = Pin("GP3", Pin.IN) # Создаём объект зуммера zum = PWM(Pin("GP15")) # Устанавливаем широту ШИМ zum.duty_u16(32000) # Функция проигрывания ноты def playNote(note): zum.freq(note[0]) delay(note[1]) zum.deinit() delay(100) # Проигрываем первую ноту, для сигнализации готовности к работе playNote(notes[0]) # создаём переменные-счётчики last_counter = 0 counter = 0 note_index = 0 # Функция-обработчик для прерывания вывода A энкодера def callbackA(Pin): global counter if not Pin.value(): counter += 1 # Назначаем прерывания на вывод А A.irq(trigger=Pin.IRQ_FALLING, handler=callbackA) # Бесконечный цикл while True: # Если изменился счётчик if last_counter != counter: last_counter = counter # Проигрываем текущую ноту по индексу playNote(notes[note_index%len(notes)]) # Приращиваем индекс note_index += 1 delay(20)
Обсуждение