// Набор функций из библиотеки для чтения данных термометра DS18B20 // Автор Nord_Air 4.1.2023г. // Более подробная информация в файлах библиотеки. //Прототипы функций для работы с 1-wire сетью. byte One_Wire_Reset (byte one_wire_network); byte One_Wire_Write (byte one_wire_network, byte *data_array); void One_Wire_Read (byte one_wire_network, byte *data_array); byte One_Wire_CRC (byte *data_array ); float DS18B20_Get_Temperature (byte one_wire_network, byte *device_SN, byte ); void DS18B20_Scan_SN (byte one_wire_network); static byte DS18B20_001 [9] = {9, 0x28,0x3A,0xCE,0x81,0xE3,0xC5,0x3C,0x9F}; // Массив включает в себя: Код семейства термометра DS18B20, серийный номер датчика, CRC код для проверки целосности данных. // Все эти данные необходимо внести в данный массив, в той последовательности, в которой они приходят в Serial Port. void setup() { Serial.begin(9600); } void loop() { float tempo = DS18B20_Get_Temperature (9, DS18B20_001, 0); Serial.print("Temperature: "); Serial.print(tempo,2); Serial.println(" Сelsius"); Serial.println("---------------------------"); DS18B20_Scan_SN (9); delay (5000); } // Функция получения серийного номера одного подключенного датчика. void DS18B20_Scan_SN (byte one_wire_network) { byte temperature_data [9] = {2,0,0,0,0,0,0,0,0}; byte temp; One_Wire_Reset (one_wire_network); temperature_data [1] = 0x33; //команда прочесть ROM One_Wire_Write (one_wire_network, temperature_data); temperature_data [0] = 9; One_Wire_Read (one_wire_network, temperature_data); // Чтение ROM temp=1; //Переменная для счетчика элементов массива. Начинается с 1, так как нулевой элемент занят данными о количестве всех элементов массива. One_Wire_Reset (one_wire_network); // "Отзеркаливание" полученных значений. Младшие биты байта меняются со старшими местами. (Пример: 00001011 - 11010000) for ( temp; temp < temperature_data[0]; temp++) { byte a = temperature_data[temp]; for (byte i=0; i<8; i++) { byte b = (a >> i) & 0x01; temperature_data[temp] = (temperature_data[temp] << 1) + b; } } Serial.print("Byte 1 : 0x"); Serial.print(temperature_data [1], HEX); Serial.println(" - DS18B20 Famaly code"); Serial.print("Byte 2 : 0x"); Serial.print(temperature_data [2], HEX); Serial.println(" *"); Serial.print("Byte 3 : 0x"); Serial.print(temperature_data [3], HEX); Serial.println(" *"); Serial.print("Byte 4 : 0x"); Serial.print(temperature_data [4], HEX); Serial.println(" * * *6-bit Serial Number"); Serial.print("Byte 5 : 0x"); Serial.print(temperature_data [5], HEX); Serial.println(" *"); Serial.print("Byte 6 : 0x"); Serial.print(temperature_data [6], HEX); Serial.println(" *"); Serial.print("Byte 7 : 0x"); Serial.print(temperature_data [7], HEX); Serial.println(" *"); Serial.print("Byte 8 : 0x"); Serial.print(temperature_data [8], HEX); Serial.println(" - CRC code"); Serial.println("---------------------------"); } byte One_Wire_Reset (byte one_wire_network) //Команда 1-wire сети: Сброс/присутствие { pinMode (one_wire_network, OUTPUT); digitalWrite (one_wire_network, LOW); delayMicroseconds(520); //Импульс сброса должен составлять от 480 до 960 мксек. pinMode (one_wire_network, INPUT); //Устройство "раздумывает" от 15 до 60 мксек. return (pulseIn(one_wire_network, LOW, 320)); //Импульс присутствия устройства составляет от 60 до 240 мксек. //Ожидаем = (МАХ длина импульса присутствия + МАХ раздумка устройства) } byte One_Wire_Write (byte one_wire_network, byte *data_array) //Функция записи данных 1-wire интерфейса. Принимает аргументом номер пина шины данных и массива байт необходимых для передачи //[0] элемент массива определяет его размер. { for (byte k=1; k> i) & 0x01; if (a) { pinMode (one_wire_network, OUTPUT); digitalWrite (one_wire_network, LOW); delayMicroseconds(10); pinMode (one_wire_network, INPUT); delayMicroseconds(50); } else { pinMode (one_wire_network, OUTPUT); digitalWrite (one_wire_network, LOW); delayMicroseconds(80); //Время подтяжки линии данных к земле, для записи "0" состовляет от 60 до 120 мксек. pinMode (one_wire_network, INPUT); delayMicroseconds(4); //Небольшой промежуток ожидания между командами. } } } } void One_Wire_Read (byte one_wire_network, byte *data_array) { //[0] элемент, определяет количество всех ячеек массива, включая первый элемент. byte bit_lenght = data_array[0] * 8; byte n; for(int8_t m=8; m < bit_lenght; m++) { pinMode (one_wire_network, OUTPUT); digitalWrite (one_wire_network, LOW); delayMicroseconds(2); pinMode (one_wire_network, INPUT); delayMicroseconds(8); n = (digitalRead (one_wire_network)); //Выборка состояния лини должна происходить в первые 15 мксек. data_array[m/8]<<=1; data_array[m/8]+= n; delayMicroseconds(56); } } byte One_Wire_CRC (byte *data_array ) //Функция расчета CRC 8 bit для многочлена (полинома) X^8 + X^5 + X^4 + 1 что эквивалентно 10011000+1 = 0х131, старший бит опускаем = 0х31 //Принимает аргументом массив по сcылке, но значения элементов не меняет. Первая ячейка - размер всего массива, включая первую ячейку. //Принимает данные в том виде которые поступили от чтения датчика, не "отзеркаленные" { byte crc=0; byte temp=0; for ( byte i=1; i<(data_array[0])-1; i++) { byte data = data_array[i]; crc = data ^ crc ; for (byte k=0; k<8; k++) { crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1; } } return crc; // Расчет CRC хорошо описан на странице https://mind-control.fandom.com/wiki/Циклический_избыточный_код } //Функция получения температуры. Принимает аргументами : пин к которому подключен датчик, серийный номер датчика, и переменную точности значений. (0-3) float DS18B20_Get_Temperature (byte one_wire_network, byte *device_SN, byte accuracy) { byte temperature_data [10] = {2,0,0,0,0,0,0,0,0,0}; // Массив для получения данных из блокнота датчика, // На первых этапах будет использоваться для отправки команд. byte T; // Предварительная переменная для возврата значения температуры byte temp; // Временная переменная для нужд функции. signed char sign=0; // Переменная для определения знака температуры. (0 - положит температура 1 - отрицательная) int conversion_time = 764; static byte accuracy_z=5; // Статичная переменная для запуска блока switch - case, чтобы при опросе датчика не происходила запись в конфигурационный бит, // если значения аргумента accuracy не изменилось с предыдущего обращения к функции. Начальное значение должно отличаться от всех номеров case. if ( 60 < (One_Wire_Reset (one_wire_network)) < 240) // Работоспособность датчика(-ов) проверяется измерением длинны импулса ответа (подтверждение присутствия) { temperature_data [1] = 0x55; //Совпадение ROM One_Wire_Write (one_wire_network, temperature_data); One_Wire_Write (one_wire_network, device_SN); if ((accuracy) != (accuracy_z)) { temperature_data [1] = 0x4E; //Команда записи конфигурационных байтов в блокнот. One_Wire_Write (one_wire_network, temperature_data); // Подготавливаем массив для отправки конфигурационных байт temperature_data [0] = 4; // Делаем запись в первую ячейку, чтобы функция поняла что массив имеет 4 байта данных. temperature_data [1] = 85; // TH/User Byte в значении по умолчанию temperature_data [2] = 5; // TL/User Byte в значении по умолчанию // Необходимо перезаписать все три конфигурационных байта. switch (accuracy) //Блок выборки точности измеряемой температуры. { case 0: //12 bit ( Время расчета датчиком значения температуры 750мс ) temperature_data [3] = 0x7F; break; case 1: //11 bit ( Время расчета датчиком значения температуры 375мс ) temperature_data [3] = 0x5F; break; case 2: //10 bit ( Время расчета датчиком значения температуры 187.5мс ) temperature_data [3] = 0x3F; break; case 3: //9 bit ( Время расчета датчиком значения температуры 93.75мс ) temperature_data [3] = 0x1F; break; } One_Wire_Write (one_wire_network, temperature_data); One_Wire_Reset (one_wire_network); temperature_data [0] = 2; // Делаем запись в первую ячейку, чтобы теперь функция поняла что массив имеет 2 байта данных. temperature_data [1] = 0x55; // Совпадение ROM One_Wire_Write (one_wire_network, temperature_data); One_Wire_Write (one_wire_network, device_SN); } temperature_data [1] = 0x44; //Запрос расчета температуры One_Wire_Write (one_wire_network, temperature_data); One_Wire_Reset (one_wire_network); delay (conversion_time >> accuracy); //Ожидание конвертации. One_Wire_Reset (one_wire_network); temperature_data [1] = 0x55; //Команда : Совпадение ROM One_Wire_Write (one_wire_network, temperature_data); One_Wire_Write (one_wire_network, device_SN); temperature_data [1] = 0xBE; //Команда : Прочесть блокнот One_Wire_Write (one_wire_network, temperature_data); temperature_data [0] = 10; One_Wire_Read (one_wire_network, temperature_data); // Чтение блокнота One_Wire_Reset (one_wire_network); accuracy_z = accuracy; // Сохраняем текущее значение accuracy переданое в функцию // Если при следующем запуске функции значение будет отличаться, запустится блок изменения конфигурационного бита. } else { // обработка ошибки работоспособности датчика. Длинна импульса подтверждения превышает установленный диапазон. 60 < Х < 240 //return (888); //Serial.print("Not connect sensor DS18B20 Serial Number : "); //Serial.print(device_SN[2], HEX); //Serial.print(device_SN[3], HEX); //Serial.print(device_SN[4], HEX); //Serial.print(device_SN[5], HEX); //Serial.print(device_SN[6], HEX); //Serial.print(device_SN[7], HEX); //Serial.println(); } //Блок проверки CRC if ( (temperature_data[9] - One_Wire_CRC (temperature_data)) != 0) { //Обработка ошибки не корректного CRC. //return (999); //Serial.print("CRC Code not correct. Sensor DS18B20 Serial Number : "); //Serial.print(device_SN[2], HEX); //Serial.print(device_SN[3], HEX); //Serial.print(device_SN[4], HEX); //Serial.print(device_SN[5], HEX); //Serial.print(device_SN[6], HEX); //Serial.print(device_SN[7], HEX); //Serial.println(); } //Далее, необходимо "отзеркалить" полученые байты. Сделать пример следующее: 01001 0000 <-> 0000 10010; //Так как массив передается по ссылке, то сразу же изменяем данные полученные предыдущим блоком. temp=1; //Переменная для счетчика элементов массива. Начинается с 1, так как нулевой элемент занят данными о количестве всех элементов массива. for ( temp; temp < temperature_data[0]; temp++) { byte a = temperature_data[temp]; for (byte i=0; i<8; i++) { byte b = (a >> i) & 0x01; temperature_data[temp] = (temperature_data[temp] << 1) + b; } } /* Serial.print("----------------------- "); Serial.println(); Serial.print("LSB "); Serial.println(temperature_data[1], BIN); Serial.print("LSB "); Serial.println(temperature_data[1]); Serial.print("Config: "); Serial.println(temperature_data[5], HEX); */ sign=0; // определения знака температуры. (0 - положит температура 1 - отрицательная) по умолчанию положительная temp = temperature_data [1]; if (temperature_data [2] > 16) // Если старший бит больше 12 то значения битов инвертируются кроме последних 4х байт младшего бита // Для расчета отрицательной температуры. // см. таблицу 2 даташита. { temperature_data [2] = ~temperature_data [2]; temp = ~temperature_data [1]; sign = 1; //Знак результирующего значения температуры меняется на отрицательный. } T = (temperature_data [2] * 16) + (temp >> 4) ; temp = (sign) ? (16 -(temperature_data [1] & B00001111)) : (temperature_data [1] & B00001111); // Сравнение с числом 15 для обнуления старших четырех бит LSB //Преобразование типов и сумма целой и дробной части температуры. return ((float)T+((float)temp * 0.0625 )) * ((sign) ? (-1) : (1)); }