Общие сведения
Энкодер - это устройство, которое позволяет определять направление и скорость вращения. В нашем случае энкодер прикреплён к валу и помещён в корпус, похожий на потенциометр, но есть различия: в отличии от типичного потенциометра энкодер можно вращать бесконечно и у экнодера нет определённого значения, то есть в момент включения микроконтроллер не может знать был ли энкодер повёрнут до включения.
Энкодеры бывают разные: оптические, механические, магнитные - мы рассмотрим применение механического энкодера.
Для этого урока нам понадобится:
Для работы данных примеров в 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 можно назначить прерывание со своей функцией. Прерывания могут срабатывать при изменении состояния вывода с логической единицы на логический ноль (FALLING), или наоборот, с логического нуля на логическую единицу (RISING).
#define pinA 3 void ISR() { Serial.println("don't touch me!"); } void setup() { Serial.begin(115200); attachInterrupt(pinA, ISR, FALLING); } void loop() { }
В данном коде ISR - это функция, которая вызывается, кода на выводе pinA происходит смена состояния с логической единицы на логический ноль
В функции attachInterrupt мы указали при изменении уровня какого вывода (pinA) будет происходить прерывание, функцию, которая будет выполняться во время прерывание (ISR) и при каком условии будет происходит прерывание (FALLING).
Если открыть монитор последовательного порта и покрутить энкодер, то будет выводиться сообщение.
Крутим энкодер в разные стороны
В данном примере в монитор порта выводится переменная-счётчик при повороте вала энкодера в разные стороны
#define pinA 3 #define pinB 2 #define pinK 7 volatile int counter = 0; void isrA() { if (!digitalRead(pinA)) { counter++; } } void isrB() { if (!digitalRead(pinB)) { counter--; } } void isrK() { Serial.println("Button pressed!"); } void setup() { Serial.begin(115200); attachInterrupt(pinA, isrA, FALLING); attachInterrupt(pinB, isrB, FALLING); attachInterrupt(pinK, isrK, FALLING); } void loop() { static int last_counter = 0; if (last_counter != counter) { last_counter = counter; Serial.println(counter); } }
Проект Музыкальная шкатулка
Это классический пример музыкальной шкатулки - мы крутим рычаг (в данном случае энкодер), шкатулка (в данном случае зуммер) играет мелодию. Не крутим рычаг - мелодия не играет.
#include <map> #include <array> #define ARR_SIZ 12 #define ZUM_PIN 15 #define pinA 3 // определяем новый тип для ноты typedef std::pair<int, int> note_t; // Массив пар с нотами, // где первое значение - частота ноты в герцах, // второе - длительность в миллисекундах std::array<note_t, ARR_SIZ> notes = { note_t(440, 200), note_t(523, 200), note_t(440, 200), note_t(523, 400), note_t(659, 200), note_t(659, 400), note_t(587, 600), note_t(494, 200), note_t(494, 400), note_t(494, 400), note_t(523, 200), note_t(587, 600), }; volatile int counter = 0; // Функция проигрывания ноты void playNote(note_t note) { analogWriteFreq(note.first); analogWrite(ZUM_PIN, 127); delay(note.second); analogWrite(ZUM_PIN, 0); delay(100); } void isr() { if (!digitalRead(pinA)) counter++; } void setup() { attachInterrupt(pinA, isr, FALLING); } void loop() { static int last_counter = 0; static size_t note_index = 0; if (last_counter != counter) { last_counter = counter; playNote(notes[note_index%ARR_SIZ]); note_index++; } delay(20); }
Обсуждение