Динамическая индикация

Тема в разделе "Arduino & Shields", создана пользователем Denis1307, 5 фев 2024.

  1. Denis1307

    Denis1307 Нуб

    Доброго времени суток.
    Помогите решить проблему динамической индикации семисегментного индикатора.
    Есть вот такой код для ардуино (термометр):
    Код (C++):
    #include <OneWire.h>
    #include <DallasTemperature.h>

    int segment[] = {2,3,4,5,6,7,8,9};
    #define SEG1 10
    #define SEG2 11
    #define SEG3 12

    #define ONE_WIRE_BUS 13 // контакт 13 на Arduino:
    OneWire oneWire(ONE_WIRE_BUS); // создаем экземпляр класса OneWire, чтобы с его помощью общаться с однопроводным устройством
    DallasTemperature sensors(&oneWire); // передаем объект oneWire объекту sensors:

    byte numbers[11] = {
    0b0111111, //0
    0b0000110, //1
    0b1011011, //2
    0b1001111, //3
    0b1100110, //4
    0b1101101, //5
    0b1111101, //6
    0b0000111, //7
    0b1111111, //8
    0b1101111, //9
    0b1100011, //знак градуса
    };

    unsigned long timer;
    int temp;

    void setup()
    {
      for (int i = 0; i <= 8; i++) {
        pinMode(segment[i],OUTPUT);
      }
      pinMode(SEG1,OUTPUT);
      pinMode(SEG2,OUTPUT);
      pinMode(SEG3,OUTPUT);

      sensors.begin(); // запускаем библиотеку:
      sensors.setResolution(12); //установка разрешения датчика
    }

    void loop()
    {
      if (millis() - timer > 3000)
      {
        timer = millis();
        sensors.requestTemperatures();
        temp = sensors.getTempCByIndex(0);
        temp = round(temp);
      }

      int C1 = temp%10;
      int C2 = temp/10;
     
      digitalWrite(SEG1,LOW);
      digitalWrite(SEG2,HIGH);
      digitalWrite(SEG3,HIGH);
      num(C2); //первый разряд
      delay(5);

      digitalWrite(SEG1,HIGH);
      digitalWrite(SEG2,LOW);
      digitalWrite(SEG3,HIGH);
      num(C1); //второй разряд
      delay(5);

      digitalWrite(SEG1,HIGH);
      digitalWrite(SEG2,HIGH);
      digitalWrite(SEG3,LOW);
      num(10); //третий разряд
      delay(5);
    }

    void num(int number)
    {
      boolean enable = 0;
      number = numbers[number];
      for (int i = 0; i <= 8; i++)
      {
        enable = bitRead(number, i - 2);
        digitalWrite(i, enable);
      }
    }
    Как организовать перебор катодов индикатора с помощью цикла (в функции void num), чтобы на нем корректно отображались показания датчика.
    Просьба тапками сильно не кидать, в ардуино новичок.
    Заранее всем спасибо.
     
    Последнее редактирование: 6 фев 2024
  2. Denis1307

    Denis1307 Нуб

    Методом проб и ошибок пришел вот к такому решению:
    Код (C++):
    #include <OneWire.h> //подключение библиотеки OneWire
    #include <DallasTemperature.h> //подключение библиотеки DallasTemperature

    int segment[] = {2,3,4,5,6,7,8,9}; //пины сегментов
    int digits[] = {10,11,12}; //пины разрядов

    #define ONE_WIRE_BUS 13 // контакт подключения датчика к Arduino:
    OneWire oneWire(ONE_WIRE_BUS); // создаем экземпляр класса OneWire, чтобы с его помощью общаться с однопроводным устройством
    DallasTemperature sensors(&oneWire); // передаем объект oneWire объекту sensors:

    byte numbers[11] = { //битовый массив отображаемых чисел и знаков
    0b0111111, //0
    0b0000110, //1
    0b1011011, //2
    0b1001111, //3
    0b1100110, //4
    0b1101101, //5
    0b1111101, //6
    0b0000111, //7
    0b1111111, //8
    0b1101111, //9
    0b1100011, //знак градуса
    };

    unsigned long timer; //переменнаят хранения значения таймера
    int temp; //переменная хранения значения температуры

    void setup() {
      for (int i = 0; i <= 8; i++) { //установка режима работы пинов сегментов
        pinMode(segment[i],OUTPUT);
      }
      for (int i = 0; i <= 3; i++) { //установка режима работы пинов разрядов
        pinMode(digits[i],OUTPUT);
      }

      sensors.begin(); // запускаем библиотеку работы с датчиком
      sensors.setResolution(9); //установка разрешения датчика
    }

    void loop() {
      if (millis() - timer > 3000) {
        timer = millis();
        sensors.requestTemperatures(); //получение значения температуры от датчика
        temp = sensors.getTempCByIndex(0); //
        temp = round(temp); //округление значения температуры до целого числа
      }

      int C1 = temp/10; // значение десятков градусов, целочисленное деление на 10
      int C2 = temp%10; // значение едениц градусов, остаток от деления на 10

      setDigit(0); //выбор первого разряда
      num(C1); //отоброжаемое значение в первом разряде
      delay(5);

      setDigit(1); //выбор второго разряда
      num(C2); //отоброжаемое значение во втором разряде
      delay(5);

      setDigit(2); //выбор третьего разряда
      num(10); //отоброжаемое значение в третьем разряде
      delay(5);
    }

    void num(int number) { //функция отображения значения
      boolean enable = 0;
      number = numbers[number];
      for (int i = 0; i <= 8; i++) {
        enable = bitRead(number, i - 2);
        digitalWrite(i, enable);
      }
    }

    void setDigit(int raz) { //функция выбора разряда
      switch(raz)
      {
        case 0:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
        case 1:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
            break;
        case 2:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
      }
    }
    Решение может быть и не идеальное, но в виду того всем откровенно говоря пофигу на вопросы от новичков, лучшее что смог придумать.
    Может быть кому-нибудь пригодиться.
    Всем большое спасибо за помощь и советы.
     
    Последнее редактирование: 7 фев 2024
  3. b707

    b707 Гуру

    Не запинается на время чтения температуры?

    Вот эти две строчки лучше поменять местами
    Код (C++):
     sensors.requestTemperatures(); //получение значения температуры от датчика
        temp = sensors.getTempCByIndex(0); //
     
    Denis1307 нравится это.
  4. Denis1307

    Denis1307 Нуб

    Запинается. Раз в три секунды, на индикаторе моргает значение температуры.
    Большое спасибо за совет, сейчас попробую.
     
  5. b707

    b707 Гуру

    попробовал?
     
  6. Denis1307

    Denis1307 Нуб

    Да. Стало получше, но все равно при опросе датчика индикатор моргает.
     
  7. Ariadna-on-Line

    Ariadna-on-Line Гуру

    OneWire - по принципу схемотехники своих устройств - протокол с жесткими требованиями к временнЫм задержкам (таймингам). А динамическая индикация - никаких таймингов не требует абсолютно. Их "требуют" только наши глаза. Поскольку OneWire не очень быстрый протокол - всегда "страдать" будет динамическая индикация - в виде миганий. Смиритесь. Зато это здорово упрощает суммарную схемотехнику устройства.
    Желательно оптимизировать код. Операции - digitalWrite(), digitalRead() -- медленные и громоздкие по размерам кода. Используйте работу с выходным регистром порта. Типа portN = b00001000. Но об этом Филин лучше знает.
     
    Последнее редактирование: 9 фев 2024
    Denis1307 нравится это.
  8. parovoZZ

    parovoZZ Гуру

    Такие вещи делаются на конечном автомате.
     
    KindMan нравится это.
  9. KindMan

    KindMan Гуру

    Да, согласен с паравозом. Делал часы с динамикой, там тоже пришлось 18b20 воткнуть. Разбиваете запрос температуры на шаги, тогда не будет заметно мигание. Я ещё замерял общее время исполнения функций и расчёт задержки для индикации менял исходя из этих данных.
     
    Denis1307 нравится это.
  10. Ariadna-on-Line

    Ariadna-on-Line Гуру

    #define PortRazr portB
    #define PortKart portD

    int massRazr[4] ; // массив для 4х номеров картинок цифр
    const int massPins[4] ; // массив для 4х значений регистра пинов разрядов
    const int massKart[10] ; // массив для 10ти картинок цифр (пинов регистра сегментов цифр)

    loop() {

    ... massRazr[0] = A; massRazr[1] = B; ........ massRazr[3] = D; // получили с датчика 4 цифры. Это по сути номера картинок цифр.

    for (int j =0; j<1000; j++) { //повторяется многократно
    for (int i=o; i<4; i++) { // процедура перебора разрядов
    PortB = massPins[ i ] ; // Выбрали один из 4х пинов разрядов
    PortD = massKart[ massRazr[ i ] ] ; // Вывели картинку цифры в этом разряде
    }
    }
    }
    Вот и вся динамика. По-моему как-то так
     
    Последнее редактирование: 9 фев 2024
  11. parovoZZ

    parovoZZ Гуру

    и для чего этот код???
    Известно, что после 40 Гц глаз человека мало восприимчив к мерцанию. Соответственно, период обновления картинки не должен превышать 20 мс. У нас 4 разряда, поэтому каждые 5 мс нам надо поджигать следующий разряд. Т.к. индикация у нас в приоритете, то мы просто обязаны укладываться в эти временные рамки. Но сколько это займёт времени? Допустим, на смену индикации потребуется 20 тактов. При тактовой частоте 16Мгц, это действие займёт 1.25 мкс. Если МК разогнать до 20 МГц, то время ещё сократиться.
    Даже учитывая, что
    всегда можно найти временное окно, когда можно "вывалиться" из опроса датчика и обновить индикацию.
    Ну и очень сильно сомневаюсь, что у OneWire такие жёсткие временные ограничения. При первом приближении наверняка окажется, что он такой же расхлябанный, как чип 2811 для светодиодов.
     
  12. KindMan

    KindMan Гуру

    Вот нашел свою тему там есть ссылка на гитхаб с кодом. Посмотрите функцию GetTermo.
     
  13. Denis1307

    Denis1307 Нуб

    Всем большое спасибо за советы и подсказки. Буду со всем разбираться.
     
  14. b707

    b707 Гуру

    так там вроде всего два шага - запрос на измерение и , спустя заданное время - получение данных. У ТС этот процесс и так разбит на два
     
  15. b707

    b707 Гуру

    Ариадна, сорри, но вы же не программист ни разу... - и кода вы не понимаете... к чему вы лезете спорить с профи?

    Конкретно по коду - ваш код блокирующий и может работать только если на МК больше ничего нет, кроме индикации. Такой код может н6аписать любой новичок и показывать тут нечего. Сложность именно в том, как совместить динамическую индикацию с другими процессами в коде.
     
    DetSimen нравится это.
  16. KindMan

    KindMan Гуру

    Только функция sensors.requestTemperatures() в его случае тормозит всю программу, пока не выдаст результат.
     
    Ariadna-on-Line нравится это.
  17. parovoZZ

    parovoZZ Гуру

    Если уж совсем в лоб, то надо снизить точность датчика до самого низу. Сколько там? 9 или 10 бит? Смысла в цифрах после запятой нет никакого - у него точность больше градуса. Но зато выдавать результат будет очень быстро.
     
  18. Ariadna-on-Line

    Ariadna-on-Line Гуру

    1. Блокирующий здесь - sensors.requestTemperatures().
    2. Профи еще не предложили ничего, чтобы спорить. Где вы видите спор ?))))))
     
    Последнее редактирование: 9 фев 2024
  19. Denis1307

    Denis1307 Нуб

    Доработал код своего термометра. Отказался от использования библиотеки DallasTemperature, что позволило убрать моргание индикатора во время опроса датчика. И использовал сдвиговый регистр, освободил цифровые пины на плате, можно будет еще что-нибудь прикрутить.

    Теперь код выглядит так.
    Код (C++):
    #include <OneWire.h> //подключение библиотеки OneWire

    int digits[] = {10,11,12,13}; //пины разрядов
    int data = 7; //пин входа данных
    int latch = 8; //пин защелки
    int clock = 9; //пин тактовых импульсов

    OneWire ds(2);

    byte numbers[14] = { //битовый массив отображаемых чисел и знаков
    0b0111111, //0
    0b0000110, //1
    0b1011011, //2
    0b1001111, //3
    0b1100110, //4
    0b1101101, //5
    0b1111101, //6
    0b0000111, //7
    0b1111111, //8
    0b1101111, //9
    0b1100011, //знак градуса
    0b0111001, //знак цельсия
    };

    unsigned long timer; //переменнаят хранения значения таймера
    int temp; //переменная хранения значения температуры

    void setup()
    {
      Serial.begin(9600);

      pinMode(data, OUTPUT);
      pinMode(latch, OUTPUT);
      pinMode(clock, OUTPUT);
      for (int i = 0; i <= 4; i++) { //установка режима работы пинов разрядов
        pinMode(digits[i],OUTPUT);
      }
    }

    void loop()
    {
      if (millis() - timer >= 1000)
      {
        timer = millis();
        ds.reset(); //сброс предыдущих данных
        ds.write(0xCC); //пропуск поиска других датчиков, если подключен только один датчик
        ds.write(0x44); //команда датчику на измерение температуры
      }

      ds.reset(); //сброс предыдущих данных
      ds.write(0xCC); //пропуск поиска других датчиков, если подключен только один датчик
      ds.write(0xBE); //команда датчику передать измеренную температуру

      temp = ds.read(); //читаем первый байт переданный датчиком и записываем его в переменную temp
      temp = (ds.read()<<8) | temp; //читаем второй байт и сдвигаем влево все его биты, чтобы освободить место для первого байта и объединяем их
      temp = (temp)>>4; //сдвиг в право на 4, для получения только целой части измеренной температуры
     
      int C1 = temp/10; // значение десятков градусов, целочисленное деление на 10
      int C2 = temp%10; // значение едениц градусов, остаток от деления на 10

      setDigit(0); //выбор первого разряда
      num(C1); //отоброжаемое значение в первом разряде
      delay(4);

      setDigit(1); //выбор второго разряда
      num(C2); //отоброжаемое значение во втором разряде
      delay(4);

      setDigit(2); //выбор третьего разряда
      num(10); //отоброжаемое значение в третьем разряде
      delay(4);

      setDigit(3); //выбор четвертого разряда
      num(11); //отоброжаемое значение в третьем разряде
      delay(4);
    }

    void num(int number) //функция отображения значения
    {
      digitalWrite(latch, LOW);
      shiftOut(data, clock, MSBFIRST, numbers[number]);
      digitalWrite(latch, HIGH);
    }
    void setDigit(int raz) //функция выбора разряда
    {
      switch(raz)
      {
        case 0:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
        case 1:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
        case 2:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
        case 3:
          for (int i = 0; i <=3; i++) {
            digitalWrite(digits[i], i == raz ? LOW : HIGH);
          }
          break;
      }
    }
     
  20. KindMan

    KindMan Гуру

    Рекомендую добавить флаг на измерение и чтение, например так: bool action;
    И меняете код
    Код (C++):

    if (millis() - timer >= 1000)
      {
        timer = millis();
    if (! action) {
        ds.reset(); //сброс предыдущих данных
        ds.write(0xCC); //пропуск поиска других датчиков, если подключен только один датчик
        ds.write(0x44); //команда датчику на измерение температуры
    action=true;}
    else{

      ds.reset(); //сброс предыдущих данных
      ds.write(0xCC); //пропуск поиска других датчиков, если подключен только один датчик
      ds.write(0xBE); //команда датчику передать измеренную температуру

      temp = ds.read(); //читаем первый байт переданный датчиком и записываем его в переменную temp
      temp = (ds.read()<<8) | temp; //читаем второй байт и сдвигаем влево все его биты, чтобы освободить место для первого байта и объединяем их
      temp = (temp)>>4; //сдвиг в право на 4, для получения только целой части измеренной температуры
    action=false;
    }
    За первый проход вы отдаете команду на измерение, за второй чтение.