Вывод данных на сегментный индикатор

Тема в разделе "Arduino & Shields", создана пользователем vvr, 12 ноя 2016.

  1. vvr

    vvr Инженерище

    считываю обороты с оптического энкодера и вывожу данные на 8 разрядный семисегментный индикатор.
    для работы с индикатором использую библиотеку LedControl.
    есть контрольный прибор с таким же энкодером.
    всё хорошо.

    ввожу корректировочный коэффициент в виде числа float.
    считываю импульсы, складываю в число (long), потом умножаю на коэф и полученный результат раскладываю для вывода на индикатор

    если коэф сразу прописываю то счёт идёт корректно

    Код (C++):
    #include "LedControl.h"
    LedControl lc = LedControl(11, 13, 10, 1);

    int encoderPin1 = 2;
    int encoderPin2 = 3;
    volatile long lastEncoded = 0;
    volatile long encoderValue = 0;
    long lastencoderValue = 0;
    long lastMSB = 0;
    long lastLSB = 0;
    long val;
    long val_old = 0;



    long distance = 0;
    long DIST = 0;
    float koef = 0.9210;

    int n1 = 0;
    int n2 = 0;
    int n3 = 0;
    long n4 = 0;
    long n5 = 0;

    int a1 = 0;
    int a2 = 0;
    int a3 = 0;
    long a4 = 0;
    long a5 = 0;

    void setup()
    {
      lc.shutdown(0, false);  
      lc.setIntensity(0, 8);  
      lc.clearDisplay(0);

      pinMode(encoderPin1, INPUT);
      pinMode(encoderPin2, INPUT);
      digitalWrite(encoderPin1, HIGH);
      digitalWrite(encoderPin2, HIGH);
      attachInterrupt(0, updateEncoder, CHANGE);
      attachInterrupt(1, updateEncoder, CHANGE);
    }

    void loop()
    {
      val = encoderValue / 4;
      if (val > val_old)
      {
        n1 ++;
        if (n1 > 9)
        {
          n1 = 0;
          n2 ++;
        }
        if (n2 > 9)
        {
          n2 = 0;
          n3 ++;
        }
        if (n3 > 9)
        {
          n3 = 0;
          n4 ++;
        }
        if (n4 > 9)
        {
          n4 = 0;
          n5 ++;
        }
        if (n5 > 9)
        {
          n5 = 0;
        }
        val_old = val;
      }

      if (val < val_old)
      {
        if ( distance > 0)
        {
          n1 --;
          if (n1 < 0)
          {
            n1 = 9;
            n2 --;
          }
          if (n2 < 0)
          {
            n2 = 9;
            n3 --;
          }
          if (n3 < 0)
          {
            n3 = 9;
            n4 --;
          }
          if (n4 < 0)
          {
            n4 = 9;
            n5 --;
          }
        }
        val_old = val;
      }

      distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000;
      DIST = distance * koef;

      a1 = DIST % 10;
      a2 = (DIST / 10) % 10;
      a3 = (DIST / 100) % 10;
      a4 = (DIST / 1000) % 10;
      a5 = (DIST / 10000) % 10;

    if (DIST < 1000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setChar(0, 4, ' ', false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 1000 && DIST < 10000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 10000 && DIST < 100000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setDigit(0, 5, a5, false);
      }
    }

    ////////////////  енкодер ////////////////

    void updateEncoder()
    {
      int MSB = digitalRead(encoderPin1);
      int LSB = digitalRead(encoderPin2);

      int encoded = (MSB << 1) | LSB;
      int sum  = (lastEncoded << 2) | encoded;

      if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011)
        encoderValue ++;
      if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000)
        encoderValue --;

      lastEncoded = encoded;
    }
    если кнопкой переключаю режимы с разными коэф то получается погрешность да 3 процентов
    может с типами чисел где то ошибка...
    буду признателен за совет.

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

    LedControl lc = LedControl(11, 13, 10, 1);
    OneButton button(9, true);

    int encoderPin1 = 2;
    int encoderPin2 = 3;
    volatile long lastEncoded = 0;
    volatile long encoderValue = 0;
    long lastencoderValue = 0;
    long lastMSB = 0;
    long lastLSB = 0;
    long val;
    long val_old = 0;

    long distance = 0;
    long DIST = 0;
    float koef;

    int n1 = 0;
    int n2 = 0;
    int n3 = 0;
    long n4 = 0;
    long n5 = 0;

    int a1 = 0;
    int a2 = 0;
    int a3 = 0;
    long a4 = 0;
    long a5 = 0;

    int state = 0;

    void setup()
    {
      lc.shutdown(0, false);  
      lc.setIntensity(0, 8);  
      lc.clearDisplay(0);

      pinMode(encoderPin1, INPUT);
      pinMode(encoderPin2, INPUT);
      digitalWrite(encoderPin1, HIGH);
      digitalWrite(encoderPin2, HIGH);
      attachInterrupt(0, updateEncoder, CHANGE);
      attachInterrupt(1, updateEncoder, CHANGE);

      button.attachClick(Click);
    }

    void loop()
    {
      button.tick();

      val = encoderValue / 4;

      if (val > val_old)
      {
        n1 ++;
        if (n1 > 9)
        {
          n1 = 0;
          n2 ++;
        }
        if (n2 > 9)
        {
          n2 = 0;
          n3 ++;
        }
        if (n3 > 9)
        {
          n3 = 0;
          n4 ++;
        }
        if (n4 > 9)
        {
          n4 = 0;
          n5 ++;
        }
        if (n5 > 9)
        {
          n5 = 0;
        }
        val_old = val;
      }

      if (val < val_old)
      {
        if ( distance > 0)
        {
          n1 --;
          if (n1 < 0)
          {
            n1 = 9;
            n2 --;
          }
          if (n2 < 0)
          {
            n2 = 9;
            n3 --;
          }
          if (n3 < 0)
          {
            n3 = 9;
            n4 --;
          }
          if (n4 < 0)
          {
            n4 = 9;
            n5 --;
          }
        }
        val_old = val;
      }

      ///////////////////////////////

      if (state == 0)
        koef = 0.9210;
      else if (state == 1)
        koef = 1.0000;

      lc.setDigit(0, 7, state, false);


      ///////////////////////////////////////


      distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000;

      DIST = distance * koef;

      a1 = DIST % 10;
      a2 = (DIST / 10) % 10;
      a3 = (DIST / 100) % 10;
      a4 = (DIST / 1000) % 10;
      a5 = (DIST / 10000) % 10;

      /////////////////////////////////////////////////////////////////


      if (DIST < 1000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setChar(0, 4, ' ', false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 1000 && DIST < 10000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 10000 && DIST < 100000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setDigit(0, 5, a5, false);
      }
    }

    ////////////////  енкодер ////////////////

    void updateEncoder()
    {
      int MSB = digitalRead(encoderPin1);
      int LSB = digitalRead(encoderPin2);

      int encoded = (MSB << 1) | LSB;
      int sum  = (lastEncoded << 2) | encoded;

      if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011)
        encoderValue ++;
      if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000)
        encoderValue --;

      lastEncoded = encoded;
    }

    void Click()
    {
      state = 1 - state;
    }
     
     
  2. Limoney

    Limoney Гик

    А замените long DIST на float DIST
     
  3. vvr

    vvr Инженерище

    попробовал float DIST
    ничего не поменялось..
    вал энкодера вращается со скоростью 300 - 500 об/мин
    при вращении рукой всегда полное совпадение с контрольным счётчиком
    при вращении двигателем совпадение только в первом варианте
     
  4. ostrov

    ostrov Гуру

    Странный способ подсчета шагов. По мне, так если использовать аппаратное прерывание, то достаточно посмотреть состояние второй ноги чтобы сделать вывод увеличился счетчик или нет. Или это накручено для борьбы с дребезгом? Я вот для энкодера с этой целью юзаю прерывание по таймеру (каждые 250 мкс), считываю состояние первой ноги в кольцевой буфер, через него нахожу изменение по маске 110 (или 001, что пофиг), тут же считываю состояние второй ноги и делаю вывод. Работает всегда со 100% точностью, кроме случаев когда энкодер врет аппаратно, такое иногда бывает при смене направлений, сбивается на 1 шаг изредка. Функция вызываемая по прерыванию:
    Код (C++):
    void encRead() {
      p[0] = p[1];
      p[1] = p[2];
      p[2] = digitalRead(P1);
      if (p[0] && p[1] && !p[2]) {
        digitalRead(P2) ? Step++ : Step--;
      }
    }
    Это для STM32, у него digitalWrite и так очень быстрый, не стал заморачиваться с портами.
     
  5. vvr

    vvr Инженерище

    меня используемый код работы с энкодером полностью устраивает)))
    бьюсь с математикой...
     
  6. ostrov

    ostrov Гуру

    Вы старый дед что ли? ))) Это их если что то устраивает, то не будут улучшать даже под страхом смерти. Надо всегда пробовать новые способы, особенно если они эффективнее и менее глючны. Я привел свой способ который раз в несколько короче и практически никогда не сбоит, при этом не реагирует на дребезг. Почему бы не попробовать? Если нужно код для запуска прерыванию по таймеру, то могу написать и его, какой у вас МК?
     
  7. vvr

    vvr Инженерище

    да уж не молодой))
    вы мой пост хоть прочитали
    существующий код работает если сразу прописан коэффициент.
    если коэффициентом играться в лупе - погрешности
    меня это интересует.
    какое это имеет отношение к методу опроса энкодера
     
  8. ostrov

    ostrov Гуру

    Не утерпел, уж простите. Код опроса показался громоздким и неоправданно сложным, вот и не выдержала душа поэта. ))
     
  9. ostrov

    ostrov Гуру

    Может причина в разных типах данных? У вас там умножение float на long и тп. попробуйте одинаковые типы сделать или привести принудительно (float)...
     
  10. vvr

    vvr Инженерище

    Limoney уже советовал
    попробовал кучу вариантов, в том числе и принудительное приведение - всё равно погрешность есть
    а если сразу прописать - нет, всё гуд))))
     
  11. ostrov

    ostrov Гуру

    Где то округляет значит, где не просят. Хотя, казалось бы разницы никакой. Может быть тут иногда статус болтается?
    Код (C++):
      if (state == 0)
        koef = 0.9210;
      else if (state == 1)
        koef = 1.0000;
    Кстати, опять много написано, так короче:
    Код (C++):
      if (!state)
        koef = 0.9210;
      else
        koef = 1.0000;
    А еще короче так:
    Код (C++):
        koef = state ? 1.0000 : 0.9210;
     
  12. vvr

    vvr Инженерище

    как у чайника в программировании знаний в этом вопросе не хватает))))
    хотя koef объявлен как float - ничего при присвоении значения не должно вроде происходить
     
  13. ostrov

    ostrov Гуру

    Сдается мне что проблема в этом:
    Код (C++):
      button.attachClick(Click);
    В упор не вижу антидребезга. То есть состояние state может меняться не так как вам надо, а хаотично, то 0 то 1, смотря как искры сверкнут. Попробуйте или аппаратно добавить RC цепь на кнопку или программно загасить дребезг кнопки.
     
  14. vvr

    vvr Инженерище

    кнопка обрабатывается с помощью библиотеки OneButton
    там с дребезгом все нормально.
     
  15. ostrov

    ostrov Гуру

    Недоверяю я слепо разным библиотекам. Тем более что кнопки разные бывают, одни 10мс дребезжат, другие 100+. Или она тестирует кнопку предварительно?
     
  16. vvr

    vvr Инженерище

    скажу более
    без кнопки если прописать

    Код (C++):
    if (state == 0)
    {
        koef = 0.9210;

      distance = n1 + n2 * 10 + n3 * 100 + n4 * 1000 + n5 * 10000;

      DIST = distance * koef;

      a1 = DIST % 10;
      a2 = (DIST / 10) % 10;
      a3 = (DIST / 100) % 10;
      a4 = (DIST / 1000) % 10;
      a5 = (DIST / 10000) % 10;

      /////////////////////////////////////////////////////////////////


      if (DIST < 1000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setChar(0, 4, ' ', false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 1000 && DIST < 10000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setChar(0, 5, ' ', false);
      }
      if (DIST >= 10000 && DIST < 100000)
      {
        lc.setDigit(0, 1, a1, false);
        lc.setDigit(0, 2, a2, false);
        lc.setDigit(0, 3, a3, true);
        lc.setDigit(0, 4, a4, false);
        lc.setDigit(0, 5, a5, false);
      }
    }
    тоже фигня получается
     
  17. ostrov

    ostrov Гуру

    0.9210 умноженное на число менее 1000 и заносимое в long округляется до целого. Вы уверены, что distance всегда больше 1000?
     
  18. vvr

    vvr Инженерище

    distance меняется от 0 до 99999
     
  19. ostrov

    ostrov Гуру

    Вот в этом и проблема.
     
  20. vvr

    vvr Инженерище

    вы так и не прочитали мой первоначальный пост

    если коэффициент прописан при объявлении переменных - всё считается правильно (первый код)
    если начинаю менять его в цикле начинаются проблемы(второй)