ПОДКЛЮЧЕНИЕ ЭНКОДЕРА К ARDUINO

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

ОСОБЕННОСТИ

  • Поворот на неограниченный угол (проворот) с отработкой значений
  • Рукоятка является кнопкой, с модуля идёт отдельный выход
  • Большая куча теории «как это работает» с примерами прошивок (работоспособность не гарантируется) — ссылка
  • Пины CLK и DT подтянуты к GND (работаем как INPUT), пин SW (кнопка) висит в воздухе, поэтому работаю с ним как INPUT_PULLUP
в магазин

СХЕМЫ И БИБЛИОТЕКИ

Библиотеку к энкодеру я делать не стал, вся отработка уместилась в функции encoderTick(). Ниже представлены два варианта скетчей, с использованием прерываний и без них. Без прерываний энкодер можно подключить куда угодно, изменив номера пинов в прошивке. С прерываниями пин CLK обязательно должен быть на 2-ом цифровом пине! Остальные неважно.

перейти на github
Скачать архив с сайта (дубль github)
ПЕРВЫЕ ШАГИ С ARDUINO

ВНИМАНИЕ!

Если вы не знаете, куда подключить Arduino, где взять программу для прошивки, как установить драйвера, как всё настроить и как устанавливать библиотеки — читайте статейку «Первые шаги с Arduino», ссылка справа от этого текста. Там же разобраны типичные ошибки, описаны варианты питания и есть краткий FAQ.

ПЕРВЫЕ ШАГИ С ARDUINO

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ

/*
   Простой скетч с отработкой энкодера. Поворот увеличивает и уменьшает значение переменной counter.
   Вся отработка уместилась в функции encoderTick(). Отработка осуществляется БЕЗ ПРЕРЫВАНИЙ, 
   поэтому для внедрения в проект нужно обеспечить максимальную прозрачность кода 
   (боже упаси delay и безвыходные циклы).
   AlexGyver Technologies http://alexgyver.ru/
*/

//--ПИНЫ ЭНКОДЕРА--
#define CLK 2
#define DT 3
#define SW 4
//--ПИНЫ ЭНКОДЕРА--

long counter = 0;
boolean DT_now, DT_last;

void setup() {
  Serial.begin (9600);
  pinMode (CLK, INPUT);
  pinMode (DT, INPUT);
  DT_last = digitalRead(CLK);         // читаем начальное положение CLK
}

void loop() {
  encoderTick();                      // ФУНКЦИЯ ОТРАБОТКИ ЭНКОДЕРА! СМОТРИ, ВОТ ОНА!
}

void encoderTick() {
  DT_now = digitalRead(CLK);          // читаем текущее положение CLK
  if (DT_now != DT_last) {            // если предыдущее и текущее положение CLK разные, значит был поворот
    if (digitalRead(DT) != DT_now) {  // если состояние DT отличается от CLK, значит крутим по часовой стрелке
      counter ++;                     // прибавить счётчик
    } else {                          // если совпадают, значит против часовой
      counter --;                     // убавить счётчик
    }
    Serial.print("Position: ");
    Serial.println(counter);
  }
  DT_last = DT_now;                   // обновить значение
}
/*
   Простой скетч с отработкой энкодера. Поворот увеличивает и уменьшает значение переменной counter.
   Код использует аппаратные прерывания, то есть отработка энкодера ведётся всегда, 
   даже если в коде используется delay и долгие циклы.
   AlexGyver Technologies http://alexgyver.ru/
*/

//--ПИНЫ ЭНКОДЕРА--
#define CLK 2  // ВАЖНО! СЮДА ПОДКЛЮЧАЕТСЯ ПРЕРЫВАНИЕ!
#define DT 3
#define SW 4
//--ПИНЫ ЭНКОДЕРА--

long counter = 0;
boolean DT_now, DT_last;

void setup() {
  Serial.begin (9600);
  pinMode (CLK, INPUT);
  pinMode (DT, INPUT);
  
  // настройка прерывания. Для работы достаточно одного прерывания на CLK!
  attachInterrupt(0, encoderTick, CHANGE);
  
  DT_last = digitalRead(CLK);         // читаем начальное положение CLK
}

void loop() {
  Serial.println(counter);

  // задержка для демонстрации работы (во время delay идёт отработка энкодера!)
  delay(1000);             
}

