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

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

  1. enclouder

    enclouder Нерд

    Доброго дня.
    Использую обычный код энкодера как сэмп к библиотеке и все ок:
    Код (C++):
    #include <Encoder.h>
    // Change these two numbers to the pins connected to your encoder.
    //   Best Performance: both pins have interrupt capability
    //   Good Performance: only the first pin has interrupt capability
    //   Low Performance:  neither pin has interrupt capability
    Encoder myEnc(5, 6); // avoid using pins with LEDs attached
    void setup() {
      Serial.begin(9600);
      Serial.println("Basic Encoder Test:");
    }
    long oldPosition  = -999;
    void loop() {
      long newPosition = myEnc.read();
      if (newPosition != oldPosition) {
        oldPosition = newPosition;
        Serial.println(newPosition);
      }
    }
    стоит только добавить задержку в доли секунд, энкодер сразу же показывает значения 0 -1 0 -1 0 -1 или же
    вообще не точно, например: 20 21 19 18 21 23 22
    Код (C++):
    void loop() {
      long newPosition = myEnc.read();
      if (newPosition != oldPosition) {
        oldPosition = newPosition;
        Serial.println(newPosition);
      delay(200); //ЗАДЕРЖКА
      }
    Пробовал с прерываниями, все равно такой же результат. Любая задержка в коде дает неверный результат энкодера на выходе, памагити!
     
  2. SergeiL

    SergeiL Оракул Модератор

    Как работает encoder:

    encoder.png

    Определяется изменение сигнала на одном из двух пинов по отношению к другому.
    По этим данным определяется направление вращения.

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

    Если в loop появляется delay(200); как в вашем примере, то проверка состояния пинов начинает производится интервалом в 200 миллисекунд.

    Если состояние пинов изменится несколько раз между опросами, этого никто не узнает, и вращение энкодера будет интерпретировано некорректно.
     
    arkadyf нравится это.
  3. akl

    akl Гуру

    а сами пины 5 и 6 могут прерывания?
     
  4. enclouder

    enclouder Нерд

    Да я вроде использовал код с прерыванием, только не помню какой и где. Но точно с attachinterrupt.
    Тоесть у нас вывод только один: использовать прерывания и да, я использовал неверный код с прерываниями?
     
  5. akl

    akl Гуру

    или не к тем пинам. на разных платах разные пины с прерываниями, на атмега328 например только 2 и 3
     
  6. enclouder

    enclouder Нерд

    тоесть на атмеге328 я что, смогу "безболезненно" подключить только один энкодер?)
    мне по условия надо 3 энкодера, подскажите, как можно организовать их применение с задержками в коде ?
     
  7. DetSimen

    DetSimen Guest

    mf2 и arkadyf нравится это.
  8. DetSimen

    DetSimen Guest

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

    статья
    http://we.easyelectronics.ru/part/i2c-rasshiritel-portov-pcf8574.html

    апчитай её всю.
     
    brokly, arkadyf и Tomasina нравится это.
  9. enclouder

    enclouder Нерд

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

    DetSimen Guest

    ах, да. еще и библиотеку для связки расширителя портов + 3 енкодера самому писать придёца. Но это же мелочи....
     
  11. enclouder

    enclouder Нерд

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

    Так вот, как использовать энкодер с задержками по коду?
     
  12. DetSimen

    DetSimen Guest

    ну это не ко мне. Щас оракулы наскачут.
     
  13. SergeiL

    SergeiL Оракул Модератор

    А зачем Вам задержки в коде?

    delay() - это по сути приостановка основного процесса на время.

    На 100% уверен, что любая программа может обойтись без delay().

    Запоминайте значение millis(), потом из текущего millis() вычитаете запомненное и проверяете результат. Если больше или равно требуемому интервалу - выполняете действие.

    На форуме это не раз уже обсуждали, поищите, да и примеры есть, например стандартный «BlinkWithoutDelay»:
    Код (C++):
    /*
      Blink without Delay

      Turns on and off a light emitting diode (LED) connected to a digital pin,
      without using the delay() function. This means that other code can run at the
      same time without being interrupted by the LED code.

      The circuit:
      - Use the onboard LED.
      - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
        and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
        is set to the correct LED pin independent of which board is used.
        If you want to know what pin the on-board LED is connected to on your
        Arduino model, check the Technical Specs of your board at:
        https://www.arduino.cc/en/Main/Products

      created 2005
      by David A. Mellis
      modified 8 Feb 2010
      by Paul Stoffregen
      modified 11 Nov 2013
      by Scott Fitzgerald
      modified 9 Jan 2017
      by Arturo Guadalupi

      This example code is in the public domain.

      http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
    */


    // constants won't change. Used here to set a pin number:
    const int ledPin =  LED_BUILTIN;// the number of the LED pin

    // Variables will change:
    int ledState = LOW;             // ledState used to set the LED

    // Generally, you should use "unsigned long" for variables that hold time
    // The value will quickly become too large for an int to store
    unsigned long previousMillis = 0;        // will store last time LED was updated

    // constants won't change:
    const long interval = 1000;           // interval at which to blink (milliseconds)

    void setup() {
      // set the digital pin as output:
      pinMode(ledPin, OUTPUT);
    }

    void loop() {
      // here is where you'd put code that needs to be running all the time.

      // check to see if it's time to blink the LED; that is, if the difference
      // between the current time and last time you blinked the LED is bigger than
      // the interval at which you want to blink the LED.
      unsigned long currentMillis = millis();

      if (currentMillis - previousMillis >= interval) {
        // save the last time you blinked the LED
        previousMillis = currentMillis;

        // if the LED is off turn it on and vice-versa:
        if (ledState == LOW) {
          ledState = HIGH;
        } else {
          ledState = LOW;
        }

        // set the LED with the ledState of the variable:
        digitalWrite(ledPin, ledState);
      }
    }
     
    arkadyf нравится это.
  14. akl

    akl Гуру

    как совсем быдло-вариант можно заменить делей вот такой штукой
    Код (C++):
    timer=millis();
    while(millis()-timer<period){
    newPosition = myEnc.read();            //а сюда напихать опросы энкодеров
    }
    но это плохой способ.
    и вообще скорее всего без прерываний будет плохо работать если программа будет делать много что кроме энкодеров и мигания лампочкой.
    Можно заточить отдельную микросхему с большим количеством прерываний (типа аттини2313), которая будет считать энкодеры, а с нее по какому-нибудь уарту только забирать результат позиции. но это сложно.
     
  15. AlexU

    AlexU Гуру

    Все цифровые входы ATmega328 поддерживают аппаратные прерывания. Энкодеров сможете подключить "безболезненно" столько, на сколько хватит входов микроконтроллера.
     
  16. DIYMan

    DIYMan Guest

    Убрать delay, и всё взлетит. Если не хотите убирать delay, сделайте так:
    Код (C++):
    void encoderRead()
    {
      long newPosition = myEnc.read();
     
      if (newPosition != oldPosition)
      {
        oldPosition = newPosition;
        Serial.println(newPosition);
      }
    }

    void yield()
    {
      encoderRead();
    }

    void loop()
    {
      encoderRead();
      delay(200); //ЗАДЕРЖКА
    }
     
    Как видите, в коде есть delay, но теперь энкодер будет опрашиваться без задержки.
     
    arkadyf нравится это.
  17. akl

    akl Гуру

    а точно, там же есть PCINT
    позор мне:oops:
    но их надо настроить, и выходит что ардуиновая библиотека этого не умеет чтоль, раз у автора не получилось?
     
    arkadyf нравится это.
  18. AlexU

    AlexU Гуру

    "Стандартная" ардуиновская библиотека умеет только INT0 и INT1. И что-то мне подсказывает, что написать свой обработчик будет гораздо быстрее, чем искать готовое решение.
     
    arkadyf нравится это.
  19. enclouder

    enclouder Нерд

    ну вот же! спасибо всем откликнувшимся! очень классные, полезные и нужные ответы.
    Однако, поясню зачем задержка: она в коде как таковая то и не проявляется, но при использовании oled i2c дисплея, в библиотеке она где то все равно походу присутствует, так как результат вывода значений энкодера на дисплей такой же, как в сериал порт, такие же выходные значения. Опытным путем было выявлено что изза задержки такое появляется, поэтому и вопрос был такой.
    Соответственно, как можно использовать oled i2c дисплей вместе с энкодерами, учитывая что в библиотеке есть задержки
     
  20. brokly

    brokly Гик

    Повесить энкодеры на прерывания.

    Вот библиотека, там даже есть пример с несколькими энкодерами:

    https://www.pjrc.com/teensy/td_libs_Encoder.html
    Ну или найдите ее же на гитхабе.
     
    arkadyf нравится это.