DS18B20 не правильно показывает температуру

Тема в разделе "Arduino & Shields", создана пользователем Dany11, 17 авг 2019.

  1. Доброго времени суток. Хочу вывести температуру с датчика ds18b20 пока в виде целого числа на 4 разр. семисегментный индикатор. Часть кода с датчиком взял с готового проекта на atmega8. Причина в том, что в этом коде не используется больших задержек, а я еще буду добавлять часы реального времени на ds1307, не хочу чтобы время сбивалось.
    upload_2019-8-28_15-56-36.png
    Но температуру не всегда отображает правильную, Вот на скриншоте с симулятора наглядно видно.
    upload_2019-8-28_15-58-39.png

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

    Код прилагаю
    Код (C++):

    #define BUFFER_SIZE 20
    #define STATE_OFF  0
    #define STATE_ON   1

    #define DS_BIT   0 // датчик подключен к порту А0

    const uint8_t latchPin = 8;//Пин подключен к ST_CP входу 74HC595
    const uint8_t clockPin = 9;//Пин подключен к SH_CP входу 74HC595
    const uint8_t dataPin = 10;//Пин подключен к DS входу 74HC595

    const uint8_t digitPins[4] = {
    4,5,6,7}; // Пины для управления 4 разрядами семисегментного индикатора ОА
    // 4 -> 4 разряд
    // 5 -> 3 разряд
    // 6 -> 2 разряд
    // 7 -> 1 разряд
    // Массив значениий для семисегментного индикатора
    const byte digit[11] =
    {
      B00111111, //0
      B00000110, //1
      B01011011, //2
      B01001111, //3
      B01100110, //4
      B01101101, //5
      B01111101, //6
      B00000111, //7
      B01111111, //8
      B01101111,  //9
      B01000000  //minus
    };

    uint8_t digitBuffer[4] = {0};
    uint8_t digitScan = 0;
    volatile unsigned char soft_scaler = 0;

    unsigned char zamer = 0;
    volatile char temp_flag = 1, Temp_H, Temp_L, OK_Flag = 0;
    uint8_t tmp;

    ISR(TIMER1_COMPA_vect) { // Обработчик прерывания таймера 1
      switch (zamer)
      {
        case 0:
          if(OK_Flag == 0)
          {
            DS18B20_init();
            write_18b20(0xCC);     // проверка кода датчика
            write_18b20(0x44);     // запуск температурного преобразования
          }
        break;

        case 1:
          DS18B20_init();
          write_18b20(0xCC);     // проверка кода датчика
          write_18b20(0xBE);     // считываем содержимое ОЗУ
          Temp_L = read_18b20(); // читаем первые 2 байта блокнота
          Temp_H = read_18b20();

          if(Temp_H &(1 << 3))   // проверяем бит знака температуры на равенство единице
          {
            int tmp;
            temp_flag = 0;      // флаг знака равен 0(минус)
            tmp = (Temp_H << 8) | Temp_L;
            tmp = -tmp;
            Temp_L = tmp;
            Temp_H = tmp >> 8;
          }
          else
          {
            temp_flag = 1;      // флаг знака равен 0(минус)
          }
        break;
      }
      if ((zamer++) > 1) zamer = 0;
    }


    ISR(TIMER2_OVF_vect) { // Обработчик прерывания по переполнению таймера 2
      soft_scaler++;
      if(soft_scaler==15)
      {
        refreshDisplay();
        soft_scaler = 0;
      }
    };

    static void timer1_init(void) {
      // Инициализация таймер 1
      // Настройка таймера на 1 с на частоте 16 МГц
      TCCR1A = 0;
      TCCR1B = 0;
      TCNT1 = 0;
      OCR1A = 15624; // регистр совпадения (от 0 до 65535)
      //15624 = (16*10^6)/(1*1024)-1 (<65536)
      TCCR1B |= (1<<WGM12); // режим СТС (сброс по совпадению)
      TCCR1B |= (1<<CS12) | (1<<CS10); // предделитель 1024
      TIMSK1 = (1<<OCIE1A); // разрешить прерывание по совпадению
    }

    static void timer2_init(void) {
      // Настройка таймера
      // Период срабатывания таймера при F_CPU = 16 MHz
      // T = 1/(16000000/8/256) = 128 мкс        
      TCCR2A = 0;
      TCCR2B = (1<<CS21);  // Предделитель на 8
      TIMSK2 = (1<<TOIE2); // Разрешение прерывания по переполнению таймера 2
      TCNT2 = 0;           // сбрасываем счетный регистр
    }

    /*** Инициализация DS18B20 ***/
    unsigned char DS18B20_init(void)
    {
      PORTC &= ~(1 << DS_BIT); // устанавливаем низкий уровень
      DDRC |= (1 << DS_BIT);
      _delay_us(490);
      DDRC &= ~(1 << DS_BIT);
      _delay_us(68);
      OK_Flag = (PINC & (1 << DS_BIT)); // ловим импульс присутствия датчика
      // если OK_Flag = 0 датчик подключен, OK_Flag = 1 датчик не подключен
      _delay_us(422);
      return OK_Flag;
    }

    /*** Функция чтения байта из DS18B20 ***/
    unsigned char read_18b20(void)
    {
      unsigned char i,dat = 0;
      for(i = 0;i < 8;i++)
      {
        DDRC |= (1 << DS_BIT);
        _delay_us(2);  
        DDRC &= ~(1 << DS_BIT);
        _delay_us(4);  
        dat = dat >> 1; // Следующий бит  
        if(PINC & (1 << DS_BIT)) dat |= 0x80;
        _delay_us(62);
      }
      return dat;
    }

    /*** функция записи байта в DS18B20 ***/
    void write_18b20(uint8_t dat)
    {
      unsigned char i;
      for(i = 0;i < 8;i++)
      {
        DDRC |= (1 << DS_BIT);
        _delay_us(2);    
        if ((dat & 0x01) == 1) DDRC &= ~(1 << DS_BIT);
        else DDRC |= (1 << DS_BIT);
        dat = dat >> 1; // Следующий бит
        _delay_us(62);
        DDRC &= ~(1 << DS_BIT);
        _delay_us(2);
      }
    }

    void refreshDisplay()
    {
      byte pattern;
      // гасим все сегменты
      for(uint8_t k=0; k<4; k++)
      {
        digitalWrite(digitPins[k], LOW);
      }
      // байт данных для вывода
      pattern = digit[digitBuffer[digitScan]];
      // устанавливаем лог. 0 на latchPin, пока не окончена передача байта данных
      digitalWrite(latchPin, LOW);

      // обновляем байт данных для вывода из регистра на дисплей
      // поскольку это дисплей с общим анодом шаблон необходимо инвертировать
      shiftOut(dataPin, clockPin, MSBFIRST, ~pattern);

      // защелкиваем регистр, чтобы данные появились на его выходах
      digitalWrite(latchPin, HIGH);
      digitalWrite(digitPins[digitScan], HIGH);

      // проходим по разрядам
      digitScan++;
      if(digitScan >= 4) digitScan = 0;
    }

    void setup()   {
      for(uint8_t i=0; i<4; i++)
      {
        pinMode(digitPins[i],OUTPUT);
      }
      pinMode(latchPin, OUTPUT);
      pinMode(clockPin, OUTPUT);
      pinMode(dataPin, OUTPUT);
      timer1_init();
      timer2_init(); // Инициализация таймера 2
      sei(); // Включить глобальные прерывания
    }

    void loop()                
    {
      unsigned int tempint,tempint1,tempint2,tempint3; // переменные для целого значения температуры
      unsigned int temppoint,temppoint1; // переменные для дробного значения температуры

      // Если датчик не ответил выводим "минус" во всех разрядах                
      /*if(OK_Flag) {
        for (uint8_t i =0; i <= 3; i++) {
           digitBuffer[0] = 10;
        }
      }*/


      tempint = ((Temp_H << 4) & 0x70)|(Temp_L >> 4); // вычисляем целое значение температуры
      tempint1 = tempint % 1000 / 100; // сотни
      tempint2 = tempint % 100 / 10; // десятки
      tempint3 = tempint % 10;      // еденицы
      //temppoint = Temp_L & 0x0F; // вычисляем дробное значение температуры
      //temppoint = temppoint * 625;       // точность температуры
      //temppoint1 = temppoint / 1000;  

      // выводим значения на дисплей
      digitBuffer[0] = tempint3;
      digitBuffer[1] = tempint2;
      digitBuffer[2] = tempint1;
      digitBuffer[3] = 0;
    }
     
     
    Последнее редактирование: 27 авг 2019
  2. b707

    b707 Гуру

    Aleksander1997 - как и в прошлой теме - вы не написали, в чем. собственно проблема.
     
  3. Некоторые значения соответствуют тем, что я задал. Но начиная со значения температуры 8 оС на индикаторе уже ерунда. Вывожу я только целую часть. Перед этим разбиваю число на еденицы, десятки и сотни и по цифре вывожу на каждый сегмент.
     
  4. b707

    b707 Гуру

    для начала вставьте вывод температуры в Сериал перед выводом его на индикатор - так вы сможете понять, в какой части программы ошибка - в работе с датчиком DS18b20 или в выводе на сегменты

    Ну и еще - дело, конечно, ваше - но вы вязли слишком сложный код для повторения. Зачем вам низкоуровневый доступ к датчику через регистры. если библиотека OneWire делает это в одно действие? - проще код - меньше ошибок
     
    Andrey12 нравится это.
  5. Да, спасибо. Буду выводить в Сериал и проверять
     
  6. Пока сделал так, в Сериал выводит температуру. Буду добавлять индикатор
    Код (C++):
    #include <avr/wdt.h> /***/

    #define DS18B20_PIN   10

    int raw_temp;
    float temp;

    ISR (WDT_vect) {/***/

    }

    ISR(TIMER1_COMPA_vect) {
      // Обработчик прерывания таймера 1
      if(ds18b20_read(&raw_temp)) {
        temp = (float)raw_temp / 16;     // (темп. в °C = raw/16)
        Serial.print("Temperature = ");
        Serial.print(temp);              // Напечатать значение температуры в градусах Цельсия
        Serial.println(" C");            // Напечатать символ '°C'
      }
      else {
        Serial.println("Communication Error!");
      }
    }

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

      /***/
      cli(); // отключить глобальные прерывания
      WDTCSR = (1 << WDCE)|(1 << WDE);
      WDTCSR = (1 << WDIE)|(1 << WDP2)|(1 << WDP1); // разрешение прерывания, выдержка 1 секунда

      // Инициализация таймер 1
      // Настройка таймера на 1 с на частоте 16 МГц
      TCCR1A = 0;
      TCCR1B = 0;
      TCNT1 = 0;
      OCR1A = 15624; // регистр совпадения (от 0 до 65535)
      //15624 = (16*10^6)/(1*1024)-1 (<65536)
      TCCR1B |= (1<<WGM12); // режим СТС (сброс по совпадению)
      TCCR1B |= (1<<CS12) | (1<<CS10); // предделитель 1024
      TIMSK1 = (1<<OCIE1A); // разрешить прерывание по совпадению
      sei();
    }

    void loop(void) {
      //delay(1000); // убрал задержку, перенес код в прерывание
    }

    // Инициализация DS18B20
    bool DS18B20_init(){
      bool ret = 0;
      digitalWrite(DS18B20_PIN, LOW);     // Послать импульс сброса на датчик
      pinMode(DS18B20_PIN, OUTPUT);
      delayMicroseconds(500);             // Подождать 500 мкс
      pinMode(DS18B20_PIN, INPUT);
      delayMicroseconds(100);             // Подождать, пока датчик ответит
      if (!digitalRead(DS18B20_PIN)) {
        ret = 1;                          // Датчик подключен
        delayMicroseconds(400);           // Подождать 400 мкс

      }
      return(ret);
    }

    void ds18b20_write_bit(bool value){
      digitalWrite(DS18B20_PIN, LOW);
      pinMode(DS18B20_PIN, OUTPUT);
      delayMicroseconds(2);
      digitalWrite(DS18B20_PIN, value);
      delayMicroseconds(80);
      pinMode(DS18B20_PIN, INPUT);
      delayMicroseconds(2);

    }

    void ds18b20_write_byte(byte value){
      byte i;
      for(i = 0; i < 8; i++)
        ds18b20_write_bit(bitRead(value, i));

    }

    bool ds18b20_read_bit(void) {
      bool value;
      digitalWrite(DS18B20_PIN, LOW);
      pinMode(DS18B20_PIN, OUTPUT);
      delayMicroseconds(2);
      pinMode(DS18B20_PIN, INPUT);
      delayMicroseconds(5);
      value = digitalRead(DS18B20_PIN);
      delayMicroseconds(100);
      return value;
    }

    byte ds18b20_read_byte(void) {
      byte i, value;
      for(i = 0; i  <8; i++)
        bitWrite(value, i, ds18b20_read_bit());
      return value;
    }



    bool ds18b20_read(int *raw_temp_value) {
      if (!DS18B20_init())                     // Отправить начальный импульс
        return(0);                              // Вернуть 0, если ошибка
      ds18b20_write_byte(0xCC);                 // Проверка кода датчика
      ds18b20_write_byte(0x44);                 // Запуск температурного преобразования
      while(ds18b20_read_byte() == 0);          // Подождать, пока оно закончится
      if (!DS18B20_init())                     // Отправить начальный импульс
        return(0);                              // Вернуть 0, если ошибка
      ds18b20_write_byte(0xCC);                 // Проверка кода датчика
      ds18b20_write_byte(0xBE);                 // Считываем содержимое ОЗУ
      *raw_temp_value = ds18b20_read_byte();    // Read temperature LSB byte and store it on raw_temp_value LSB byte
      *raw_temp_value |= (unsigned int)(ds18b20_read_byte() << 8);     // Read temperature MSB byte and store it on raw_temp_value MSB byte
      return(1);                                // OK --> return 1
    }
     
     
  7. KindMan

    KindMan Гик

    А вот это напрасно
     
    parovoZZ нравится это.
  8. Почему? , мне delay() лишняя
    Правильный опрос датчика при 12 бит разрешении должен быть не менее чем через 750 мс по даташиту. Так эту задержку можно сделать и по прерыванию
     

    Вложения:

  9. KindMan

    KindMan Гик

    У вас как то всё сложно… вы хотите сделать устройство, или практикуетесь с таймерами, регистрами?
     
    b707 нравится это.
  10. Так я ещё буду часы добавлять на ds1307 , мне задержки лишние не надо
    Хочу сделать, суточное реле времени с измерение комнатной темпепатуры
     
  11. KindMan

    KindMan Гик

    В "лупе" делайте все действия с разной периодичностью. Мне кажется, так проще.
     
    b707 нравится это.
  12. b707

    b707 Гуру

    для этого прерывания совершенно не нужны. все делается в 2 строки обычным миллис
     
  13. b707

    b707 Гуру

    Александр, вы выбрали очень сложный путь.
    Вот, почитайте вот эту ветку -
    http://arduino.ru/forum/programmirovanie/pochistil-sketch-primera-raboty-s-ds18b20-iz-bibly-oneware
    там и примеры кода есть, например в сообщении №11 - всего-то десяток строк, ни прерываний, ни задержек.

    PS надеюсь админы простят ссылку на чужой форум. Где-то на амперке то есть подобная ветка, но у меня ссылки нет
     
  14. ИгорьК

    ИгорьК Гуру

    А чего тут такого то. Ссылка как ссылка, не реклама.
     
  15. b707, я все понимаю. Этот код можно было написать через готовую либу для ds18b20 и делать опрос датчика на millis().
    Дело в том, что полгода назад заказал 5 шт про мини на atmega168 с 16 кБ. Поэтому делаю прошивку под нее. Код часов брал с http://www.jucetechnology.com/wordpress/a-timer-with-the-ds1307-rtc-chip-the-code/. Он очень жрущий память, из -за большого количества строк. Вот и стараюсь с большего сам писать без библиотек и встроенных функций
    Так и тут позже перепишу все digitalRead и digitalWrite и pinMode на прямое обращение к порту
     
    Последнее редактирование: 30 авг 2019
  16. SergeiL

    SergeiL Гуру

    Ну, а в прерывании задержку делать вообще нельзя, а Вы делаете. в функции ds18b20_read()
    Вы думаете у вас прерывание подвиснет на 750мс а все остальное будет продолжать работать? Не будет!

    Чтобы задержки не было, надо в loop() запускать преобразование, и не в цикле ждать взведения флага окончания преобразования, а в loop() проверять флаг, и если взвелся - считывать.
    Так задержки не будет. А прерывание для этого не требуется.
     
  17. SergeiL, вы уверены, что прерывание так надолго повиснет, там в сумме получаются миллисекундные паузы. Само прерывание вызывается каждую секунду, но в нем больших задержек нет
     
  18. В симуляторе программа нормально отрабатывает
     
  19. b707

    b707 Гуру

    Александр, посмотрите на это с другой стороны. Разница в цене между атмегой 168 и 328 - менее 50 рублей....
     
  20. Я понимаю, но уже плату сделал
    Часы проверял нормально выводят, как буду дома поразбираюсь с температурой
    Думаю, тоже все будет в порядке