void encoderTick() {
  DT_now = digitalRead(CLK);          // читаем текущее положение CLK
  if (DT_now != DT_last) {            // если предыдущее и текущее положение CLK разные, значит был поворот
    if (digitalRead(DT) != DT_now) {  // если состояние DT отличается от CLK, значит крутим по часовой стрелке
      counter ++;                     // прибавить счётчик
    } else {                          // если совпадают, значит против часовой
      counter --;                     // убавить счётчик
    }
  }
  DT_last = DT_now;                   // обновить значение
}
/*
   Скетч про многоцелевую отработку поворота и нажатий энкодера с защитой от дребезга.
   Поворот энкодера меняет переменную norm_counter в большую или меньшую сторону с шагом NORM_STEP
   Поворот С НАЖАТОЙ РУКОЯТКОЙ меняет переменную hold_counter в большую или меньшую сторону с шагом HOLD_STEP
   При одиночном нажатии (БЕЗ ПОВОРОТА) вызывается функция encoderPress
   При нажатии и удержании (~ секунду) вызывается функция encoderHold
   При любом нажатии вызывается функция encoderClick (вдруг пригодится)
   AlexGyver Technologies http://alexgyver.ru/
*/

// ПИНЫ ЭНКОДЕРА
#define CLK 2
#define DT 3
#define SW 4
// ПИНЫ ЭНКОДЕРА

#define NORM_STEP 10  // шаг изменения переменной norm_counter при вращении
#define HOLD_STEP 1   // шаг изменения переменной hold_counter при нажатии, удерживании и вращении

int norm_counter, hold_counter;
boolean DT_now, DT_last, SW_state, hold_flag, butt_flag, turn_flag;
unsigned long debounce_timer;

void setup() {
  Serial.begin (9600);

  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);
  DT_last = digitalRead(CLK);  // читаем начальное положение CLK (энкодер)
}

void encoderClick() {
  Serial.println("Click");
}
void encoderPress() {
  Serial.println("Press");
}
void encoderHold() {
  Serial.println("Hold");
}
void encoderTurn() {
  Serial.print("Turn: ");
  Serial.println(norm_counter);
}
void encoderHoldTurn() {
  Serial.print("HoldTurn: ");
  Serial.println(hold_counter);
}


void loop() {
  encoderTick();                      // ФУНКЦИЯ ОТРАБОТКИ ЭНКОДЕРА! СМОТРИ, ВОТ ОНА!
}


