Датчик температуры DS18B20

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Пподскажите пожалуйста. Используется код для вывода температуры на дисплей. Фишка в том, что на дисплее температура постоянно показывается со значениями, например 5.000 градусов, а бывает 5.00 и всё такое. Т.е. разное кол-во нулей. Как сделать наиболее точный вывод температуры, с возможными половинами крадуса ? 
Вот код:
 

#include <LiquidCrystal.h>
#include <OneWire.h>

OneWire  ds(13);  // Подключаем датчик к 8 цифровому пину

LiquidCrystal lcd(32, 30, 28, 26, 24, 22); //Подключаем LCD


void setup(void) 
{
  pinMode(9, OUTPUT); 
  analogWrite(9, 0);  
  lcd.begin(16, 2); //16 знаков, 2 строки
  lcd.print("Temp:"); //Печатаем верхнюю строку LCD
}

void loop(void) 
{
  byte i;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
  // Ищем алрес датчика
  if ( !ds.search(addr)) 
  {
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  // Проверяем не было ли помех при передаче
  if (OneWire::crc8(addr, 7) != addr[7]) 
  {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // Определяем серию датчика
  switch (addr[0]) 
  {
    case 0x10:  
      type_s = 1;
      break;
    case 0x28:
      type_s = 0;
      break;
    case 0x22:
      type_s = 0;
      break;
    default:
      return;
  } 

  ds.reset();            
  ds.select(addr);       // Выбираем адрес
  ds.write(0x44, 1);     // Производим замер, в режиме паразитного питания
  delay(10000);    
  
  ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Считываем оперативную память датчика

  for ( i = 0; i < 9; i++) 
  {           
    data[i] = ds.read(); // Заполняем массив считанными данными
  }  // Данные о температуре содержатся в первых двух байтах, переведем их в одно значение и преобразуем в шестнадцатиразрядное число
  int16_t raw = (data[1] << 8) | data[0]; // Переводим температуру в шкалы по Цельсию и Фаренгейту  
  if (type_s) 
  {
    raw = raw << 3; 
  }
  if (data[7] == 0x10) 
  {
    raw = (raw & 0xFFF0) + 12 - data[6];
  } else {
    byte cfg =  (data[4] & 0x60);
    if (cfg == 0x00) 
      raw = raw << 3; 
    else if  (cfg == 0x20) 
      raw = raw << 2; 
    else if  (cfg == 0x40) \
      raw = raw << 1;
  }  
  celsius =  (float)raw / 16.0;
  
  lcd.setCursor(8, 0); //Пишем в LCD на 2 строке 
  lcd.print(celsius); //Цельсии
 }

 

bwn
Offline
Зарегистрирован: 25.08.2014

lcd.print(celsius , 1)
Вроде так

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Ну и это округлит до целых ? Может ли датчик выводить точне значения ? 1.5C; 1,2C и всё остальное ? 

 

bwn
Offline
Зарегистрирован: 25.08.2014

Округлит до 1 знака после запятой. Вы бы хоть попробовали сперва.
Разрядность самого датчика при 12бит - 0,00625гр.цельсия

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Ну теперь постоянно показывает чисто.ноль. (-4.0 ; 3.0). А хочу, чтоб более точное значение было, с десятым/сотыми. Подскажите пожалуйста.

bwn
Offline
Зарегистрирован: 25.08.2014

Х-ня у вас какая то, попробуйте отсюда код, четвертый пост, для одного датчика. В третьей строке впишите адрес своего датчика. Если будет также гнать только целыми, раскомментируйте блок с 12 по 17 строки. Отпишитесь, что получилось.

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Вот состряпал такой франкенштейн-код. И на экране просто написанно " -1 ".
 

#include <OneWire.h>
#include <LiquidCrystal.h>

OneWire  ds(12);
LiquidCrystal lcd(32, 30, 28, 26, 24, 22); //Подключаем LCD

byte addr[8]={0x28,0x04,0x13,0x80,0x06,0x00,0x00,0xF8};
volatile int celsius;

void setup(void) 
{
pinMode(9, OUTPUT); 
  analogWrite(9, 0);  
  lcd.begin(16, 2); //16 знаков, 2 строки
  lcd.print("Temp:"); //Печатаем верхнюю строку LCD

pinMode (13,OUTPUT);
WDTCSR=(1<<WDCE)|(1<<WDE);
WDTCSR=(1<<WDIE)| (1<<WDP2)|(1<<WDP1);
ds.reset(); // сброс шины
ds.select(addr); //выставить адрес
ds.write(0x4E); // разрешение записать конфиг
ds.write(0x7F); // Th контроль температуры макс 128грд
ds.write(0xFF); //Tl контроль температуры мин -128грд
ds.write(0x60); // 0x60 12-бит разрешение, 0x00 -9бит разрешение
}
void loop(void) {

}
ISR (WDT_vect){ //вектор прерывания WD
static boolean n=0; // флаг работы: запрос температуры или её чтение
n=!n;
if (n) {ds.reset();  // сброс шины
        ds.select(addr); // выбор адреса
        ds.write(0x44); // начать преобразование (без паразитного питания)
        }
else   {ds.reset();
        ds.select(addr);    
        ds.write(0xBE); // Read Scratchpad (чтение регистров)  
        celsius =  ds.read() | (ds.read()<<8); //прочитаны 2 байта       
        }
        lcd.setCursor(8, 0); 
        lcd.print(celsius); 
}

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

f0rZzZ, вы что, через строчку читаете то, что вам пишут? Перечитайте ещё раз второе предложение из #5

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Простите, немного туговат ) Подскажите, как узнать адресс своего датчика ? 

terminal
Offline
Зарегистрирован: 02.09.2015

Нужно умножить показания на 0,0625. Пример #A8  168*0,0625= 10,5

 

bwn
Offline
Зарегистрирован: 25.08.2014

f0rZzZ, запускаете примеры из OneWire для DS18, какой то из них показывает адреса. Зачем вы создаете себе проблемы, экран на компьютере не работает? Добейтесь сперва нормального считывания, а потом работоспособный  код прикручивайте к LCD.

terminal, а откуда вы такое узнали?

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Так, ну в терминал выдаёт теперь температуру как и хотелось бы: -5.56

А подсоеденив экран, и вписав нужный код, на экране крокозябры. Может из-за честого обновления параметра температуры с датчика ? 
maslyukov
Offline
Зарегистрирован: 29.12.2015

f0rZzZ пишет:

Пподскажите пожалуйста. Используется код для вывода температуры на дисплей. Фишка в том, что на дисплее температура постоянно показывается со значениями, например 5.000 градусов, а бывает 5.00 и всё такое. Т.е. разное кол-во нулей. Как сделать наиболее точный вывод температуры, с возможными половинами крадуса ? 
Вот код:
 

#include <LiquidCrystal.h>
#include <OneWire.h>

OneWire  ds(13);  // Подключаем датчик к 8 цифровому пину

LiquidCrystal lcd(32, 30, 28, 26, 24, 22); //Подключаем LCD


void setup(void) 
{
  pinMode(9, OUTPUT); 
  analogWrite(9, 0);  
  lcd.begin(16, 2); //16 знаков, 2 строки
  lcd.print("Temp:"); //Печатаем верхнюю строку LCD
}

void loop(void) 
{
  byte i;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
  // Ищем алрес датчика
  if ( !ds.search(addr)) 
  {
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }

  // Проверяем не было ли помех при передаче
  if (OneWire::crc8(addr, 7) != addr[7]) 
  {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // Определяем серию датчика
  switch (addr[0]) 
  {
    case 0x10:  
      type_s = 1;
      break;
    case 0x28:
      type_s = 0;
      break;
    case 0x22:
      type_s = 0;
      break;
    default:
      return;
  } 

  ds.reset();            
  ds.select(addr);       // Выбираем адрес
  ds.write(0x44, 1);     // Производим замер, в режиме паразитного питания
  delay(10000);    
  
  ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Считываем оперативную память датчика

  for ( i = 0; i < 9; i++) 
  {           
    data[i] = ds.read(); // Заполняем массив считанными данными
  }  // Данные о температуре содержатся в первых двух байтах, переведем их в одно значение и преобразуем в шестнадцатиразрядное число
  int16_t raw = (data[1] << 8) | data[0]; // Переводим температуру в шкалы по Цельсию и Фаренгейту  
  if (type_s) 
  {
    raw = raw << 3; 
  }
  if (data[7] == 0x10) 
  {
    raw = (raw & 0xFFF0) + 12 - data[6];
  } else {
    byte cfg =  (data[4] & 0x60);
    if (cfg == 0x00) 
      raw = raw << 3; 
    else if  (cfg == 0x20) 
      raw = raw << 2; 
    else if  (cfg == 0x40) \
      raw = raw << 1;
  }  
  celsius =  (float)raw / 16.0;
  
  lcd.setCursor(8, 0); //Пишем в LCD на 2 строке 
  lcd.print(celsius); //Цельсии
 }

 

Тут ошибка

int16_t raw = (data[1] << 8) | data[0];

data имеет тип byte при сдвиге обнулится, потому делать нужно так

int16_t raw = ((int16_t)data[1] << 8) | data[0];

Далее, для получения результата с точностью после запятой делаем следующее

uint16_t cels = raw >> 4;
uint16_t mcels = ((raw & 0xF)*625)/100;
lcd.print(cels);
lcd.print(',');
if (mcels < 10) {
    //добавляем ноль перед дробной частью
    lcd.print(0);
}
lcd.print(mcels);

 

bwn
Offline
Зарегистрирован: 25.08.2014

f0rZzZ пишет:

Так, ну в терминал выдаёт теперь температуру как и хотелось бы: -5.56

А подсоеденив экран, и вписав нужный код, на экране крокозябры. Может из-за честого обновления параметра температуры с датчика ? 

Я вам для чего ссылку дал, чтобы посмотрели, как у Dimax сделаны ваши раскоряченные сдвиги и преобразования. Не нужна "собака", уберите ее и останется два фрагмента - на преобразование и на считывание. Между ними задержка 750мС при 12бит. И крутите эти фрагменты, как вам нравится.

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Простите, но так и не разобрался, что подправить для большей выдержки (

bwn
Offline
Зарегистрирован: 25.08.2014
#include <OneWire.h>
OneWire  ds(2);
byte addr[8]={0x28,0x04,0x13,0x80,0x06,0x00,0x00,0xF8};
volatile int celsius;
void setup(void) {
Serial.begin(9600);
pinMode (13,OUTPUT);
//WDTCSR=(1<<WDCE)|(1<<WDE); //установить биты WDCE WDE (что б разрешить запись в другие биты
//WDTCSR=(1<<WDIE)| (1<<WDP2)|(1<<WDP1); // разрешение прерывания + выдержка 1 секунда
// (55 страница <a href="<a href="http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf" rel="nofollow">http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf</a>" rel="nofollow">даташита</a>)
// снять все ремарки если нужно поменять разрешение
//  ds.reset(); // сброс шины
//  ds.select(addr); //выставить адрес
//  ds.write(0x4E); // разрешение записать конфиг
//  ds.write(0x7F); // Th контроль температуры макс 128грд
//  ds.write(0xFF); //Tl контроль температуры мин -128грд
//  ds.write(0x60); // 0x60 12-бит разрешение, 0x00 -9бит разрешение
}

void loop(void) {
Serial.print("  Temperature = ");
Serial.println(celsius/16.0);

 
//ISR (WDT_vect){ //вектор прерывания WD
//static boolean n=0; // флаг работы: запрос температуры или её чтение
//n=!n;
//if (n) 
        ds.reset();  // сброс шины
        ds.select(addr); // выбор адреса
        ds.write(0x44); // начать преобразование (без паразитного питания)
        
        delay(750); 

//else   
        ds.reset();
        ds.select(addr);    
        ds.write(0xBE); // Read Scratchpad (чтение регистров)  
        celsius =  ds.read() | (ds.read()<<8); //прочитаны 2 байта       
        
}

Добавьте инициализацию LCD. После сериалпринтов, напишите аналогично команды для своего LCD. До делая команды на преобразование, после на считывание. В первый проход покажет чушь, все последующие правда.
Что нужно сделать, чтобы сразу показало правду?

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Благодарю всех. Сделал, всё шикарно.

bwn
Offline
Зарегистрирован: 25.08.2014

Ну, если нет желания поразбираться, ваше право.

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Дак я понял в чём суть, подправил что надо и добился желаемого результата. 

bwn
Offline
Зарегистрирован: 25.08.2014

Так delay(750), это фи. 3/4 секунды программа висит. Если только выводить температуру, без разницы. Начнете приделывать кнопки и прочие фишки, начнет раздражать или непонятно работать. Собака у Dimax не просто так прикручена была.

f0rZzZ
Offline
Зарегистрирован: 31.01.2015

Ну пока просто температуру и вывожу. А потом хочу попробовать проект Arduino Mega Server

Alexino
Offline
Зарегистрирован: 29.12.2015

bwn пишет:

Собака у Dimax не просто так прикручена была.

ИМХО, проще millis заюзать, чем эти извращения с собакой.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Alexino, конечно проще, какай разговор! А ещё проще взять даллосовскую библу, и не забивать голову лишними командами. А ещё проще вообще ничего не делать, а лежать на диване и пить пиво. Зачем извращаться и что-то самому колхозить, когда всё можно купить готовое? :)

Alexino
Offline
Зарегистрирован: 29.12.2015

Ну... скатились до "купить" :) Причём тут это ? Просто для чего использовать какие то нестандартные решения, если для этого есть стандартные ? Нужен периодичный опрос - юзаем системный таймер - это вполне обоснованное и адекватное решение.
Собака... Вообще, на сколько мне известно, собака ресетит чип. В таком случае, использовать её для периодичных интервалов - не совсем айс.
Далласовская библиотека. Тут она тоже немного не в тему. Она не реализует никаких периодичных опросов. Так что, не известно, проще ли её взять.
Ничего не делать. Хм... заманчиво, но не по-нашему :)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Alexino пишет:

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

Что значит не айс? Вотчдог это такой же таймер. Даже в чём-то более удобный, т.к. может работать когда МК спит.

Alexino
Offline
Зарегистрирован: 29.12.2015

А он не ресетит микроконтроллер ? 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Alexino, по желанию. У него три варианта по истечению счёта: только прерывание, только ресет, прерывание и за ним ресет.

Alexino
Offline
Зарегистрирован: 29.12.2015

Всё ясно. Ну тогда да, как вариант....

Но всё равно, через millis более стандартно ))))))