Некорректные данные энкодера

Тема в разделе "Arduino & Shields", создана пользователем enclouder, 5 дек 2017.

  1. Onkel

    Onkel Гуру

    Это нужно даташит вкуривать, а он на английском... Но можно и свой написать, потратив денек - используя установку регистров для прерываний по ногам, отличным от int0 и int1, у меня тут 2 энкодера, подвешены на ноги A0-A3 (PC0-PC3)
    // here we config the interraption for encoder services
    EICRA=0x00; // INT0 INT1 Type didn't matter
    EIMSK=0x00; // INT0 INT1 disabled
    PCMSK1=0x09; // Pin change mask pins ENABLE any change
    PCIFR =0x02; //PCINT Flag clear
    PCICR =0x02; //Pins C0 - C5 interruptions enable

    выставить режимы пинов для энкодеров
    pinMode(A0,INPUT_PULLUP);
    pinMode(A1,INPUT_PULLUP);
    pinMode(A2,INPUT_PULLUP);
    pinMode(A3,INPUT_PULLUP);


    и юзать сишную функцию обработки прерывания
    ISR(PCINT1_vect) // External interruption for Encoder service
    {
    }
     
    arkadyf нравится это.
  2. enclouder

    enclouder Нерд

    Спасибо большое!
    Но что писать в сишной функции?
    В последних скобках пишем стандартный код сбора информации с энкодеров?
     
  3. Onkel

    Onkel Гуру

    прерывание вызывается при любом изменении состояния пинов С0 и С3. В функции прерывания пишем ( в простейшем случае) - примерно так
    если С0 стал ноль был 1 -
    {если С1 ==0
    то +1 к счетчику
    else
    -1 к счетчику
    }

    и аналогично для 2 энкодера (с пинами C2 и С3)
    B ввести переменную для предыдущего состояния пинов, лучше тоже регистром
    OldPINC=PINC;
    по этой команде все пины C пишутся в переменную OldPINC
     
  4. brokly

    brokly Гик

    Не мучайтесь. Вот по этой ссылке: https://github.com/PaulStoffregen/Encoder , берете файлы : Encoder.cpp, Encoder.h и папку Utility. Кидаете их в корень папки с вашим скетчем.

    Вот пример как использовать три энкодера. Можно и больше, из примера все понятно.

    Код (C++):
    #include "Encoder.h"

    Encoder enc0(5, 6); // подключаем энкодеры
    Encoder enc1(7, 8); // важно, что бы ножки имели функцию INT или PCINT
    Encoder enc2(9, 10);

    long pos0 = 0; // тут будем хранить считаные положения энкодеров
    long pos1 = 0;
    long pos2 = 0;

    void setup() {
      Serial.begin(9600);
      Serial.println("Three Encoders Test:");
    }

    void loop() {
      long oldPos0, oldPos1, oldPos2;
      pos0 = enc0.read(); // читаем текущее положение энкодеров
      pos1 = enc1.read();
      pos2 = enc2.read();

      if (oldPos0 != pos0 || oldPos1 != pos1 || oldPos2 != pos2) { //печать изменения положения энкодеров
        Serial.print("Encoder 0 = ");
        Serial.println(pos0);
        Serial.print("Encoder 1 = ");
        Serial.println(pos1);
        Serial.print("Encoder 2 = ");
        Serial.println(pos2);

        Serial.print("Encoder 0 "); // определение направления вращения нулевого энкодера
        if (oldPos0>pos0){
           Serial.println("turned left");
        } else if (oldPos0<pos0) {
           Serial.println("turned right");
        } else {
           Serial.println("stopped");
        }

        oldPos0 = pos0;
        oldPos1 = pos1;
        oldPos2 = pos2;

      }

      if (Serial.available()) {  // для ресета энкодеров нажми букву 'R'
        if (Serial.read()=='R') {
           Serial.println("Reset encoders to zero");
           enc0.write(0); // установка отсчета энкодеров в 0
           enc1.write(0);
           enc2.write(0);
        }
      }

    }
     
    Всего две нужные вам функции write - установка значения энкодера, нужно для сброса в ноль или еще какого то не благого дела ибо это действие сбивает предыдущий счет энкодера. И read - считать показания. Энкодеры сядут на прерывания, если ноги ардуино поддерживают прерывания.
     
    Последнее редактирование: 11 дек 2017
    enclouder нравится это.
  5. b707

    b707 Гуру

    Два энкодера, три энкодера.... Зачем столько? Энкодер - это средства навигации по меню взамен мышки. Вы когда-нибудь встречали компьютер, к которому было бы подключено три мышки? - и вряд ли встретите, хотя технически никаких проблем подключить кучу мышек нет... Ответ-то прост - никто не сможет эффективно работать тремя мышками одновременно.
    Точно так же и с энкодером - и технически проще, и чисто человечески удобнее повесить функции всех трех энкодеров на один физический девайс....
     
  6. brokly

    brokly Гик

    Энкодер это средство контроля за вращением :) Двигатель тоже вертится. Шасси на четырех независимых приводах, без поворота колес. Ширше нужно мыслить :)
    Кстати, вы знаете как устроены датчики АБС на автомобиле ;)
    А еще есть сервоприводы и станки на их основе.
     
  7. Onkel

    Onkel Гуру

    энкодер- подсчет числа сделанных оборотов (или долей оборота) моторов. Если у вас есть машина с правым и левым мотором и нужно ехать прямо или даже не прямо - то подсчет числа сделанных оборотов позволит корректировать движение.
     
  8. b707

    b707 Гуру

    точно... об этом как-то не подумал.
    Как устроены датчики АБС - знаю :)
     
  9. enclouder

    enclouder Нерд

    Спасибо вам большое за подробное объяснение! Однако... не помог ваш код)
    При прокрутке энкодеров начинается зацикливание, serial порт постоянно забивается данными, будто энкодер крутится без продыху, а иногда и нормально, не понятно. Но если добавить задержку - то результат как и раньше, неверные данные. Нашел решение на другом ресурсе, может кому поможет: http://mypractic.ru/urok-55-rabota-s-inkrementalnym-enkoderom-v-arduino-biblioteka-encod_er-h.html
    Код (C++):
    #include <TimerOne.h>
    #include <Encod_er.h>

    Encod_er encoder( 14, 15, 4);
    Encod_er encoder2( 16, 17, 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;
      }
     
     
      if(encoder2.timeRight != 0) {
        Serial.print("R=");
        Serial.print(encoder2.timeRight);
        Serial.print(" Pos=");
        Serial.println(encoder2.read()); // вывод текущего положения
        encoder2.timeRight= 0;
      }
      if(encoder2.timeLeft != 0) {
        Serial.print("L=");
        Serial.print(encoder2.timeLeft);
        Serial.print(" Pos=");
        Serial.println(encoder2.read()); // вывод текущего положения
        encoder2.timeLeft= 0;
      }
     
      delay(200);
    }

    // обработчик прерывания 250 мкс
    void timerInterrupt() {
      encoder.scanState();
      encoder2.scanState();
    }
    На oled дисплее не тестил, но главный показатель что с delay работает, значит и с дисплеем наверняка проблем не будет.

    Всем спасибо большое за обсуждение и помощь!
     
  10. Onkel

    Onkel Гуру

    А что за энкодер? В хреновых энкодерах, по 100 рублей , такой дребезг что может что угодно забить. Поставьте 0,2 uF между GND и пинами энкодера. Если энкодер фирменный, то там конечно дребезга нет.
    Да, и зачем Вам постоянно выводить на serial? Выводите не чаще раза в 10 мс, например. И если у Вас был дребезг, то устранение дребезга конденсатором поможет. А так, по хорошему, если собираетесь и дальше работать с микроконтроллерами, то конечно лучше вкурить даташит и написать функцию обработки прерываний по пинам самому. И еще по одной причине лучше писать самому ф-ю обработки энкодера- будет ли работать библиотека энкодера с дисплеем - не факт. Примененная Вами библиотека использует таймер 1, если библиотека работы с дисплеем тоже использует таймер 1, может быть конфликт. Ну и пара выводов ~ перестанет работать, я не помню какие из них связаны с таймером 1.
     
  11. brokly

    brokly Гик

    Что то неправильно подключили и нужно смотреть весь код. Но если вопрос решен, то забейте.
     
  12. enclouder

    enclouder Нерд

    подключил все правильно, а код скопировал ваш, за исключением 3го энкодера - его удалил, т.к. у меня всего их два. А так же подключил ножки ардуино нано 14, 15 на один и 16, 17 на второй энкодер.

    С другим кодом с прерываниями, который я указал, все заработало. Засим вывод, что у вас в коде все таки что то не верно, иначе почему же в остальных заработало?
     
  13. brokly

    brokly Гик

    Странный вы делаете вывод сравнивая один код с другим :) Вывод тут другой, у вас что то не так :)
     
  14. enclouder

    enclouder Нерд

    Извиняюсь, если грубо показалось)
    Но что у меня может быть не так? Взял ваш код, изменил только номера ножек:
    Код (C++):
    Encoder enc0(14, 15); // подключаем энкодеры
    Encoder enc1(16, 17); // важно, что бы ножки имели функцию INT или PCINT
    и удалил из списка объявлений 3 энкодер, все! Влево вправо работает, но иногда зацикливается. С другими примерами из стандартной библиотеки энкодера не зацикливался, но допускаю что это дребезг контактов. А вот то что с delay() не работает у вас так же, как и в обычном примере - это да.
     
  15. brokly

    brokly Гик

    Повторюсь, без полного кода обсуждение этого вопроса не имеет смысла.
     
  16. enclouder

    enclouder Нерд

    Вот:
    Код (C++):
    #include "Encoder.h"

    Encoder enc0(14, 15); // подключаем энкодеры
    Encoder enc1(16, 17); // важно, что бы ножки имели функцию INT или PCINT

    long pos0 = 0; // тут будем хранить считаные положения энкодеров
    long pos1 = 0;

    void setup() {
      Serial.begin(9600);
      Serial.println("Three Encoders Test:");
    }

    void loop() {
      long oldPos0, oldPos1, oldPos2;
      pos0 = enc0.read(); // читаем текущее положение энкодеров
      pos1 = enc1.read();

      if (oldPos0 != pos0 || oldPos1 != pos1) { //печать изменения положения энкодеров
        Serial.print("Encoder 0 = ");
        Serial.println(pos0);
        Serial.print("Encoder 1 = ");
        Serial.println(pos1);

        Serial.print("Encoder 0 "); // определение направления вращения нулевого энкодера
        if (oldPos0>pos0){
           Serial.println("turned left");
        } else if (oldPos0<pos0) {
           Serial.println("turned right");
        } else {
           Serial.println("stopped");
        }

        oldPos0 = pos0;
        oldPos1 = pos1;
      }

      if (Serial.available()) {  // для ресета энкодеров нажми букву 'R'
        if (Serial.read()=='R') {
           Serial.println("Reset encoders to zero");
           enc0.write(0); // установка отсчета энкодеров в 0
           enc1.write(0);
        }
      }

    delay(200);
    }
    При добавлении строки delay(200) энкодер один из 100 раз только срабатывает, с горем пополам. Что один, что второй. Закомментируем строку с задержкой и (если опустить момент зацикливания) все начинает без проблем работать.

    Что может быть у меня не так? Код, как вы видите, идентичный, без прерываний скет стандартный от билиотеки с энкодером тоже вполне себе годно работает. И так, что может быть не так?
     
  17. brokly

    brokly Гик

    Да, действительно не работает на 328. И ошибка даже не в этой библиотеке а утилитах которые она использует. Не корректно обрабатываются прерывания PCINT.
     
  18. akl

    akl Гуру

    по идее, в ардуине есть настроенное по умолчанию прерывание по таймеру 0, которое срабатывает вроде как каждую миллисекунду (при 16 мгц по крайней мере). можно прямо туда запихнуть алгоритм опроса энкодера. Это будет в 4 раза реже, но зато таймер1 освободится.
    но лучше конечно с PCINT разобраться.
     
  19. AlexU

    AlexU Гуру

    Представленная Вами библиотека вообще ни как не использует прерывания PCINT, она умеет работать только с INTx.
     
  20. brokly

    brokly Гик

    Да, к сожалению, это так.

    Но из кода видно, что можно использовать до 60 прерываний. Метод обработки прописан в утилитах. Посмотрел. Можно допилить. Но это уже другая история.
     
    Последнее редактирование: 12 дек 2017