//---------------------------------------------------------------------------
// -------------------------ОТРАБОТКА ЭНКОДЕРА-------------------------------
void encoderTick() {
  DT_now = digitalRead(CLK);          // читаем текущее положение CLK
  SW_state = !digitalRead(SW);        // читаем положение кнопки SW

  // отработка нажатия кнопки энкодера
  if (SW_state && !butt_flag && millis() - debounce_timer > 200) {
    hold_flag = 0;
    butt_flag = 1;
    turn_flag = 0;
    debounce_timer = millis();
    encoderClick();
  }
  if (!SW_state && butt_flag && millis() - debounce_timer > 200 && millis() - debounce_timer < 500) { butt_flag = 0; if (!turn_flag && !hold_flag) { // если кнопка отпущена и ручка не поворачивалась turn_flag = 0; encoderPress(); } debounce_timer = millis(); } if (SW_state && butt_flag && millis() - debounce_timer > 800 && !hold_flag) {
    hold_flag = 1;
    if (!turn_flag) {  // если кнопка отпущена и ручка не поворачивалась
      turn_flag = 0;
      encoderHold();
    }
  }
  if (!SW_state && butt_flag && hold_flag) {
    butt_flag = 0;
    debounce_timer = millis();
  }

  if (DT_now != DT_last) {            // если предыдущее и текущее положение CLK разные, значит был поворот
    if (digitalRead(DT) != DT_now) {  // если состояние DT отличается от CLK, значит крутим по часовой стрелке
      if (SW_state) {           // если кнопка энкодера нажата
        hold_counter += HOLD_STEP;
        encoderHoldTurn();
      } else {                  // если кнопка энкодера не нажата
        norm_counter += NORM_STEP;
        encoderTurn();
      }
    } else {                          // если совпадают, значит против часовой
      if (SW_state) {           // если кнопка энкодера нажата
        hold_counter -= HOLD_STEP;
        encoderHoldTurn();
      } else {                  // если кнопка энкодера не нажата
        norm_counter -= NORM_STEP;
        encoderTurn();
      }
    }
    turn_flag = 1;                    // флаг что был поворот ручки энкодера
  }
  DT_last = DT_now;                   // обновить значение для энкодера
}
// -------------------------ОТРАБОТКА ЭНКОДЕРА-------------------------------
//---------------------------------------------------------------------------
/*
   Скетч про многоцелевую отработку поворота и нажатий энкодера с защитой от дребезга.
   Код использует аппаратные прерывания, то есть отработка энкодера ведётся всегда, 
   даже если в коде используется delay и долгие циклы.
   Поворот энкодера меняет переменную norm_counter в большую или меньшую сторону с шагом NORM_STEP
   Поворот С НАЖАТОЙ РУКОЯТКОЙ меняет переменную hold_counter в большую или меньшую сторону с шагом HOLD_STEP
   При одиночном нажатии (БЕЗ ПОВОРОТА) вызывается функция encoderPress
   При нажатии и удержании (~ секунду) вызывается функция encoderHold
   При любом нажатии вызывается функция encoderClick (вдруг пригодится)
   AlexGyver Technologies http://alexgyver.ru/
*/

// ПИНЫ ЭНКОДЕРА
#define CLK 2  // ВАЖНО! СЮДА ПОДКЛЮЧАЕТСЯ ПРЕРЫВАНИЕ!
#define DT 3
#define SW 4
// ПИНЫ ЭНКОДЕРА

#define NORM_STEP 1   // шаг изменения переменной norm_counter при вращении
#define HOLD_STEP 1   // шаг изменения переменной hold_counter при нажатии, удерживании и вращении

int norm_counter, hold_counter;
boolean DT_now, DT_last, SW_state, hold_flag, butt_flag, turn_flag;
unsigned long debounce_timer;

void setup() {
  Serial.begin (9600);

  pinMode(CLK, INPUT);
  pinMode(DT, INPUT);
  pinMode(SW, INPUT_PULLUP);

  // настройка прерывания. Для работы достаточно одного прерывания на CLK!
  attachInterrupt(0, encoderTick, CHANGE);

  DT_last = digitalRead(CLK);  // читаем начальное положение CLK (энкодер)
}

void encoderClick() {

}
void encoderPress() {

}
void encoderHold() {

}
void encoderTurn() {

}
void encoderHoldTurn() {

}


void loop() {
  Serial.print("Turn: ");
  Serial.print(norm_counter);
  Serial.print("  HoldTurn: ");
  Serial.println(hold_counter);
  delay(1000);
}


//---------------------------------------------------------------------------
// -------------------------ОТРАБОТКА ЭНКОДЕРА-------------------------------
void encoderTick() {
  DT_now = digitalRead(CLK);          // читаем текущее положение CLK
  SW_state = !digitalRead(SW);        // читаем положение кнопки SW

  // отработка нажатия кнопки энкодера
  if (SW_state && !butt_flag && millis() - debounce_timer > 200) {
    hold_flag = 0;
    butt_flag = 1;
    turn_flag = 0;
    debounce_timer = millis();
    encoderClick();
  }
  if (!SW_state && butt_flag && millis() - debounce_timer > 200 && millis() - debounce_timer < 500) {
    butt_flag = 0;
    if (!turn_flag && !hold_flag) {  // если кнопка отпущена и ручка не поворачивалась
      turn_flag = 0;
      encoderPress();
    }
    debounce_timer = millis();
  }

  if (SW_state && butt_flag && millis() - debounce_timer > 800 && !hold_flag) {
    hold_flag = 1;
    if (!turn_flag) {  // если кнопка отпущена и ручка не поворачивалась
      turn_flag = 0;
      encoderHold();
    }
  }
  if (!SW_state && butt_flag && hold_flag) {
    butt_flag = 0;
    debounce_timer = millis();
  }

  if (DT_now != DT_last) {            // если предыдущее и текущее положение CLK разные, значит был поворот
    if (digitalRead(DT) != DT_now) {  // если состояние DT отличается от CLK, значит крутим по часовой стрелке
      if (SW_state) {           // если кнопка энкодера нажата
        hold_counter += HOLD_STEP;
        encoderHoldTurn();
      } else {                  // если кнопка энкодера не нажата
        norm_counter += NORM_STEP;
        encoderTurn();
      }
    } else {                          // если совпадают, значит против часовой
      if (SW_state) {           // если кнопка энкодера нажата
        hold_counter -= HOLD_STEP;
        encoderHoldTurn();
      } else {                  // если кнопка энкодера не нажата
        norm_counter -= NORM_STEP;
        encoderTurn();
      }
    }
    turn_flag = 1;                    // флаг что был поворот ручки энкодера
  }
  DT_last = DT_now;                   // обновить значение для энкодера
}
// -------------------------ОТРАБОТКА ЭНКОДЕРА-------------------------------
//---------------------------------------------------------------------------

2018-03-06T00:38:48+00:00