![]() |
![]() |
![]() |
![]() |
Общие сведения
Энкодер - это устройство, которое позволяет определять направление и скорость вращения. В нашем случае энкодер прикреплён к валу и помещён в корпус, похожий на потенциометр, но есть различия: в отличии от типичного потенциометра энкодер можно вращать бесконечно и у экнодера нет определённого значения, то есть в момент включения микроконтроллер не может знать был ли энкодер повёрнут до включения.
Энкодеры бывают разные: оптические, механические, магнитные - мы рассмотрим применение механического энкодера.
Для этого урока нам понадобится:
Для работы данных примеров в 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);
}





Обсуждение