DS18B20 только с библиотекой OneWire

Питер Скаргилл нашел способ использовать датчик температуры DS18B20 без какой-либо библиотеки и просто используя Библиотека OneWire, и удивительно, насколько она молниеносна! (это старая статья 2013 года)

#include <OneWire.h>
int16_t dallas(int x,byte start){
    OneWire ds(x);
    byte i;
    byte data[2];
    int16_t result;
    do{
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        for (int i = 0; i < 2; i++) data[i] = ds.read();
        result=(data[1]<<8) |data[0];
        result>>=4; if (data[1]&128) result |=61440;
        if (data[0]&8) ++result;
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return result;
}
void setup (){
  dallas(A0,1);
}
void loop (){
  float currentTemp = dallas(A0,0);
}

Проблема в точности, он преобразует float в int, чтобы получить точность в 1 градус, и все работает нормально.

Как я могу получить точность 0,5 (или 0,2?) градуса, используя эту полубиблиотеку?

, 👍3

Обсуждение

Какой контакт у DS18B20? я не понимаю, @Uriel


2 ответа


Лучший ответ:

4

На самом деле он не конвертирует float в int. Он никогда не использует float, так как он ему не нужен. В таблице данных DS18B20 на странице 6 показано, как форматируются данные:

структура данных

  1. 4 старших бита просто напоминают 11-й бит знака.
  2. Первая цифра с полными градусами – бит 4. Каждый бит ниже имеет значения ниже 1 °C.

Таким образом, байты данных 0b1111100001010000 будут представлять температуру -123°C, а 0b1111100001011000 -122,5°C.

Автор ссылки затем сдвигает эти данные на 4 бита вправо, так что все биты, представляющие значения меньше 1, исчезают. Вы также можете преобразовать полученное значение за раздачу в float, используя это.

int16_t whole_degree = (result & 0x07FF) >> 4;
float temperature = whole_degree + 0.5*((data[0]&0x8)>>3) + 0.25*((data[0]&0x4)>>2) + 0.125*((data[0]&0x2)>>1) + 0.625*(data[0]&0x1);
if (data[1]&128) temperature*=-1;

Поскольку float не имеет той же структуры, что и данные, нам нужно их преобразовать. Поэтому я сначала рассчитываю количество целых градусов по аналогии с вашим кодом. Затем я добавляю это со значениями младших 4 бит. ((data[0]&0x8)>>3) и его братья и сестры будут равны 1, если установлен соответствующий бит, и 0, если нет.

Таким образом, вы получаете полную точность датчика (12 бит). Обратите внимание, что для того, чтобы это работало, вы не должны настраивать сенсор на меньшее разрешение. В этом случае вам придется игнорировать соответствующие младшие биты. Но датчик все равно находится в 12-битном режиме при включении питания, и ваш код не настраивает его иначе.

Примечание. Я не уверен в строке result |=61440; в исходном коде. Это устанавливает 4 старших бита 16-битного значения равным 1. Я не понимаю, почему это должно быть правильным здесь, поскольку обычное 16-битное целое число имеет только 1 знаковый бит. В моем понимании исходный код привел бы к ложным показаниям (далеко), при чтении температуры ниже нуля. Вместо этого я просто умножил полученную температуру на -1.

Весь код будет выглядеть следующим образом (я также добавил оператор для вывода чистых полученных данных в виде двоичных файлов, как я просил в своем комментарии. Эти операторы в настоящее время закомментированы):

#include <OneWire.h>
float dallas(int x,byte start){
    OneWire ds(x);
    byte i;
    byte data[2];
    int16_t result;
    float temperature;
    do{
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        for (int i = 0; i < 2; i++) data[i] = ds.read();
        result=(data[1]<<8) |data[0];
        // Здесь вы можете распечатать полученные байты в двоичном виде, как и просили в моем комментарии:
        // Serial.println(результат, BIN);
        int16_t whole_degree = (result & 0x07FF) >> 4; // вырезаем знаковые биты и сдвигаем
        temperature = whole_degree + 0.5*((data[0]&0x8)>>3) + 0.25*((data[0]&0x4)>>2) + 0.125*((data[0]&0x2)>>1) + 0.625*(data[0]&0x1);
        if (data[1]&128) temperature*=-1;
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return temperature;
}
void setup{
  //Добавление Serial.begin(9600); для печати полученных байтов здесь
  //Серийный.начало(9600);
  dallas(A0,1);
}
void loop{
  float currentTemp = dallas(A0,0);
}
,

я внедрил это в код, и чтение составляет около 470 градусов!, @ElectronSurf

Mhh, вы можете вывести чистое чтение в двоичном формате и написать это в комментарии. Вы можете сделать это через серийный номер с помощью Serial.println(result, BIN);, @chrisl

я думаю, может быть, моя имплантация неверна, не могли бы вы вставить этот код в мой код или дать какую-то инструкцию, куда я должен вставить эти строки?, @ElectronSurf

""результат" не был объявлен в этой области", @ElectronSurf

@newbie Я добавил в свой ответ непроверенную реализацию. Можешь попробовать. Есть также операторы для печати чистого чтения в двоичном формате. Эти утверждения просто нужно раскомментировать в случае, если этот код работает некорректно., @chrisl

спасибо, там написано: «Результат не был объявлен в этой области», когда я хочу выполнить «Serial.println (результат, BIN)» в пустом цикле / настройке. а температура все равно выше 400..., @ElectronSurf

Да, конечно, это говорит о том, что в цикле, так как вы не должны помещать его туда, а в функцию dallas(). Но вы можете просто использовать предоставленный мной код и раскомментировать строки с Serial в dallas() и setup(), @chrisl

111001010 458,00, @ElectronSurf

@newbie, вы просили меня взглянуть на этот вопрос, но я думаю, что этот ответ хорошо его объяснил (и проголосовал за него) ... или у вас есть что-то конкретное, на что я могу обратить внимание?, @Michel Keijzers

@MichelKeijzers да, спасибо, Крис хорошо объяснил, но показания температуры неверны. подумал, может быть, вы имеете представление об этом..., @ElectronSurf

@newbie Я исправил свой код. Дело со знаком не сработало, как я думал, поэтому теперь я сделал это, просто умножив на -1. Также я включил бит, чтобы вырезать биты знака из integer_grade перед сдвигом, чтобы единицы не могли создавать мусорные значения. Пожалуйста, еще раз проверьте, работает ли это для вас. Надеюсь, что теперь это правильно. Я проверил расчеты с запасным Arduino Nano и значением макета (тот, который вы разместили в качестве комментария, должен дать 28 625 ° C)., @chrisl

@chrisl спасибо, но результат для меня все тот же! 111000011 451,00 Очень странно!, @ElectronSurf

@newbie Более эффективно следить за исправлением Крисла, поскольку он почти или уже нашел правильный ответ, вместо того, чтобы я начал погружаться в эту проблему., @Michel Keijzers

@MichelKeijzers, спасибо, что заглянули, чувак, очень ценю это., @ElectronSurf

@chrisl я скопировал/вставил точный код, который вы разместили, и я просто делаю две печати: Serial.println(result, BIN); и Serial.println(dallas(A0,0));, почему я я все еще получаю это 111000011 451,00 на выходе!, @ElectronSurf

Я буду исследовать снова, @chrisl

@chrisl Я бы купил тебе выпить, если бы знал тебя в реальном мире, ты такой хороший джентльмен., @ElectronSurf

@newbie всегда пожалуйста... но все кредиты для chrisl, @Michel Keijzers

Я думаю, что нашел проблему *facepalm* Я не изменил оператор return функции. Он по-прежнему возвращает «результат», но реальная температура находится в переменной «температура». Я меняю это сейчас, @chrisl

Комментарии не для расширенного обсуждения; этот разговор был [перемещен в чат] (https://chat.stackexchange.com/rooms/96267/discussion-on-answer-by-chrisl-ds18b20-with-only-onewire-library)., @VE7JRO


1

Почему бы не переписать функцию dallas() так, чтобы она возвращала число с плавающей запятой?

#include <OneWire.h>

OneWire ds(A0);

float dallas(OneWire& ds, byte start = false) {
    int16_t temp;
    do {
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        ds.read_bytes((uint8_t*) &temp, sizeof(temp));
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return (temp * 0.0625);
}

void setup {
  dallas(ds, true);
}

void loop {
  float currentTemp = dallas(ds);
}
,