CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

Энкодер и шкала

Да, именно такое сочетание никак не связанных между собой девайсов мы рассмотрим сегодня. Объектами для изучения будут:
  • Энкодер механический инкрементальный EC12E24404A6
  • Шкала линейная 10-разрядная DC-10GWA
Энкодер по-научному он называется «преобразователь угол-код», или сокращённо ПУК (:
Название говорит само за себя: он позволяет перевести угол поворота в некий код. Абсолютные энкодеры выдают непосредственно угол, абсолютное положение, тогда как инкрементальный — определённое число щелчков на оборот и направление.

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



Здесь изображены датчики которые реагируют на «дырки» — это могут быть оптические датчики работающие на просвет или отражение, магнитные, реагирующие на намагниченность диска или контактные, размыкаемые диэлектрическим диском.

А как насчёт рассматриваемого нами экземпляра?



При вращении ручки вращается скользящий контакт сложной формы над тремя секторами, которые соединены с соответствующими выводами. Вот он и замыкает средний сектор на контактные площадки то левого, то правого вывода в нужной последовательности.

Если записать уровни напряжения как биты 2-битного числа, то последовательность двоичных кодов за один «щелчок» ручкой энкодера будет такой:

00 - 0
01 - 1
11 - 3
10 - 2
Если крутить ручку в обратную сторону, последовательность кодов тоже будет обратной. На первый взгляд, особого смысла в этой последовательности нет. А на второй взгляд, это код Грея (:
Приведённая выше последовательность кодов — это ничто иное, как числа от 0 до 3, записанные в коде Грея. То есть, после декодирования мы получим такую последовательность:

00 - 0
01 - 1
10 - 2
11 - 3
Если крутить ручку энкодера в другую сторону, получим обратную последовательность: 3, 2, 1, 0. То есть, зная последовательность чисел, поступающую с энкодера, можно легко понять, в какую сторону крутится ручка.

Рассматриваемый энкодер EC12E24404A6 имеет 24 отсчёта, то есть его выводы сменят состояние от 00 до 11 ровно 24 раза за один полный оборот ручки, а это значит, что за один щелчок ручка поворачивается на 15 градусов.

С теорией ознакомились — пора проверять на практике. Подсоединим энкодер к Arduino следующим образом:
  • средний вывод — к +5 В
  • остальные два вывода — прижаты к земле (GND) через резисторы 10 кОм и подключены к пинам 2 и 3 Arduino


При нейтральном положении ручки с энкодера будут считываться нули. Наваяем простой скетч, считывающий значения с энкодера и показывающий направление вращения. В статье на Википедии приведена функция декодирования двоичного кода Грея в обычный двоичный, которой мы и воспользуемся, лишь слегка улучшив стиль кода.

/* Пины, к которым подключен энкодер */
enum { ENC_PIN1 = 2, ENC_PIN2 = 3 };

void setup()
{
 pinMode(ENC_PIN1, INPUT);
 pinMode(ENC_PIN2, INPUT);
 
 Serial.begin(9600);
}

/* Функция декодирования кода Грея, взятая с Википедии.
* Принимает число в коде Грея, возвращает обычное его представление.
*/
unsigned graydecode(unsigned gray)
{
 unsigned bin;

 for (bin = 0; gray; gray >>= 1)
    bin ^= gray;

 return bin;
}

void loop()
{
 static uint8_t previous_code = 0; // предыдущий считанный код
 
 /* gray_code - считанное с энкодера значение
  * code - декодированное значение
  */
 uint8_t gray_code = digitalRead(ENC_PIN1) | (digitalRead(ENC_PIN2) << 1),
         code = graydecode(gray_code);
 
 /* Если считался нуль, значит был произведён щелчок ручкой энкодера */
 if (code == 0)
 {
   /* Если переход к нулю был из состояния 3 - ручка вращалась
    * по часовой стрелке, если из 1 - против.
    */
   if (previous_code == 3)
     Serial.println("->");
   else if (previous_code == 1)
     Serial.println("<-");
 }
 
 /* Сохраняем код и ждём 1 мс - вполне достаточно опрашивать энкодер
  * не более 1000 раз в секунду.
  */
 previous_code = code;
 delay(1);
}

Залейте скетч и откройте Serial monitor. Повращайте ручку энкодера и посмотрите, как скетч определяет направление вращения. Выглядеть это будет так:



Ну, покрутили ручкой, а дальше что? А дальше — покрутим ещё раз, но на сей раз визуализировав процесс при помощи 10-разрядной шкалы DC-10GWA:



Шкала представляет собой банальную сборку независимых светодиодов с катодами со стороны надписи на корпусе. Ставить 10 резисторов к шкале на макетную плату мне было жутко влом, так что я применил резисторную сборку из 9 резисторов по 470 Ом и один резистор на 510 Ом. Сборка резисторов выглядит так:



Общий провод — там, где нарисован белый ромбик. Подключаем общий провод к земле, оставшиеся 9 выводов — к анодам шкалы, а один оставшийся анод подключаем к земле отдельным резистором. Катоды шкалы подключаем последовательно к пинам 4-13 Arduino. Далее шьём такой скетч:

enum
{
 ENC_PIN1 = 2,
 ENC_PIN2 = 3,
 BAR_FIRST_PIN = 4, // первый пин, поключённый к шкале
 BAR_PINS_COUNT = 10 // число пинов (делений шкалы)
};

void setup()
{
 pinMode(ENC_PIN1, INPUT);
 pinMode(ENC_PIN2, INPUT);

 /* Конфигурируем все пины, к которым подключена шкала, на выход */
 for (int i = 0; i < BAR_PINS_COUNT; ++i)
   pinMode(BAR_FIRST_PIN + i, OUTPUT);

 Serial.begin(9600);
}

unsigned graydecode(unsigned gray)
{
 unsigned bin;

 for (bin = 0; gray; gray >>= 1)
   bin ^= gray;

 return bin;
}

void loop()
{
 static uint8_t previous_code = 0;
 static int bar_level = 0; // "уровень", который показывает шкала

 uint8_t gray_code = digitalRead(ENC_PIN1) | (digitalRead(ENC_PIN2) << 1),
         code = graydecode(gray_code);

 if (code == 0)
 {
   /* Увеличиваем или уменьшаем "уровень" шкалы, запихивая его
    * в диапазон от 0 до 10 стандартной для Arduino функцией constrain().
    */
   if (previous_code == 3)
   {
     bar_level = constrain(bar_level + 1, 0, BAR_PINS_COUNT);
     Serial.println(bar_level);
   }
   else if (previous_code == 1)
   {
     bar_level = constrain(bar_level - 1, 0, BAR_PINS_COUNT);
     Serial.println(bar_level);
   }
 }

 /* Зажигаем количество полосок на шкале, соответствующее
  * текущему "уровню", гасим остальные полоски.
  */
 for (int i = 0; i < BAR_PINS_COUNT; ++i)
   digitalWrite(BAR_FIRST_PIN + i, (i < bar_level ? HIGH : LOW));

 previous_code = code;
 delay(1);
}

Теперь в Serial monitor будет закидываться текущий «уровень», который будет отображаться на шкале:



Так как самый правый катод шкалы оказался подключен к 13 пину, при выкручивании максимального уровня дополнительно загорается светодиод L (:

Шкала занимает аж 10 выводов, а ведь хочется подключать и другие устройства — эту проблему можно решить, используя сдвиговые регистры 74HC595, о использовании которых я писал тут и тут.

Кроме зажигания шкалы, у энкодеров могут найтись и более интересные применения: регулировка звука в магнитоле, навигация в меню, и вообще можно задавать всякие дискретные величины. Ещё энкодеры применяют для получения обратной связи от вращающихся механизмов (моторов, например), обычно оптические и магнитные. Оптический можно сделать из двух фотоинтерапторов или дискретных ИК-передатчиков и приёмников, а магнитный — из датчиков Холла (цифровых или аналоговых). Механические энкодеры тут не подойдут — сотрутся контакты.

UPDATE: по однократной просьбе представителя прекрасной половины человечества выкладываю видео-демонстрацию (:

  • +6
  • 31 августа 2011, 16:19
  • burjui

Комментарии (18)

RSS свернуть / развернуть
+
0
А есть идеи как определить направление вращения с помощью датчиков Холла? Нужно определять кол-во оборотов и направление вращения лебедки.
avatar

Toxa

  • 31 августа 2011, 18:38
+
+1
Ну, тут принцип будет такой же: размещаешь два датчика на небольшом расстоянии друг от друга на неподвижной части, а на лебёдке — магнит. При вращении лебёдки и прохождении магнита напротив датчиков они будут поочерёдно срабатывать, выдавая тот же код Грея. Как на цветной схеме вначале статьи, только вместо «дырки» на вращающемся диске — магнит.
Может, следующую статью посвятить датчикам Холла? (:
avatar

burjui

  • 31 августа 2011, 18:50
+
0
А какое максимальное расстояние между датчиком и магнитом?
Да, статья про датчики Холла была бы интересна)
avatar

Toxa

  • 31 августа 2011, 19:03
+
0
А какое максимальное расстояние между датчиком и магнитом?

Этого я пока не знаю, но предполагаю, что около сантиметра или даже больше. Как с датчиками поиграю — будет ясно (:
avatar

burjui

  • 31 августа 2011, 19:13
+
0
Буду с нетерпением ждать статью:)
Расстояние важно так как лебедка достаточно быстрая ( около 100об\мин) и мощная (около 250тонн). Не хотелось бы чтобы при работе разнесло конструкцию из датчиков)
avatar

Toxa

  • 31 августа 2011, 19:16
+
0
Мне не нравятся такие схемы, как в этом топике и поэтому я создал схему в Fritzing.
Энекодер в Fritzing
«Энекодер в Fritzing» на Яндекс.Фотках

За место энкодера тут потенциометр!!! Просто в стандартной библиотеке элементов нет энкодера, поэтому изобразил так. Надо считать, что энкодер расположен ножками к нам.
avatar

saippuakauppias

  • 26 сентября 2011, 22:51
+
+1
Вообще-то, в топике вообще нет ни одной схемы, только фотки. А в этом Фрице сглаживание линий нихт арбайтен лесенка гут? (:
avatar

burjui

  • 27 сентября 2011, 00:32
+
0
Извиняюсь) Вы, конечно, правы, что схем нет, а только фотки :)
Я сам этой программой пользовался один раз и не нашёл там как в макетной плате выровнять провода. В принципиальной схеме и печатной плате есть автотрассировка для выравнивания соединений.
avatar

saippuakauppias

  • 27 сентября 2011, 05:26
+
0
Да не, я к тому, что странно это — в 21 веке есть ещё программы, которые не используют сглаживание линий, и в результате возникает «лесенка», алгоритмы сглаживания-то уже десятки раз описаны (:
avatar

burjui

  • 27 сентября 2011, 12:03
+
0
Возможно, где-то оно есть, просто я не знаю как включать ^^
К другим статьям тоже буду делать такие схемы — там разберусь как включить)
avatar

saippuakauppias

  • 27 сентября 2011, 15:18
+
0
С вашего позволения свой вариантик простого кода выложу. Может кому полезно будет.


/*Простой логический код на энкодер от Peter=))*/


int n;

void setup()
{
 pinMode(2, INPUT);
 pinMode(3, INPUT);
 
 Serial.begin(9600);
}

void loop()
{


if (digitalRead(3)==HIGH) // В одну сторону
{
while(digitalRead(3)==HIGH);
while(digitalRead(2)==HIGH);

delayMicroseconds(10);
 
n=n-1;
Serial.println(n);
}


if (digitalRead(2)==HIGH) // В другую
{
while(digitalRead(2)==HIGH);
while(digitalRead(3)==HIGH);

delayMicroseconds(10);
 
n=n+1;
Serial.println(n);
}
}








avatar

Peter

  • 29 февраля 2012, 23:16
+
+1
А оно у Вас работает? Именно в таком виде???
Мне просто интересно, что Вы хотели сказать вот этим:
while(digitalRead(3)==HIGH);
while(digitalRead(2)==HIGH);

Что оно должно делать?
Это задержка до смены уровня?
А если энкодер остановлен и уровень не сменится?
А если мне надо делать еще что-нибудь, кроме обслуживания единственного энкодера?
avatar

able

  • 1 марта 2012, 06:50
+
0
Это конечно же тестовый код…

Да он просто ждет смены уровня.

while(digitalRead(3)==HIGH);
while(digitalRead(2)==HIGH); 


Только единственное, на среднюю ногу энкодора я подавал HIGH.
avatar

Peter

  • 4 марта 2012, 14:38
+
+1
Но вопрос всё тот же:
А если энкодер остановлен и уровень не сменится?
Программа-то зависнет. Намертво.
Так код писать просто нельзя.
avatar

able

  • 4 марта 2012, 16:13
+
0
Согласен. Этот код я написал, чтобы понять насколько быстро можно вращать энкодер, чтобы данные были четкими, т.к. коды выше не могут похвастаться стабильной работой при быстром вращении энкодера. Чтобы прога не «висела», то можно сделать не пустой цикл, а таймер с выходом… Это просто демо для проверки энкодера, идея так скажем… Но критику тем не менее принимаю, и согласен, что если нужна работа программы во время вращения энкодера, и защитится от зависания, код надо изменить.
avatar

Peter

  • 6 марта 2012, 18:26
+
0
получим обратную последовательность: 3, 2, 1, 0.

Обратная последовательность должна быть 2, 3, 1, 0
avatar

GraninDm

  • 28 июня 2013, 10:39
+
0
Крутим в одну сторону — 0, 1, 2, 3
Крутим в другую — 3, 2, 1, 0
Я говорю сейчас о кодах Грея (не Саши, а Франка). 2, 3, 1, 0 — это если читать числа, как есть. А в коде Грея это 3, 2, 1, 0. Если что-то не понятно, почитайте статью о коде Грея по приведённой в статье ссылке.
avatar

burjui

  • 1 июля 2013, 21:32
+
0
Да, про декодирование упустил.
Только в нем вообще нет никакого смысла.
Важна только последовательность смены кодов.
А преобразования — это лишние телодвижения.
avatar

GraninDm

  • 2 июля 2013, 06:38

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.