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

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





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

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

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

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

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

Ссылки




Обсуждение

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