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

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

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

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

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

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

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

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

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

Для того что бы отслеживать изменение положения энкодера в фоновом режиме, я использую прерывание 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<<PCIE1); Включить прерывание PCINT1 PCMSK1 = 0b00001110; // Разрешить прерывание для A1, A2, A3 Serial.begin(115200);
} //****************************************
void loop() { if(enc_state==1) // Если энкодер вращался без нажатия { Serial.print("Вращение без нажатия "); Serial.println(enc_rotation); enc_state=0; //обнуляем статус энкодера } if(enc_state==2) // Если энкодер вращался с нажатием { Serial.print("Вращение с нажатием "); Serial.println(btn_enc_rotate); enc_state=0; //обнуляем статус энкодера } if(enc_state==3) // Если было нажатие на кнопку { Serial.println("Нажатие кнопки "); enc_state=0; //обнуляем статус энкодера } if(enc_state==4) // Если было длинное нажатие на кнопку { Serial.println("Длинное нажатие кнопки "); enc_state=0; //обнуляем статус энкодера }
} //****************************************
ISR (PCINT1_vect) //Обработчик прерывания от пинов A1, A2, A3
{ uint8_t comb = bitRead(PINC, 3) << 2 | bitRead( PINC, 2)<<1 | bitRead(PINC, 1); //считываем состояние пинов энкодера и кнопки if (comb == 3 && lastcomb == 7) btn_press=1; //Если было нажатие кнопки, то меняем статус if (comb == 4) //Если было промежуточное положение энкодера, то проверяем его предыдущее состояние { if (lastcomb == 5) --enc_rotation; //вращение по часовой стрелке if (lastcomb == 6) ++enc_rotation; //вращение против часовой enc_state=1; // был поворот энкодера btn_enc_rotate=0; //обнулить показания вращения с нажатием } if (comb == 0) //Если было промежуточное положение энкодера и нажатие, то проверяем его предыдущее состояние { if (lastcomb == 1) --btn_enc_rotate; //вращение по часовой стрелке if (lastcomb == 2) ++btn_enc_rotate; //вращение против частовой enc_state=2; // был поворот энкодера с нажатием enc_rotation=0; //обнулить показания вращения без нажатия btn_press=0; //обнулить показания кнопки } if (comb == 7 && lastcomb == 3 && btn_press) //Если было отпускание кнопки, то проверяем ее предыдущее состояние { if (millis() - timer > btn_long_push) // проверяем сколько прошло миллисекунд { enc_state=4; // было длинное нажатие } else { enc_state=3; // было нажатие } btn_press=0; //обнулить статус кнопки } timer = millis(); //сброс таймера lastcomb = comb; //сохраняем текущее состояние энкодера
}

Заключение

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

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

Читайте так же:

  • Новинки ASUS с выставки CES 2022Новинки ASUS с выставки CES 2022 Привет, Хабр! В рамках мероприятия CES 2022 мы показали много новых устройств. Я знаю, что не у всех получилось посмотреть презентацию, поэтому в этой статье я постараюсь рассказать вам о каждом из них, чтобы вы смогли составить общее впечатление.Скажу сразу, что на онлайн-презентации […]
  • World Affiliate ShowWorld Affiliate Show Описание В Москве 10 июня 2021 года в Loft Hall состоится грандиозное событие: Форум World Affiliate Show 2021. Среди участников и экспонентов WAS 21: крупнейшие компании арбитражной индустрии. Владельцы бизнесов. Представители крупных партнерских сетей. Нашумевших стартапов. Значимые […]
  • Zoom готовит к запуску сервис для проведения онлайн-конференцийZoom готовит к запуску сервис для проведения онлайн-конференций Zoom анонсировал запуск расширенной версии сервиса под названием Zoom Events. В ней компании смогут проводить крупные мероприятия длительностью в несколько дней и решать все организационные вопросы. Запуск новой платформы запланирован на лето. Сервис Zoom Events разрабатывается на […]
  • GitOps, облачный вендор-лок и Jenkins-джобы: что будет на Luxoft TechFest #5GitOps, облачный вендор-лок и Jenkins-джобы: что будет на Luxoft TechFest #5 Как перестать создавать Jenkins-джобы руками? Как избежать зависимости от конкретного облачного провайдера? Как Git может помочь не только в разработке, но и в развертывании приложений? Обо всем этом пойдет речь на Luxoft TechFest #5: бесплатном онлайн-мероприятии с тремя докладами по […]