КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Raspberry Pi Pico: работаем с энкодером





Общие сведения

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

Энкодеры бывают разные: оптические, механические, магнитные - мы рассмотрим применение механического энкодера.

Для этого урока нам понадобится:

Для работы данных примеров в 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)

Ссылки




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями