Урок 55. Работа с инкрементальным энкодером в Ардуино. Библиотека Encod_er.h.

Encoder EC11

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

Предыдущий урок     Список уроков     Следующий урок

Мне заказали разработку программы для устройства, в котором в качестве управляющего элемента используется инкрементальный энкодер. Поэтому я решил написать внеплановый урок о работе с энкодером в системе Ардуино.

 

Совсем коротко, о чем идет речь, т.е. о классификации энкодеров.

Энкодеры это цифровые датчики угла поворота. Другими словами преобразователи угол-код.

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

Энкодеры бывают абсолютные и накапливающие (инкрементальные).

Абсолютные энкодеры формируют на выходе код, соответствующий текущему углу положения вала. У них нет памяти. Можно выключить устройство, повернуть вал энкодера, включить и на выходе будет новый код, показывающий новое положение вала. Такие энкодеры сложные, дорогие, часто используют для подключения стандартные цифровые интерфейсы RS-485 и им подобные.

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

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

В подобных случаях инкрементальные энкодеры становятся идеальными устройствами управления, установки параметров, выбора меню. Они намного удобнее, чем кнопки ”+” и ”-”.

В этих случаях можно использовать самые простые механические инкрементальные энкодеры, которые отличаются низкой ценой.

По моей партнерской ссылке механический инкрементальный энкодер с кнопкой EC11 стоит всего 50 руб. При покупке 5 штук 45 руб., при 10 - 40 руб.

Encoder EC11

 

Принцип действия механического инкрементального энкодера.

Импульсы на выходе инкрементального энкодера должны сообщать не только о повороте вала, но и о направлении поворота. Поэтому необходимо использовать 2 сигнала, которые обычно обозначаются A и B. Эти сигналы подключены к механическим контактам энкодера, другие выводы которых соединены на выводе C.

Принцип действия энкодера

Если подключить к сигналам A и B подтягивающие резисторы, то диаграмма выходных сигналов при вращении энкодера будет выглядеть так.

Диаграмма работы инкрементального энкодера

Если вы покрутите вал энкодера, то заметите, что у него есть фиксированные положения. У моего энкодера 20 таких положений на оборот. Т.е. точность определения угла 360° / 20 = 18° или 20 импульсов на оборот.

Эти, механически зафиксированные, положения отмечены на диаграмме стрелочками снизу вверх. В этот момент оба контакта разомкнуты, сигналы A и B находятся в высоком уровне.

На первых 2 отрезках диаграммы энкодер повернули по часовой стрелке. Сначала в низкий уровень упал сигнал A, затем B. На 3 и 4 интервалах вал вращают против часовой стрелки. После фиксированного положения сначала становится равным нулю сигнал B. По последовательности изменения сигналов можно определить, в какую сторону повернули энкодер.

Формализованный алгоритм анализа сигналов энкодера выглядит так.

  • Если в момент перехода сигнала A в низкое состояние сигнал B находится в высоком уровне, то произошел поворот по часовой стрелке.
  • Если в момент перехода сигнала A в низкое состояние сигнал B находится в низком уровне, то был поворот против часовой стрелки.

Возможны другие кодировки положения энкодера. Все, что я пишу верно, относительно EC11.

 

Параметры инкрементальных энкодеров.

Подробно технические характеристики и параметры энкодера EC11 в формате PDF можно посмотреть по этой ссылке EC11.pdf. Я приведу и поясню основные.

Encoder EC11

  • Главный параметр – число импульсов на оборот. Определяет точность измерения угла. У моего датчика 20 импульсов на оборот, точность 18°.
  • Предельно допустимые электрические параметры определяют предельные значения тока и напряжения для контактов. В моем случае это 10 мА и 5 В. Эти параметры влияют на выбор подтягивающих резисторов. Сопротивление резисторов не должно быть ниже 500 Ом.
  • Прочность изоляции. Я бы не рискнул использовать подобный энкодер в цепях, гальванически связанных с высоким напряжением.
  • Износоустойчивость. Разработчики гарантируют от 15 000 до 1 000 000 циклов, в зависимости от модификации.
  • Рабочий диапазон температур -30 … +85 °C.
  • Механические параметры: габаритные и установочные размеры, моменты вращения и т.п.

 

Подключение энкодера к плате Ардуино.

С электрической точки зрения энкодер это 3 кнопки: сигналы A, B и кнопка. Я использовал внутренние подтягивающие резисторы, но в рабочих схемах лучше добавить внешние резисторы сопротивлением 2 – 10 кОм.

Подключение Encoder EC11 к Ардуино

Программная обработка сигналов энкодера.

Сначала приведу простую программу, позволяющую определить кодировку энкодера.

void setup() {
  Serial.begin(9600); // инициализируем порт, скорость 9600
  pinMode(2, INPUT_PULLUP); // определяем вывод как вход
  pinMode(8, INPUT_PULLUP); // определяем вывод как вход
}
void loop() {
  if( digitalRead(2) == HIGH ) Serial.print("H ");
  else Serial.print("L ");
  if( digitalRead(8) == HIGH ) Serial.println("H");
  else Serial.println("L");
  delay(200);
}

У меня энкодер подключен к выводам 2 и 8. Программа в цикле выводит через последовательный порт состояния сигналов A и B.

Если я плавно вращаю вал по часовой стрелке у меня монитор последовательного порта выводит:
HH -> LH -> LL -> HL -> HH

При вращении  против часовой стрелки:
HH -> HL -> LL -> LH -> HH

Все как на диаграмме выше.

Алгоритм обработки сигналов энкодера простой:

  • выделяем отрицательный фронт сигнала A;
  • считываем состояние сигнала B;
  • если у сигнала B:
    • высокий уровень – был поворот по часовой стрелке;
    • низкий  уровень – был поворот против часовой стрелки.

Но у механического энкодера механические контакты. Им свойственно явление дребезга. К тому же энкодеры часто устанавливаю на передней панели устройства, и подключают к контроллеру достаточно длинными проводами. Т.е. нужна цифровая фильтрация сигналов и устранение дребезга. Все это реализовано в моей библиотеке Button.h. Ее вполне можно использовать для работы с энкодером.

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

#include <TimerOne.h>
#include <Button.h>

Button encoderA (2, 4); // сигнал A
Button encoderB (8, 4); //  сигнал B
Button encoderButton(10, 40); // кнопка

long pos=0; // пооложение энкодера

void setup() {
  Serial.begin(9600); // инициализируем порт, скорость 9600
  Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
  Timer1.attachInterrupt(timerInterrupt, 250); // задаем обработчик прерываний
}

void loop() {
// сброс положения 
  if( encoderButton.flagClick == true ) {
    encoderButton.flagClick= false;
    pos= 0;
  }
  Serial.println(pos); // вывод положения
}

// обработчик прерывания 250 мкс
void timerInterrupt() {
  encoderA.filterAvarage(); // вызов метода фильтрации
  encoderB.filterAvarage(); // вызов метода фильтрации 
  encoderButton.filterAvarage(); // вызов метода фильтрации

// обработка сигналов энкодера
  if( encoderA.flagClick == true ) {
    encoderA.flagClick= false;
      if( encoderB.flagPress == true) {
        // против часовой стрелки
        pos--;
      }
      else {
        // по часовой стрелке
        pos++;
      }
  }
}

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

Вопрос, с какой частотой необходимо опрашивать энкодер. У меня в программе цикл прерывания 250 мкс и 4 выборки при фильтрации. В итоге период реакции на изменения состояния сигналов 1 мс.

Все зависит от того с какой частотой сигналы энкодера могут изменять свое состояние. А это определяют максимальная скорость вращения энкодера и параметр - число импульсов на оборот.

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

  • Запомнить механическое положение вала.
  • Сбросить вычисленное положение кнопкой (привязать к механическому положению).
  • Теперь сколько бы вы ни вращали вал, при попадании в механическое положение, к которому он был привязан, на экране должно быть значение кратное числу импульсов на оборот. В моем случае это 0,20, 40…

 

Библиотека Encod_er.h.

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

Энкодером перемещался шпиндель сверлильного станка. С одной стороны нужна была высокая точность позиционирования.  С другой – приходилось долго крутить ручку, чтобы переместить шпиндель в нижнее положение.

Решили при медленном вращении энкодера перемещать шпиндель по одному шагу, а при увеличении скорости вращения – увеличивать число шагов перемещения шпинделя на каждый шаг энкодера. Т.е. если медленно крутить энкодер, то шпиндель перемещается точно и медленно. При быстром вращении – шпиндель перемещается быстро.

Для реализации этого алгоритма необходимо вычислять скорость вращения энкодера. Я разработал библиотеку обработки энкодера с этой функцией.

Загрузить библиотеку Encod_er.h можно по этой ссылке:

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

 

Описание класса Encod_er.

Я привел только public свойства и методы.

class Encod_er {
  public:
    Encod_er(byte pinA, byte pinB, byte timeFilter);
    byte timeRight; // время/признак вращения вправо (* 8 периодов)
    byte timeLeft; // время/признак вращения влево (* 8 периодов)
    long position; // текущее положение 
    void scanState(); // метод проверки состояния
    long read(); // метод чтения положения 
};

Encod_er(byte pinA, byte pinB, byte timeFilter)  - конструктор.

  • pinA – вывод сигнала A;
  • pinB – вывод сигнала B;
  • timeFilter – число выборок фильтрации сигналов.

Encod_er myEncoder(2, 3, 5);   // энкодер к выводам 2 и 3, 5 выборок фильтрации

void  scanState() – метод сканирования состояния энкодера. Должен вызываться регулярно в параллельном процессе.

// обработчик прерывания 250 мкс
void timerInterrupt() {
  myEncoder.scanState();
}

byte timeRight и timeLeft – признаки поворота энкодера соответственно вправо и влево. Если равны 0, то поворота не было. Если не равны 0, то содержат время поворота на один шаг. Время вычисляется, как

Tповорота = Tвызова scanState()  * 8 * timeRight.

Т.е. единица значения timeRight или timeLeft это период вызова scanState(),  умноженный на 8. В моей программе это 250 мкс * 8 = 2 мс. На 8 я умножил, чтобы использовать один байт. Значение переменных  timeRight или timeLeft ограничиваются на уровне 255.

Если признаки используются, то должны сбрасываться  в основном цикле.

if(encoder.timeRight != 0) {
  encoder.timeRight= 0;
...........
}

long  position – текущее положение энкодера. Может быть прочитано или записано.

long  read() – метод чтения положения энкодера. Возвращает переменную position.

Все просто. Предыдущую программу можно переписать с использованием библиотеки Encod_er.h.

#include <TimerOne.h>
#include <Encod_er.h>

Encod_er encoder( 2, 8, 4);

void setup() {
  Serial.begin(9600); // инициализируем порт, скорость 9600
  Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
  Timer1.attachInterrupt(timerInterrupt, 250); // задаем обработчик прерываний
}

void loop() {
  if(encoder.timeRight != 0) {
    Serial.print("R=");
    Serial.print(encoder.timeRight);
    Serial.print(" Pos=");
    Serial.println(encoder.read()); // вывод текущего положения 
    encoder.timeRight= 0;
  }
  if(encoder.timeLeft != 0) {
    Serial.print("L=");
    Serial.print(encoder.timeLeft);
    Serial.print(" Pos=");
    Serial.println(encoder.read()); // вывод текущего положения
    encoder.timeLeft= 0;
  }
}

// обработчик прерывания 250 мкс
void timerInterrupt() {
  encoder.scanState();
}

На каждый поворот вала в монитор последовательного порта выводится направление вращение и значение timeRight или timeLeft.

Окно монитора порта

Обработку кнопки энкодера я в библиотеку не включил. Кнопку лучше обрабатывать библиотекой Button.h.

С помощью этой библиотеки я реализовал управление перемещением шпинделя сверлильного станка по описанному выше алгоритму.

 

Анализировал признаки timeRight и timeLeft. При ненулевом значении признаков шаговый двигатель перемещения шпинделя делал количество шагов в зависимости от значения активного признака. Зависимость задал в массиве.

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 16 часов

Эдуард

2
Комментарии: 1102Публикации: 122Регистрация: 13-12-2015

Один комментарий на «Урок 55. Работа с инкрементальным энкодером в Ардуино. Библиотека Encod_er.h.»

  1. Здравствуйте, мне нужно добавить в условие,
    if(encoder.read() = 4095)
    {??? = 4095;
    }
    как правильно записать в энкодер, что если меньше нуля то всегда равен нулю и если больше 4095, то равен 4095?
    Подскажите пожалуйста?

    0
    • Здравствуйте!

      noInterrupts();
      if(encoder.position < 0) encoder.position=0; if(encoder.position > 4095) encoder.position=4095;
      interrupts();

      У переменной position права public.
      x=encoder.read(); и x=encoder.position; одно и то же. Только прерывание надо запрещать, чтобы не считывать частично модифицированную переменную.

      0
  2. Здравствуйте!
    Очень интересные уроки. Все замечательно работает.
    Есть такой вопрос:
    Возможна ли одновременная корректная работа библиотек , из этого урока и высокочастотной ШИМ из Урока 37 (Широтно-импульсная модуляция в Ардуино)? Как лучше реализовать управление заполнением высокочастотной ШИМ с помощью енкодера и учетом скорости вращения?

    0
    • Конечно. ШИМ реализуется аппаратным способом. Программа не тратит на него время.
      Для управления двигателем надо делать регулятор. Измерять скорость вращения. Возможно вам не подойдет эта библиотека. При высоких скоростях вращения не хватит времени на обработку 2 сигналов энкодера. Вам достаточно обрабатывать один сигнал. Вы же знаете в какую сторону крутите двигатель. И энкодер должен быть другой, не механический, с большим ресурсом.

      0
  3. Добрый день. Нужно чтобы нажали кнопку, реле 1 замкнулось, как только будет n оборотов энкодера, реле выключилось. Как это можно реализовать?

    0
  4. Здравствуйте!
    У Вас очень хорошие уроки, спасибо Вам за труд!
    Подскажите пожалуйста, почему Вы решили делать прерывания по таймеру? Почему просто не привязать аппаратное прерывание к А с параметром FALLING?

    0
    • Здравствуйте!
      Спасибо за отзыв по сайту.
      Я стараюсь использовать внешние прерывания только в крайнем случае. Они обладают низкой помехозащищенностью, срабатывают на дребезг контактов, система становится асинхронной. К тому же я предполагаю, что программа выполняет параллельные задачи, которые требуют прерывания по таймеру.

      0
  5. Добрый день, пожалуйста приведите пример добавления произвольного таймера, в код. Пробую по этому примеру: http://arduino.ru/Reference/Millis
    В итоге компилятор ошибки выдаёт. Полагаю TimerOne не позволяет использовать эти средства. Как с его помощью реализовать тоже самое, что и в примере на ардуино.ру ?

    0
    • Здравствуйте!
      millis() возвращает время (в мс), с момента запуска контроллера. Для отработки временного интервала необходимо в начале отсчета считать функцией millis() текущее время. Затем периодически считывать время этой функцией, проверяя, не достигло ли оно требуемого значения (значение считанного в начале отсчета + требуемое время интервала). В промежутках между проверками текущего времени программа может заниматься другими задачами.
      Т.е. при использовании millis() вы должны все время проверять значение времени и реализовывать переход при выполнении условия. На проверку тратится определенное время. Кроме того точность отработки времени таймера будет зависеть и от частоты вызова функции millis().
      При использовании TimerOne программа перейдет на обработку прерывания аппаратными средствами без затрат программного времени.

      0
  6. Уважаемый,Эдуард, доброго вам времени суток!
    У вас очень интересные уроки на различные, актуальные темы.
    Большим плюсом, что они используют прерывания.
    Однако у меня, начинающего в этой области, возникли трудности со скрещиванием этой библиотеки (энкодер) со StepDirDriver.
    Никак не могу понять, как это правильно организовать.
    Еще одним камнем преткновения является то, что энкодер у меня круговой оптический с 1000 рисками.
    А задача следить за перемещением энкодера и делать соответствующее кол-во шагов в том же направлении.
    И вообще, возможно ли скрещивание этих двух библиотек малой кровью?
    Заранее,благодарен, с Уважением Андрей.

    0
    • Здравствуйте!
      Ничего скрещивать не надо. Библиотеки работают независимо друг от друга параллельными процессами. Надо в прерывании по таймеру вызывать методы control() и scanState(). Теперь можно пользоваться ресурсами библиотек.
      Только почитайте в уроке 55 о частоте вызова scanState(). Возможно у вас энкодер высокого разрешения и вращается с большой скоростью.
      Можете открыть тему на форуме сайта, там удобнее обсуждать.

      0
  7. Добрый день.
    Подскажите как реализовать проект:
    Необходимо определить угол наклона и вылет стрелы при помощи 2х энкодеров.
    При определенном угле ограничить перемещение стрелы.

    если угол больше снять ограничение

    Спасибо

    0
    • Здравствуйте!
      Это достаточно сложная задача. И, насколько я понимаю, такая система должна быть очень надежной.

      0
        • Мне стало очень интересно, кем вы являетесь на работе и кто вам разрешил (и знает ли об этом) что разработку такого узла как прибор БЕЗОПАСНОСТИ гидроподъемника вы делаете на ардуино, при околонулевом (судя по вопросам) уровне своих познаний.

          Если этот прибор будет использоваться не только лично вами, то это вообще-то преступление.

          И ардуино это не более чем детские кубики для обучения. Но делать на них что-то промышленного назначения — это полный звиздец.

          0
      • Так вроде все в порядке,
        Но при подключении серва прога реагирует только на него.
        Если убрать строку //servo1.attach(pinServo); то работает только энкодер.
        Поможете разобраться.
        Спасибо.

        #include
        #include
        #include
        #include
        int ledPin = 8; // the number of the LED pin
        int Pin_3 = 3;
        int Pin_4 = 6;
        Servo servo1;
        int pinServo=9; // Пин для подключения сервопривода
        int POT=0; // Аналоговый вход A0 для подключения потенциометра
        int valpot = 0; // переменная для хранения значения потенциометра
        int angleServo = 0; // переменная для хранения угла поворота серв

        Encod_er encoder( 2, 4, 5);
        Encod_er encoder_1( 5, 7, 5);

        void setup() {
        Serial.begin(9600); // инициализируем порт, скорость 9600
        Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
        Timer1.attachInterrupt(timerInterrupt, 250); // задаем обработчик прерываний
        pinMode(ledPin, OUTPUT);
        // initialize the pushbutton pin as an input:
        pinMode(Pin_3, OUTPUT);
        pinMode(Pin_4, OUTPUT);
        // подключить переменную servo к выводу pinServo
        servo1.attach(pinServo);

        }

        void loop() {

        valpot = analogRead(POT); // чтение данных потенциометра
        // масштабируем значение к интервалу 0-180
        angleServo=map(valpot,120,1240,0,230); //120,1240,0,230
        // поворот сервопривода на полученный угол
        servo1.write(angleServo);

        if(encoder.timeRight != 0) {
        Serial.print(«R=»);
        Serial.print(encoder.timeRight);
        Serial.print(» Ugol podyema ap=»);
        Serial.println(encoder.read()); // вывод текущего положения
        encoder.timeRight= 0;
        }
        if(encoder.timeLeft != 0) {
        Serial.print(«L=»);
        Serial.print(encoder.timeLeft);
        Serial.print(» Ugol podyema doun=»);
        Serial.println(encoder.read()); // вывод текущего положения
        encoder.timeLeft= 0;
        }
        if(encoder_1.timeRight != 0) {
        Serial.print(«\t R_1=»);
        Serial.print(encoder_1.timeRight);
        Serial.print(«\t vylet ap=»);
        Serial.println(encoder_1.read()); // вывод текущего положения
        encoder_1.timeRight= 0;
        }
        if(encoder_1.timeLeft != 0) {
        Serial.print(«\t L_1=»);
        Serial.print(encoder_1.timeLeft);
        Serial.print(«\t vylet doun =»);
        Serial.println(encoder_1.read()); // вывод текущего положения
        encoder_1.timeLeft= 0;
        }
        digitalWrite(Pin_3, LOW);

        digitalWrite(Pin_4, LOW);
        // Функция управления условиями
        if (encoder_1.read()>= 10&& encoder.read()>= 20) //Если encoder_1 >= 10&& encoder <= 10 то включаем реле
        {
        // turn LED on:
        digitalWrite(ledPin, HIGH);
        } else {
        // turn LED off:
        digitalWrite(ledPin, LOW);
        }

        }

        // обработчик прерывания 250 мкс
        void timerInterrupt() {
        encoder.scanState();
        encoder_1.scanState();

        }

        0
        • Здравствуйте!
          Может быть библиотека Servo использует таймер 1. Попробуйте сделать прерывание для энкодеров от таймера 2.

          0
          • Подскажите как это сделать???
            Спасибо.

            0
          • Здравствуйте!
            Библиотека для таймера 2 описана в уроке10.
            Подключаете библиотеку:
            #include

            Инициализируете в setup():
            MsTimer2::set(1, timerInterupt); // задаем период прерывания по таймеру 1 мс
            MsTimer2::start(); // разрешаем прерывание по таймеру

            Вызов обработчика происходит так:
            // обработчик прерывания
            void timerInterupt() {
            . . . .
            }

            Все также, как и с таймером 1. Только библиотека MsTimer2 устанавливает прерывание с дискретностью 1 мс, т.е. минимальное время 1 мс. Энкодер будет обрабатывать с меньшей скоростью. Попробуйте так, потом будем думать.

            0

  8. Здравствуйте, спасибо за уроки. Без Вас бы закопался в куче бесполезной информации.
    Прикручивая энкодер, с Вашими библиотеками натолкнулся на интересный момент: При повороте на 1/8(примерно) шага и возврате в прежнее положение, программа изменяет счетчик на 1. Работает в обе стороны.

    // обработка сигналов энкодера
    if( encoderA.flagClick == true ) {
    encoderA.flagClick= false;
    if( encoderB.flagPress == true) {
    // против часовой стрелки
    pos—;
    }
    else {
    // по часовой стрелке
    pos++;
    }
    }

    А вот с этим кодом, без таймера такого не происходит. Хотя, на быстрых скоростях он теряет шаги(отличие 4 шага на щелчек):

    old=val;
    val=(digitalRead(2)<<1)+digitalRead(8);
    if (old!=val)
    {
    if ((old==3)&&(val==1))++cur;
    if ((old==3)&&(val==2))—cur;
    if ((old==1)&&(val==0))++cur;
    if ((old==1)&&(val==3))—cur;
    if ((old==0)&&(val==2))++cur;
    if ((old==0)&&(val==1))—cur;
    if ((old==2)&&(val==3))++cur;
    if ((old==2)&&(val==0))—cur;
    printing=true;
    }

    0
    • Здравствуйте!
      Спасибо за отзыв.
      У меня самый простой и быстрый способ обработки сигналов энкодера. Тот недостаток, который вы обнаружили, во многих приложениях вряд ли играет какую-нибудь роль.

      0
      • Если я правильно разобрался в библиотеке Button, то в ней не реализован признак отжатия кнопки. Если бы он был, то легко бы этот недостаток исключался:

        // обработка сигналов энкодера
        if( encoderA.flagClick == true )
        { encoderA.flagClick= false;
        if( encoderB.flagPress == true) {pos—;} else {pos++;}
        };
        if( encoderA.flagUnClick == true )
        { encoderA.flagUnClick==false;
        if ( encoderB.flagPress == true) pos++ else pos—
        }

        0
  9. Здравствуйте, Эдуард
    Делаю обработку энкодера параллельным процессом на Arduino Pro Micro. Установил:
    #include
    #include

    Button encoderA (7, 40); // сигнал A
    Button encoderB (5, 40); // сигнал B
    Button encoderButton(2, 40); // кнопка

    Управляющие сигналы создаю электронным имитатором энкодера. Счет идет, но только на увеличение. В обратную сторону не вычитает, хотя меандры А и В поменялись местами, что подтверждается миганием светодиодов на выходах имитатора. Исследую схему на частотах около 1 Гц.

    void setup() {
    Serial.begin(9600); // инициализируем порт, скорость 9600
    pinMode(2, INPUT_PULLUP); // определяем вывод как вход
    pinMode(8, INPUT_PULLUP); // определяем вывод как вход
    }
    void loop() {
    if( digitalRead(2) == HIGH ) Serial.print(«H «);
    else Serial.print(«L «);
    if( digitalRead(8) == HIGH ) Serial.println(«H»);
    else Serial.println(«L»);
    delay(200);
    }
    Эта программа тоже видит на 5 и 7 входах HH-HL-LL-LH…

    0
    • Здравствуйте!
      А почему у вас такое большое число подтверждений сигналов энкодера 40? А если энкодер подключить, работает?

      0
  10. Что то я всю голову сломал. Новичок я во всем этом. Буду очень признателен если вы покажете код для управления биполярным шаговым двигателем при помощи энкодера. Мотор имеет 984 шага, вылет тяги 41 миллиметр т.е. один сигнал с энкодера крути движок 24 шага (или 1 мм) при этом нужно чтобы сделав 984 шага двигатель не крутился (т.е. ограничить его от 0 шагов до 984). Заранее благодарен…

    0
    • Здравствуйте!
      Вот проект управления сверлильным станком http://mypractic-forum.ru/viewtopic.php?t=66. Там перемещение шпинделя происходит энкодером. Но программа сложная. У меня другой нет.

      0
      • Доброго вам времени суток. Спасибо вам за ваши труды. Вопрос такой у меня возник… при вращении энкодера показания в порт появляются только со второго щелчка, а как сделать так чтобы обрабатывалось каждое изменение сигнала как в этом коде

        #include «Stepper.h»
        #define STEPS 24 // Number of steps for one revolution of Internal shaft
        // 2048 steps for one revolution of External shaft

        volatile boolean TurnDetected; // need volatile for Interrupts
        volatile boolean rotationdirection; // CW or CCW rotation

        const int PinCLK=2; // Generating interrupts using CLK signal
        const int PinDT=3; // Reading DT signal
        const int PinSW=4; // Reading Push Button switch

        int RotaryPosition=0; // To store Stepper Motor Position

        int PrevPosition; // Previous Rotary position Value to check accuracy
        int StepsToTake; // How much to move Stepper

        // Setup of proper sequencing for Motor Driver Pins
        // In1, In2, In3, In4 in the sequence 1-3-2-4
        Stepper small_stepper(STEPS, 8, 10, 9, 11);

        // Interrupt routine runs if CLK goes from HIGH to LOW
        void isr () {
        delay(10); // delay for Debouncing
        if (digitalRead(PinCLK))
        rotationdirection= digitalRead(PinDT);
        else
        rotationdirection= !digitalRead(PinDT);
        TurnDetected = true;
        }

        void setup () {
        Serial.begin(9600);
        pinMode(PinCLK,INPUT);
        pinMode(PinDT,INPUT);
        pinMode(PinSW,INPUT);
        digitalWrite(PinSW, HIGH); // Pull-Up resistor for switch
        attachInterrupt (0,isr,CHANGE); // interrupt 0 always connected to pin 2
        }

        void loop () {
        small_stepper.setSpeed(830); //Max seems to be 700
        if (!(digitalRead(PinSW))) { // check if button is pressed
        if (RotaryPosition == 0) { // check if button was already pressed
        } else {
        small_stepper.step(-(RotaryPosition*24));
        RotaryPosition=0; // Reset position to ZERO
        }
        }

        // Runs if rotation was detected
        if (TurnDetected) {
        PrevPosition = RotaryPosition; // Save previous position in variable
        if (rotationdirection) {
        RotaryPosition=RotaryPosition-1;} // decrase Position by 1
        else {
        RotaryPosition=RotaryPosition+1;} // increase Position by 1

        TurnDetected = false; // do NOT repeat IF loop until new rotation detected

        // Which direction to move Stepper motor
        if ((PrevPosition + 1) == RotaryPosition) { // Move motor CW
        StepsToTake=24;
        small_stepper.step(StepsToTake);
        }

        if ((RotaryPosition + 1) == PrevPosition) { // Move motor CCW
        StepsToTake=-24;
        small_stepper.step(StepsToTake);
        }
        }
        Serial.println(RotaryPosition*24);
        }

        0
        • Здравствуйте!
          Моя библиотека рассчитана больше для работы с энкодером для задания параметров, замены кнопок, обеспечения интерфейса пользователя. Она написана с минимальным размером кода, минимальными требованиями к ресурсам микроконтроллера.
          Чтобы реализовать ваш алгоритм надо писать другую библиотеку.

          0
  11. Доброго вам времени суток. Спасибо вам за ваши труды. Вопрос такой у меня возник… при вращении энкодера показания в порт появляются только со второго щелчка, а как сделать так чтобы обрабатывалось каждое изменение сигнала как в этом коде

    #include «Stepper.h»
    #define STEPS 24 // Number of steps for one revolution of Internal shaft
    // 2048 steps for one revolution of External shaft

    volatile boolean TurnDetected; // need volatile for Interrupts
    volatile boolean rotationdirection; // CW or CCW rotation

    const int PinCLK=2; // Generating interrupts using CLK signal
    const int PinDT=3; // Reading DT signal
    const int PinSW=4; // Reading Push Button switch

    int RotaryPosition=0; // To store Stepper Motor Position

    int PrevPosition; // Previous Rotary position Value to check accuracy
    int StepsToTake; // How much to move Stepper

    // Setup of proper sequencing for Motor Driver Pins
    // In1, In2, In3, In4 in the sequence 1-3-2-4
    Stepper small_stepper(STEPS, 8, 10, 9, 11);

    // Interrupt routine runs if CLK goes from HIGH to LOW
    void isr () {
    delay(10); // delay for Debouncing
    if (digitalRead(PinCLK))
    rotationdirection= digitalRead(PinDT);
    else
    rotationdirection= !digitalRead(PinDT);
    TurnDetected = true;
    }

    void setup () {
    Serial.begin(9600);
    pinMode(PinCLK,INPUT);
    pinMode(PinDT,INPUT);
    pinMode(PinSW,INPUT);
    digitalWrite(PinSW, HIGH); // Pull-Up resistor for switch
    attachInterrupt (0,isr,CHANGE); // interrupt 0 always connected to pin 2
    }

    void loop () {
    small_stepper.setSpeed(830); //Max seems to be 700
    if (!(digitalRead(PinSW))) { // check if button is pressed
    if (RotaryPosition == 0) { // check if button was already pressed
    } else {
    small_stepper.step(-(RotaryPosition*24));
    RotaryPosition=0; // Reset position to ZERO
    }
    }

    // Runs if rotation was detected
    if (TurnDetected) {
    PrevPosition = RotaryPosition; // Save previous position in variable
    if (rotationdirection) {
    RotaryPosition=RotaryPosition-1;} // decrase Position by 1
    else {
    RotaryPosition=RotaryPosition+1;} // increase Position by 1

    TurnDetected = false; // do NOT repeat IF loop until new rotation detected

    // Which direction to move Stepper motor
    if ((PrevPosition + 1) == RotaryPosition) { // Move motor CW
    StepsToTake=24;
    small_stepper.step(StepsToTake);
    }

    if ((RotaryPosition + 1) == PrevPosition) { // Move motor CCW
    StepsToTake=-24;
    small_stepper.step(StepsToTake);
    }
    }
    Serial.println(RotaryPosition*24);
    }

    0
  12. Здравствуйте! Отличная статья и отличная библиотека. Но вот незадача, у меня помимо энкодера используется сервомотор. И из за прерываний сервомотор не может нормально прочитать сигнал с ШИМа. Есть ли способ на время остановить считывание энкодера, а потом его возобновить.

    0
    • Прошу прощения, не заметил комментарий другого человека с такой же проблемой. Ваше решение помогло, спасибо

      0
  13. Добрый день!
    Мне именно Ваш пример подходит.
    Большое спасибо за пример.
    Но не могу реализовать следующее:
    нужно управлять обычным мотором с редуктором в зависимости от интенсивности поворота энкодера хотелось увидеть увеличение скорости мотора, а у меня он постоянно крутится то влево, то вправо

    0
  14. Добрый день, перепробовал много разных скетчей, но все почему то не работали, а этот работает! Но подскажите, можно ли с этим скетчем обрабатывать 2 и более энкодеров?
    Пробовал немного подкорректировать код для обработки 2-х энкодеров, но получается, что второй корректно работает, а первый обрабатывается сам и к значению второго тоже прибавляет по единице, не зависимо в какую сторону крутить ручку первого энкодера.

    добавил код
    Button encoder2A (4, 4); // энкодер 2 сигнал A
    Button encoder2B (12, 4); // энкодер 2 сигнал B

    и в обработчик прерывания

    encoder2A.filterAvarage(); // вызов метода фильтрации
    encoder2B.filterAvarage(); // вызов метода фильтрации

    if( encoder2A.flagClick == true ) {
    encoder2A.flagClick= false;
    if( encoder2B.flagPress == true) {
    // против часовой стрелки
    pos2—;
    }
    else {
    // по часовой стрелке
    pos2++;
    }
    }

    Что-то делаю не так или может такими простыми изменениями не получится?

    0
    • Здравствуйте!
      Библиотека должна работать с несколькими энкодерами. Создавайте для них объекты и обрабатывайте признаки. Что не работает?

      0
      • Спасибо что откликнулись…
        «Создавайте для них объекты»
        вот это оно?:
        Button encoder2A (4, 4); // энкодер 2 сигнал A
        Button encoder2B (12, 4); // энкодер 2 сигнал B

        «и обрабатывайте признаки»
        вот это оно?:
        в обработчик прерывания

        encoder2A.filterAvarage(); // вызов метода фильтрации
        encoder2B.filterAvarage(); // вызов метода фильтрации

        if( encoder2A.flagClick == true ) {
        encoder2A.flagClick= false;
        if( encoder2B.flagPress == true) {
        // против часовой стрелки
        pos2—;
        }
        else {
        // по часовой стрелке
        pos2++;
        }
        }

        если «оно», то не корректно работает… если в обработчике прерывания оставить только мой новый код, то второй энкодер работает корректно, но вот если оба проверять, то не корректно.

        0
          • Добрый день… у меня нет таких объектов, я использую вторую версию кода, которая начинается с

            #include
            #include

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

            0
          • что-то названия инклудов не вставились
            #include
            #include

            0
          • В общем попробовал по третьему коду где используется Encod_er — проблема таже — второй корректно работает, а первый обрабатывается сам и к значению второго тоже прибавляет по единице, не зависимо в какую сторону крутить ручку первого энкодера.
            Если закоментить код первого энкодера — то второй корректно работает.

            0
          • Ладно… нашел другой скетч, который работает с несколькими энкодерами.

            0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *