Arduino

Поворотный энкодер и кнопочные/переключательные модули (key switch, tilt switch, металлосенсор)

3 просмотров

Введение

Поворотный энкодер (rotary encoder) и несколько разных по принципу действия переключателей (модуль переключателя ключей, переключатель наклона, металлический сенсорный модуль, модуль «для охоты»/»hit») объединены в одну статью как разные способы получения дискретного пользовательского ввода или обнаружения механических событий.

Концепция

Поворотный энкодер даёт информацию о направлении и количестве «шагов» вращения через два сигнала (A и B), сдвинутые по фазе друг относительно друга — анализ последовательности изменений этих двух сигналов (квадратурное декодирование, концептуально упомянутое в Raspberry Pi-цикле, но здесь применительно к Arduino) позволяет определить направление вращения, в отличие от простого потенциометра, дающего абсолютное, но ограниченное по диапазону положение. Переключатель наклона (tilt switch, обычно ртутный или шариковый механический переключатель) замыкается/размыкается в зависимости от ориентации в пространстве относительно гравитации — простая, дискретная альтернатива полноценному акселерометру для задач, где достаточно знать «наклонено/не наклонено», а не точный угол. Металлический сенсорный модуль (touch sensor) реагирует на касание токопроводящей поверхности (часто используется как простая, не механическая кнопка); модули «hit»/»для охоты» по описанию в наборе — вероятно, простые датчики удара/вибрации, аналогичные модулю переключателя вибрации, уже отдельно упомянутому в исходном списке.

Пример кода

// Поворотный энкодер — квадратурное декодирование направления вращения
const int ENCODER_PIN_A = 2;
const int ENCODER_PIN_B = 3;

volatile int encoderPosition = 0;
volatile int lastEncoded = 0;

void encoderInterruptHandler() {
    int MSB = digitalRead(ENCODER_PIN_A);
    int LSB = digitalRead(ENCODER_PIN_B);
    int encoded = (MSB << 1) | LSB;
    int sum = (lastEncoded << 2) | encoded;

    // Таблица переходов для определения направления — стандартный паттерн квадратурного декодирования
    if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderPosition++;
    if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderPosition--;

    lastEncoded = encoded;
}

void setupEncoder() {
    pinMode(ENCODER_PIN_A, INPUT_PULLUP);
    pinMode(ENCODER_PIN_B, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), encoderInterruptHandler, CHANGE);
    attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_B), encoderInterruptHandler, CHANGE);
}
// Переключатель наклона, металлический сенсор касания, модуль ключевого переключателя —
// все три дают простой дискретный сигнал, аналогичный обычной кнопке
const int TILT_PIN = 4;
const int TOUCH_PIN = 5;
const int KEY_SWITCH_PIN = 6;

void setupSwitches() {
    pinMode(TILT_PIN, INPUT_PULLUP);
    pinMode(TOUCH_PIN, INPUT);       // многие touch-модули имеют собственную обработку, без необходимости pullup
    pinMode(KEY_SWITCH_PIN, INPUT_PULLUP);
}

void checkSwitches() {
    bool tilted = !digitalRead(TILT_PIN);
    bool touched = digitalRead(TOUCH_PIN);
    bool keyTurned = !digitalRead(KEY_SWITCH_PIN);

    if (tilted) Serial.println("EVENT:TILTED");
    if (touched) Serial.println("EVENT:TOUCHED");
    if (keyTurned) Serial.println("EVENT:KEY_ACTIVATED");
}

Пояснения к коду

Таблица переходов в encoderInterruptHandler() — стандартный, проверенный паттерн квадратурного декодирования: каждая допустимая последовательность из четырёх бит (два предыдущих состояния плюс два новых) однозначно соответствует определённому направлению вращения, и недопустимые комбинации (не входящие в таблицу) означают либо очень быстрое вращение, пропустившее промежуточное состояние, либо шум/дребезг сигнала — в обоих случаях обработчик просто не изменяет позицию для нераспознанной комбинации. Прерывания, привязанные к обоим пинам энкодера (CHANGE — срабатывание при любом изменении, и нарастающем, и спадающем фронте), необходимы, поскольку для надёжного декодирования важно зафиксировать каждое отдельное изменение любого из двух сигналов, а не только одного из них.

Подводные камни

  • Использование одного прерывания только на одном из двух пинов энкодера (только A или только B), вместо обоих — это упускает половину переходов состояния, необходимых для надёжного квадратурного декодирования, и хотя простейшие реализации иногда обходятся одним прерыванием для базового подсчёта «шагов» без надёжного определения направления, для корректного определения именно направления вращения (а не только факта вращения) нужны прерывания на обоих сигналах, как показано в примере.
  • Слишком быстрое вращение энкодера, генерирующее изменения сигналов быстрее, чем успевает обработать прерывание (особенно на более медленных Arduino-платах) — это может привести к пропуску промежуточных состояний и временной потере точности подсчёта позиции; для приложений с ожидаемым очень быстрым вращением стоит рассмотреть более быстрые платформы (ESP32) или специализированные аппаратные счётчики, доступные на некоторых микроконтроллерах.
  • Использование ртутного переключателя наклона без учёта экологических и нормативных ограничений на ртуть в некоторых регионах/применениях, и без понимания различия между ртутным и более современным шариковым типом переключателя наклона (механический шарик, замыкающий контакт при наклоне, без токсичной ртути) — при заказе и использовании таких модулей стоит уточнить конкретный тип используемого механизма, особенно для проектов, предназначенных для длительной эксплуатации или образовательных целей с участием детей, где ртутные компоненты особенно нежелательны.
  • Доверие неточным, машинно переведённым названиям модулей («для охоты», «hit») без точной идентификации реального назначения и схемы подключения конкретного физического модуля — как уже отмечалось, для модулей с неясным из перевода назначением стоит искать маркировку непосредственно на самой плате модуля (обычно содержащую более точное, оригинальное название производителя) и соответствующую документацию, а не строить логику работы с модулем на основе предположений из неточного перевода названия.