Подключение энкодера к Ардуино и полнофункциональный код обработки для него

Энкодер — это устройство преобразования механического перемещения или угловых изменений положения в цифровой сигнал. В статье рассматривается самый популярный в DIY сообществе инкрементальный энкодер EC11 с кнопкой. При его вращении на выходах A и B формируются TTL сигналы в виде импульсов сдвинутые между собой по фазе на 90 градусов. Таким образом с его помощью, можно определить направление и скорость вращения, а так же рассчитать угол поворота. В отличие от потенциометров, энкодер KY-040 гораздо надежней и долговечный.

22a5ff285e867efc5728c1d43ee1c0b2.jpg

Немного подробностей

Собирая один из проектов с использованием encoder. Я не смог найти код для Ардуино выполняющий все мои условия. Так как для проекта нужно обрабатывать следующие команды: «Вращение без нажатия», «Вращение с нажатием», «Нажатие» и «Длинное нажатие», а так же требуется стабильная работа энкодера. Скетчи использующие один пин с прерыванием INT0 или INT1, работают отвратительно и при вращении вала энкодера вылетает очень много ошибок. Код без использования прерываний работает стабильно, но он не работает в фоновом режиме, его нужно встраивать в тело основной программы, что в свою очередь приводит к не своевременному срабатыванию обработчика и пропускам при вращении энкодера. Еще хуже обстоят дела с обработкой нажатия с вращением вала энкодера и обычным с нажатием. Пришлось написать свой код обработки, который исключает описанные выше проблемы. С дребезгом контактов я не стал бороться программно, так как это приводит к задержкам обработки. Проще и надежней использовать керамические конденсаторы.

Схема подключения энкодера к Ардуино

Для считывания сигналов с выходов EC-11, нужно использовать три цифровых входа Arduino. В схеме подключения я использовал редко используемые мной в своих проектах выводы Arduino (A1, A2 и A3). Внешние подтягивающие резисторы отсутствуют, так как я использовал внутреннюю подтяжку микроконтроллера. Конденсаторы нужны для гашения импульсов дребезга контактов. Если у вас новый и хороший энкодер, то можно обойтись и без них. Но на кнопку в любом случае потребуется конденсатор, так как ее дребезг неизбежен.

Используемые в схеме компоненты:

Arduino nano — 1 шт.
Энкодер EC11 -1 шт.
Соединительные повода — 4 шт.
Керамические конденсаторы 0,1 мкФ — 3 шт.

730c5df74f85f6718abbd82ba75e8564.jpg

Скетч для Ардуино

Для того что бы отслеживать изменение положения энкодера в фоновом режиме, я использую прерывание PCINT1. Обработка всех функций происходит в прерывании, обработчик в зависимости от произошедшего действия изменяет переменную enc_state. Если значение переменной enc_state=0 — ничего не произошло, enc_state=1 — экодер вращался без нажатия, enc_state=2 — экодер вращался с нажатием, enc_state=3 — было нажатие на кнопку, enc_state=4 — было длинное нажатие на кнопку, Прерывание будет срабатывать каждый раз по изменению состояния входов, как с высокого уровня на низкий, так и наоборот. То есть при одном щелчке энкодера прерывание сработает 4 раза. Или по 2 раза для каждого из входов. Но обработчик выдаст сигнал поворота только 1 раз на все 4 прерывания.
Код обработчика при каждом срабатывании записывает в переменную lastcomb состояние входов, к которым подключен энкодер. И ждет состояние когда выходы A и B будут замкнуты на GND, это гарантированный сигнал того, что энкодер вращается. После того как этот сигнал получен, обработчик проверяет в какую сторону было вращение. Для этого он сравнивает его предыдущее значение из переменной lastcomb и в зависимости от фазы сдвига определит в какую сторону был поворот ротора. Как я писал ранее, сложнее всего отслеживать нажатие кнопки.
Так как использовать определенные тайминги я не планировал, потому, что они неизбежно приводят длительным задержкам работы обработчика и основной программы, или требуют использование таймера, которых в микроконтроллере всего 3 шт. их, как правило никогда не хватает. Собственно проблема состояла в том, чтобы разделить «нажатие с последующим вращением» от простого нажатия. В итоге как вы уже можете убедиться, я решил эту задачу. Оптимизацией кода я не стал заниматься, потому как все работает и меня все устраивает. Для наглядности в коде все действия с энкодером, отображаются в Serial мониторе программы Adruino IDE.

/*
При публичном размещении кода ссылка на первоисточник обязательна.
*/

#define btn_long_push 1000   // Длительность долинного нажатия кнопки
volatile uint8_t lastcomb=7, enc_state, btn_push=0;
volatile int enc_rotation=0, btn_enc_rotate=0;
volatile boolean btn_press=0;
volatile uint32_t timer;

//********************************
void setup() 
{
  pinMode(A1,INPUT_PULLUP); // ENC-A
  pinMode(A2,INPUT_PULLUP); // ENC-B
  pinMode(A3,INPUT_PULLUP); // BUTTON

  PCICR =  0b00000010; // PCICR |= (1< btn_long_push)         // проверяем сколько прошло миллисекунд
    {
      enc_state=4;                              // было длинное нажатие 
    } else {
             enc_state=3;                    // было нажатие 
            }
      btn_press=0;                           //обнулить статус кнопки
    }
   
  timer = millis();                       //сброс таймера
  lastcomb = comb;                        //сохраняем текущее состояние энкодера
}

Заключение

Результат работы кода меня порадовал и теперь я могу продолжить работу над своим новым проектом, который скоро здесь выложу. Надеюсь эта короткая статья вам понравилась и вы сможете воспользоваться моей наработкой в своих самоделках.

Если у Вас остались вопросы и замечания, пишите их в комментариях. Я с удовольствием на них отвечу.

© Habrahabr.ru