В продолжение темы 1-Wire рассмотрим температурный 1-Wire датчик — DS18S20 🙂

DS18S20 – высокоточный цифровой термометр с интерфейсом 1-Wire (High Precision 1-Wire Digital Thermometer) от DALLAS Semiconductor (Maxim).
Для подключения датчика достаточно двух проводов – линии данных и заземления;
питание элемента в этом случае называется «паразитным»/фантомным (Parasite power mode) и осуществляется по линии данных за счёт накопления энергии во встроенном конденсаторе во время высокого уровня напряжения
(не рекомендуется при температуре свыше +100 С из-за быстрого разряда конденсатора).

Нормальный режим питания (external supply), заключается в подключении к датчику источника питания (3V-5V).

В обоих случаях рекомендуется использовать подтягивающий резистор в 4.7k
Характеристики DS18S20:
Интерфейс 1-Wire
Измеряемая температура от -55 до +125 °C
Точность 0.5 °C в диапазоне от -10 до +85 °С
Температура считывается 9-ю битами данных
Время на конвертацию температуры — 750 ms (максимальное)
Как и любое 1-Wire устройство — каждый DS18S20 содержит уникальный 64-битный ROM
Первые 8 бит — код серии ( для DS18S20 код — 10h, а для DS18B20 — 28h ).
Затем 48 бит уникального номера, и в конце 8 бит CRC-кода.
Однако, самое интересное — данные о температуре хранятся в оперативной памяти(scratch-pad memory) датчика.

Память состоит из оперативной ROM и энергонезависимой EEPROM:
Первые два байта – содержат данные об измеренной температуре,
Третий и четвёртый байты хранят верхний (TH) и нижний (TL) пределы температуры.
Пятый и шестой – не используются.
Седьмой и восьмой – байты – счётчики – могут использоваться для более точного измерения температуры.
Девятый байт хранит CRC-код предыдущих восьми 🙂
Теперь осталось разобраться с тем – какие команды может выполнять этот датчик.
Кроме уже знакомых нам:
Поиск адресов — Search ROM [F0h]
Чтение адреса устройства — Read ROM [33h]
Выбор адреса — Match ROM [55h]
Игнорировать адрес — Skip ROM [CCh]
Добавились:
Поиск Тревоги — Alarm Search [ECh] – операция этой команды идентична операции поиска адресов [F0h], за исключением того, что в данном случае ответят только те датчики, у которых, при последнем измерении, температуры вышла за установленные пределы (выше TH или ниже TL).
И команды работы со scratch-pad memory:
Конвертировать температуру — Convert T [44h] – датчик произведёт измерение и запись данных о текущей температуре. Если ведущее устройство будет за этой командой слать тайм-слоты чтения, то пока конвертация не закончена — DS18S20 будет выдавать в линию «0», а после завершения конвертации «1».
Если датчик работает в режиме паразитного питания, то не позже 10 мкс после подачи команды устройство управления должно установить высокий уровень на шине на время продолжительности преобразование (не менее 750 ms)
Запись в память — Write Scratchpad [4Eh] – эта команда позволяет записать 3 байта в память датчику. Первый байт запишется в TH, второй в TL, а третий байт запишется в пятый байт памяти – у DS18S20 – он не используется, а у DS18B20 – это байт конфигурации
Чтение памяти — Read Scratchpad [BEh] – позволяет нам считать память датчика. В ответ на эту команду датчик вернёт 9 байт своей памяти, начиная с 0-го байта TEMPERATURE LSB и заканчивая восьмым – CRC.
Копировать память — Copy Scratchpad [48h] – датчик скопирует содержимое ОЗУ — TH и TL в EEPROM
Если датчик работает в режиме паразитного питания, то не позднее 10 мкс после подачи этой команды устройство управление должно установить высокий уровень на шине и поддерживать его в течении не менее 10ms.
Производители обещают, что EEPROM датчика DS18S20 должен выдержать минимум 50000 циклов перезаписи и будет хранить данные 10 лет при T = +55°C.
Повторная загрузка — Recall E2 [B8h] – загружает данные из EEPROM в ОЗУ. Эта операция выполняется автоматически, как только на датчик подаётся напряжение.
Вид электропитания датчика — Read Power Supply [B4h] – с помощью этой команды можно определить – какой вид питания использует датчик. Если датчик выставит на шине «0» — значит он использует паразитное питание. Если же датчик использует внешнее питание, то он ответит «1».
Теперь становится понятно, что нужно сделать, чтобы получить от датчика данные о температуре 🙂
После RESET-а и поиска устройств на линии 1-Wire, нужно выдать команду
0x44 , чтобы запустить конвертацию температуры датчиком.
Подождать не менее 750 ms и выдать команду
0xBE , чтобы считать ОЗУ датчика.
Данные о температуре будут в первых двух байтах.

Остаётся только сложить эти два байта, предварительно сдвинув старший байт на 8 бит влево:
TReading = (HighByte << 8) + LowByte;
Температуру получим просто разделив на 2 (шаг ведь в пол-градуса)
Tc_100 = TReading/2;
Однако, датчик ведь называется «высокоточный»! Поэтому предусмотрена процедура для получения более точных данных о температуре:
0. Считать данные с датчика: LSB, MSB, COUNT_REMAIN, COUNT_PER_C
1. Получить данные о температуре с точностью в пол-градуса. (Tc_100)
2. Уточнённые данные о температуре высчитываются по формуле:
Temperature = Tc_100 – 0.25 + (COUNT_PER_C - COUNT_REMAIN)/ COUNT_PER_C
Как помним, для работы с датчиками с интерфейсом 1-Wire можно использовать библиотеку Jim Studt-а – OneWire. скачать
Соответственно, скетч для получения температуры от DS18S20 будет таким:
#include <OneWire.h>
/*
* получаем температуру от DS18S20
*/
OneWire ds(10); // линия 1-Wire будет на pin 10
void setup(void)
{
Serial.begin(9600);
}
void loop(void)
{
byte i;
byte present = 0;
byte data[12];
byte addr[8];
if ( !ds.search(addr)) {
Serial.print("No more addresses.\n");
ds.reset_search();
return;
}
Serial.print("R=");
for( i = 0; i < 8; i++) {
Serial.print(addr[i], HEX);
Serial.print(" ");
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.print("CRC is not valid!\n");
return;
}
if ( addr[0] != 0x10) {
Serial.print("Device is not a DS18S20 family device.\n");
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // запускаем конвертацию
delay(1000); // скорее всего достаточно 750ms
// we might do a ds.depower() here, but the reset will take care of it.
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // считываем ОЗУ датчика
Serial.print("P=");
Serial.print(present,HEX);
Serial.print(" ");
for ( i = 0; i < 9; i++) { // обрабатываем 9 байт
data[i] = ds.read();
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.print(" CRC=");
Serial.print( OneWire::crc8( data, 8), HEX);
Serial.println();
// высчитываем температуру :)
int HighByte, LowByte, TReading, Tc_100;
LowByte = data[0];
Serial.print("LB= ");Serial.print(LowByte,HEX);
HighByte = data[1];
Serial.print(" HB= ");Serial.print(HighByte,HEX);
TReading = (HighByte << 8) + LowByte;
Tc_100 = TReading/2;
Serial.print(" T = ");Serial.print(Tc_100);
Serial.println();
}
Этот скетч можно опробовать даже не собирая схемы :)
Элемент DS18S20 есть в базе протеуса, поэтому можно набросать схему в нём:

Загрузив в МК полученный после компиляции hex и запустив симуляцию увидим в окошке терминала:

Соответствующая температура выставляется на виртуальном датчике кнопками со стрелочками вверх и вниз .
А вот что будет в порту, если попробуем подключить реальный датчик DS18S20



80 комментариев на «“Практическое программирование Arduino/CraftDuino — температурный 1-Wire датчик DS18S20”»
Жаль что библиотека OneWire не катит под последнюю arduino-0018
Побыстропу не пофиксить, нужно править либу.
или просто воспользоваться 0017 😉
Как то не по фэншую 🙂
а на основании чего был сделан вывод, что библиотека не работает под 0018? 🙂
Пробовал скомпилить, вывалилась ошибка.
Ошибка касательно digital_pin_to_port и т.п. что связано с пинами. Теперь работа с ними немного другая, поэтому старых структур просто нет (в этом то и ошибка).
Я конечно могу ошибаться, потому что предыдущие версии не видел, ибо обзавелся контроллером только на днях, поэтому заюзал сразу последнюю версию. Но порывшись в сурсах библиотек для дуины, пришел именно к такому выводу.
странно — сейчас специально проверил — под 0018 всё нормально скомпилировалось. Может просто версия самой библиотеки старая?
Ну как бы скачал по ссылке в топике… Там последняя?
попробуйте эту версию —
Вот эта уже под последние версии сделана. Скомпилилась нормально.
Жаль датчик у меня не DS18S20 а DS18B20 и этот пример не работает на нем, пишет всегда: No more addresses.
Спасибо за либу.
На 18В20 всё должно работать. Проверьте подключение датчика. На паразитном питании у меня считывает температуру на 20-метровом кабеле и с нескольких датчиков.
Датчик работает, юзаю его нормально с другим скетчем. Дело в коде. Или вы тоже такой датчик юзаете и все ок?
да, у меня 18В20.
Там кода то — 5 строк, где у вас вылетает 🙂
if ( !ds.search(addr)) { Serial.print("No more addresses.\n"); ds.reset_search(); return; }у меня это работает. Ошибка может только тут быть:
Больше мест не вижу.
Кстати, <b> внутри <code> тут не работает 😉
Да и закрытие тегов не отслеживается…
Ругается:
Может здесь подругому нужно проверять на нашем датчике?
if ( addr[0] != 0x10) { Serial.print("Device is not a DS18S20 family device.\n"); return; }Заккоментил это место, вроде прошло, но температуру показывает не правильно, на улице -23 а он мне -187 показывает.
Заменил расчет температуры на другой, из рабочего скетча:
int HighByte, LowByte, TReading, Tc_100, SignBit, Whole, Fract; LowByte = data[0]; HighByte = data[1]; TReading = (HighByte << 8) + LowByte; SignBit = TReading & 0x8000; // test most sig bit if (SignBit) // negative { TReading = (TReading ^ 0xffff) + 1; // 2's comp } Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625) or 6.25 Whole = Tc_100 / 100; // separate off the whole and fractional portions Fract = Tc_100 % 100; Serial.print("Temperature: "); if (SignBit) // If its negative { Serial.print("-"); } Serial.print(Whole); Serial.print("."); if (Fract < 10) { Serial.print("0"); } Serial.print(Fract); Serial.print("\n");И вуала, работает нормально 🙂
Но все же мне не нравится косяк на проверке… почему не определился датчик?
потому что это другая модель датчика, и у неё, разумеется, другой код серии 😉
Так это совсем другая ошибка 🙂
Да, для B20 надо так:
Дома поподробнее еще гляну эту статью… Вообще с данным датчиком делал термометр на Arduino Duemilanove — про CraftDuino, к сожалению, только вчера узнал… Делал вывод на сборку из 4-х семисегментников… Если кому интересно — могу, в принципе, выложить…
конечно интересно! выкладывайте 🙂
здравствуйте, купил датчик DS18B20… ds.search(addr) возвращает все время 0… схему проверял несколько раз… как можно проверить работоспособность датчика?? в чем может быть проблема?
там же вся схема — это четыре проводка и резистор О_о
и что значит возвращает 0?
в том то и проблема
if (!ds.search(addr)) { ds.reset_search(); return; }происходит цикл в это ифе. то есть arduino не видит датчик.
попробуйте проверить схему подключения на
Спасибо за помощь, я так и не понял, что было не так, но сейчас заработало.
Как подключить библиотеку Onewire?
1. распаковать
2. поместить директорию библиотеки в библиотечную директорию Arduino IDE (\hardware\libraries\) или создать в папке своих скетчей такую же библиотеку libraries и поместить её туда
3. перезапустить IDE
см.
Вопрос таковой:
А если нужно считывать с 3-х датчиков одновременно температуру, то как это реализовать и еще сделать приделы, то есть при 30 и выше подается питание на 13 пин, а если меньше — LOW.
и так с 3-мя датчиками и контактами 13,12,10 соответственно??
помогите пожалуйста=)
Всем спасибо за помощь. Вообщем я переписал код под оба датчика (надеюсь 🙂 ), у меня работает.
Вот если кому интересно:
#include <OneWire.h> /* * Получаем температуру от датчиков серии DS18x20 */ // Поддерживаемые датчики #define DS18S20_ID 0x10 #define DS18B20_ID 0x28 // Линия 1-Wire будет на pin 2 OneWire ds(2); void setup(void) { Serial.begin(9600); } void loop(void) { byte i; byte present = 0; byte data[12]; byte addr[8]; if (!ds.search(addr)) { ds.reset_search(); return; } if (OneWire::crc8( addr, 7) != addr[7]) { Serial.print("CRC is not valid!\n"); return; } if (addr[0] != DS18S20_ID && addr[0] != DS18B20_ID) { Serial.print("Device is not a DS18x20 family device.\n"); return; } ds.reset(); ds.select(addr); // Запускаем конвертацию ds.write(0x44, 1); // Подождем... delay(1000); present = ds.reset(); ds.select(addr); // Считываем ОЗУ датчика ds.write(0xBE); // Обрабатываем 9 байт for ( i = 0; i < 9; i++) { data[i] = ds.read(); } // Высчитываем температуру :) int HighByte, LowByte, TReading, Tc_100, SignBit, Whole, Fract; LowByte = data[0]; HighByte = data[1]; TReading = (HighByte << 8) + LowByte; // Проверяем дубак там или нет SignBit = TReading & 0x8000; // Если на улице дубак :) if (SignBit) { TReading = (TReading ^ 0xffff) + 1; } // Умножаем на (100 * 0.0625) или 6.25 Tc_100 = (6 * TReading) + TReading / 4; // Отделяем целые от дробных чисел Whole = Tc_100 / 100; Fract = Tc_100 % 100; Serial.print("Temperature: "); // Если на улице дубак напишем минус перед цифрами :) if (SignBit) { Serial.print("-"); } Serial.print(Whole); Serial.print("."); if (Fract < 10) { Serial.print("0"); } Serial.print(Fract); Serial.print("\n"); }Работает с DS18B20. Спасибо.
Не подскажете, где найти простую программу для фиксации показаний датчика в файле компьютера?
Каким образом можно считывать температуру на 20-метровом кабеле и с нескольких датчиков?
На счет проги не знаю, помоему проще самому накидать в этом случае.
А в кабеле сколько жил? 🙂
Три жилы. 10 датчиков.
Тут нужно углублятся в OneWire интерфейс. Я честно говоря не знаю, поддерживает ли он последовательное/параллельное подключение девайсов.
Где-то читал, что DS18B20 можно вешать параллельно на кабель и опрашивать любой по их уникальному адресу.
Для этого надо в программе заранее считать адреса всех датчиков на кабеле и тогда опрашивать любой.
На то он и 1wire 🙂 Можно вешать кучу датчиков и даже разных. И термометр и гигрометр и iButton и чёрта в ступе 🙂
программку можно накидать самостоятельно — на программирования.
Причём, можно не просто сохранять в файл, а и (используется скрипт на питоне) 🙂
Выкладывать на сайт — этим наверно воспользуюсь позже. Тем более, имеется готовое решение.
Пробую управлять с помощью Wiring. Для начала хватает. Да, и на сайтах много готовых программ.
В каких случаях может потребоваться Питон? Когда без него не обойтись?
обойтись можно — если предпочитаете программировать на чём-то другом 😉
Я просто уточнил, что в примере (для опрашивания COM-порта ардуины со стороны ПК) используется скрипт на питоне.
Для опрашивания COM-порта можно использовать
Он позволит, при желании, и в файл сохранить, и графику нарисовать.
И ноги у них растут из одного места — Wiring/Arduino IDE/Processing…
разумеется можно — просто там ближе к java 🙂
Надеюсь Вы простите новичка за сжатость изложения…
В конструкции использован четырехразрядный моноблочный семисегментный индикатор с десятичными точками CA04-41SRWA от Kingbright. Разряды выполнены по схеме с общим анодом так же 2 старших разряда объединены между собой катодами сегментов и так же 2 младших.
Для реализации схемы все соответствующие катоды были объединены между собой в результате чего для целей индикации было задействованно 12(1-8 сегменты с точками 9-12 разряды) цифровых «портов». И 1(0) порт был использован под чтение с датчика…
Для операций была утянута библиотека , Найденная дрейфовым поиском по родному сайту Arduino…
Дальше, собственно, используя 2 таймер и прерывание от данного таймера по переполнению была реализованна классическая схема динамической индикации.
#include <avr/interrupt.h> #include <DallasTemperature.h> int rdigs[] = {9, 10, 11, 12}; int rsegs[] = { 1, 2, 3, 4, 5, 6, 7, 8}; bool digits[15][8] = { {1, 1, 1, 1, 1, 1, 0, 0}, //0 {0, 1, 1, 0, 0, 0, 0, 0}, //1 {1, 1, 0, 1, 1, 0, 1, 0}, //2 {1, 1, 1, 1, 0, 0, 1, 0}, //3 {0, 1, 1, 0, 0, 1, 1, 0}, //4 {1, 0, 1, 1, 0, 1, 1, 0}, //5 {1, 0, 1, 1, 1, 1, 1, 0}, //6 {1, 1, 1, 0, 0, 0, 0, 0}, //7 {1, 1, 1, 1, 1, 1, 1, 0}, //8 {1, 1, 1, 1, 0, 1, 1, 0}, //9 {0, 0, 0, 0, 0, 0, 0, 0}, //none {0, 0, 0, 0, 0, 0, 1, 0}, //- {1, 0, 0, 1, 1, 1, 1, 1}, //E. {0, 0, 0, 0, 1, 0, 1, 1}, //r. {1, 1, 1, 1, 1, 1, 0, 1} //0. }; int cdigit = 0; int odigit = 0; int displ[] = {0, 8, 4, 9}; DallasTemperature tempSensor; void displayTemperature(float term); void cleardisplay(); void displayErr(int err); // Подпрограмма прерывания ISR(TIMER2_OVF_vect) { // return; int dig; if(cdigit < 0 || cdigit > 3) cdigit = 0; digitalWrite(rdigs[odigit], LOW); dig = abs(displ[cdigit]); if(displ[cdigit] < 0) digits[dig][7] = 1; else digits[dig][7] = 0; for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i])); digitalWrite(rdigs[cdigit], HIGH); odigit = cdigit; cdigit++; TCNT2 = 0; } void setup() { for(int i = 0; i<4; i++) { pinMode(rdigs[i], OUTPUT); digitalWrite(rdigs[i], LOW); } for(int i = 0; i<8; i++) { pinMode(rsegs[i], OUTPUT); digitalWrite(rsegs[i], LOW); } //Установка делителей таймера TCCR2A = 0; TCCR2B |= 1<<CS22; TCCR2B &= ~((1<<CS21) | (0<<CS20)); TCCR2B &= ~((1<<WGM21) | (1<<WGM20)); //Timer2 Overflow Interrupt Enable TIMSK2 |= 1<<TOIE2; //reset timer TCNT2 = 0; cleardisplay(); tempSensor.begin(0); } void loop() { byte i; byte present = 0; byte addr[8]; switch(tempSensor.isValid()) { case 1: //Invalid CRC displayErr(-1); tempSensor.reset(); // Attempts to redetect IC return; case 2: //Not a valid device displayErr(-2); tempSensor.reset(); // Attempts to redetect IC return; } displayTemperature(tempSensor.getTemperature()); } void displayTemperature(float term) { int bdis[4] = {10, 10, 10, 10}; int cycle; int temp = 100*abs(term); cycle = 2; while(temp > 0) { for(int i=0; i<3; i++) bdis[i] = bdis[i+1]; bdis[3] = temp % 10; if(cycle==0) { if(bdis[3]==0) bdis[3] = -14; else bdis[3] *= -1; } temp /= 10; cycle--; } if(term<0) { for(int i=0; i<3; i++) bdis[i] = bdis[i+1]; bdis[3] = 11; if(cycle==0) bdis[3] *= -1; } for(int i=0; i<4; i++) displ[i] = bdis[i]; } void cleardisplay() { for(int i=0; i<4; i++) displ[i] = 10; } void displayErr(int err) { displ[0] = err; displ[1] = -13; displ[2] = -13; displ[3] = -12; }Как мне кажется — наименования переменных и функций говорят сами за себя, но если есть вопросы — отвечу…
большое спасибо! хотя, такой подробный комментарий лучше было бы оформить в виде отдельной статьи 😉
Попробую попозже оформить статьей…
Извиняюсь — накосячил с картинками, надеюсь администрация поправит…
Спасибо за пример, давно уже искал аналогичное. Только при компилировании появляются ошибки:
Библиотеку DallasTemperature.h установил и проверил, а в этом примере ошибка почему то.
Еще нигде ничего не нашел про библиотеку
Её нужно отдельно устанавливать или она уже предустановлена?
aspire89
Библиотека должна находиться:
{arduinoinstalldir}/hardware/libraries/DallasTemperature/
Когда положите ее на место она станет доступна из ArduinoIDE через Sketch->Import Library->DallasTemperature
#include <avr/interrupt.h> идет в составе avr-gcc и WinAvr и информацию по работе с ней лучше черпать на сайте Atmel и
Посмотрел еще на всякий случай в Ubuntu 9.10 — пакеты avr-gcc и avr-libc идут раздельно и, если мне не изменяет мой склероз, при установке avr-gcc второй пакет зависиммостями не прихватывается и его приходится устанавливать отдельно…
Спасибо, разобрался. Проблема была в следующем. У меня была установлена библиотека DallasTemperature.h, но не та которая выложена выше и после замены все запускается.
Только индикатор у меня с общим катодом. И если я правильно понимаю, то мне нужно все значения HIGH заменить на LOW и наоборот?
Да. Где в программе напрямую указаны LOW и HIGH — «проинвертировать» их и в таблице digits 0 и 1 поменять…
Либо дождаться вечера — постараюсь сегодня вечерком программку рихтануть чтоб указывать только в одном месте тип включения индикатора…
Спасибо огромное. Также если не трудно прокомментируйете пожалуйста эти строчки:
int cdigit = 0; int odigit = 0; int displ[] = {0, 8, 4, 9};int cdigit = 0; — разряд индикатора обрабатываемый в текущем цикле обработки
int odigit = 0; — разряд индикатора обработанный в прошлом цикле обработки
int displ[] = {0, 8, 4, 9}; — собственно массив куда записана информация для отображения на дисплей… 0 8 4 9 — начальная инициализация, от балды, пока писал саму индикацию — проверял, что правильно отображается… В него записывается порядковый номер элемента из таблицы digits. Если в данном разряде надо отобразить точку — то записывается отрицательное значение… например при
{7,7,7,7} на экране будет 7777, а при {-7,-7,-7,-7} на экране будет 7.7.7.7.
int displ[] = {0, 8, 4, 9} должно отобразиться на дисплее как 9480…
void displayErr(int err) {
displ[0] = err;
displ[1] = -13;
displ[2] = -13;
displ[3] = -12;
}
Отображает E.r.r.{err} при текущем размере digits [15][8] — err не должно быть больше 14…
Писал то программульку чисто для себя — поэтому проверки на ошибки отсутствуют…
Да, и еще — как понимаете -0 — это нонсенс — посему 14 элемент массива digits как раз и предназначен чтоб можно было 0. отобразить на дисплее…
Да, и еще — как понимаете -0 — это нонсенс — посему 14 элемент массива digits как раз и предназначен чтоб можно было 0. отобразить на дисплее…
Т.е. для отображения 0.25 надо заполнить displ[] = {5, 2, -14, 10}
Вчера вечером руки не дошли. Быстренько поправил сидя на работе…
Вобщем, если нигде ничего не пропустил — должно все нормально работать. Для индикаторов с общим катодом надо снять комментарий со строчки
#include <avr/interrupt.h> #include <DallasTemperature.h> //Снять комментарий для индикаторов с общим катодом //#define OKAT bool LLOW = LOW; bool HHIGH = HIGH; int rdigs[] = {9, 10, 11, 12}; int rsegs[] = { 1, 2, 3, 4, 5, 6, 7, 8}; bool digits[15][8] = { {1, 1, 1, 1, 1, 1, 0, 0}, //0 {0, 1, 1, 0, 0, 0, 0, 0}, //1 {1, 1, 0, 1, 1, 0, 1, 0}, //2 {1, 1, 1, 1, 0, 0, 1, 0}, //3 {0, 1, 1, 0, 0, 1, 1, 0}, //4 {1, 0, 1, 1, 0, 1, 1, 0}, //5 {1, 0, 1, 1, 1, 1, 1, 0}, //6 {1, 1, 1, 0, 0, 0, 0, 0}, //7 {1, 1, 1, 1, 1, 1, 1, 0}, //8 {1, 1, 1, 1, 0, 1, 1, 0}, //9 {0, 0, 0, 0, 0, 0, 0, 0}, //none {0, 0, 0, 0, 0, 0, 1, 0}, //- {1, 0, 0, 1, 1, 1, 1, 1}, //E. {0, 0, 0, 0, 1, 0, 1, 1}, //r. {1, 1, 1, 1, 1, 1, 0, 1} //0. }; int cdigit = 0; int odigit = 0; int displ[] = {0, 8, 4, 9}; DallasTemperature tempSensor; void displayTemperature(float term); void cleardisplay(); void displayErr(int err); // Подпрограмма прерывания ISR(TIMER2_OVF_vect) { // return; int dig; if(cdigit < 0 || cdigit > 3) cdigit = 0; digitalWrite(rdigs[odigit], LLOW); dig = abs(displ[cdigit]); #ifdef OKAT if(displ[cdigit] < 0) digits[dig][7] = 0; else digits[dig][7] = 1; #else if(displ[cdigit] < 0) digits[dig][7] = 1; else digits[dig][7] = 0; #endif for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i])); digitalWrite(rdigs[cdigit], HHIGH); odigit = cdigit; cdigit++; TCNT2 = 0; } void setup() { int i; #ifdef OKAT int j; LLOW = HIGH; HHIGH = LOW; for(i=0; i<15; i++) { for(j=0; j<8; j++) { if(digits[i][j]==0) digits[i][j]=1; else digits[i][j]=0; } } #endif for(i=0; i<4; i++) { pinMode(rdigs[i], OUTPUT); digitalWrite(rdigs[i], LLOW); } for(i=0; i<8; i++) { pinMode(rsegs[i], OUTPUT); digitalWrite(rsegs[i], LLOW); } //Установка делителей таймера TCCR2A = 0; TCCR2B |= 1<<CS22; TCCR2B &= ~((1<<CS21) | (0<<CS20)); TCCR2B &= ~((1<<WGM21) | (1<<WGM20)); //Timer2 Overflow Interrupt Enable TIMSK2 |= 1<<TOIE2; //reset timer TCNT2 = 0; cleardisplay(); tempSensor.begin(0); } void loop() { byte i; byte present = 0; byte addr[8]; switch(tempSensor.isValid()) { case 1: //Invalid CRC displayErr(-1); tempSensor.reset(); // Attempts to redetect IC return; case 2: //Not a valid device displayErr(-2); tempSensor.reset(); // Attempts to redetect IC return; } displayTemperature(tempSensor.getTemperature()); } void displayTemperature(float term) { int bdis[4] = {10, 10, 10, 10}; int cycle; int temp = 100*abs(term); cycle = 2; while(temp > 0) { for(int i=0; i<3; i++) bdis[i] = bdis[i+1]; bdis[3] = temp % 10; if(cycle==0) { if(bdis[3]==0) bdis[3] = -14; else bdis[3] *= -1; } temp /= 10; cycle--; } if(term<0) { for(int i=0; i<3; i++) bdis[i] = bdis[i+1]; bdis[3] = 11; if(cycle==0) bdis[3] *= -1; } for(int i=0; i<4; i++) displ[i] = bdis[i]; } void cleardisplay() { for(int i=0; i<4; i++) displ[i] = 10; } void displayErr(int err) { displ[0] = err; displ[1] = -13; displ[2] = -13; displ[3] = -12; }Вообще, как идея, надо попробовать сделать автоматическое определение типа индикатора… Если в ближайшее время дойдут руки — попробую…
собственно сама идея в следующем:
допустим мы включаем диод между 0 и 1 портами
0 порт в статусе INPUT
1 в статусе OUTPUT
при включении 0-К 1-А вне зависимости от состояния порта 1 — с нулевого должна читаться HIGH.
при включении 0-А 1-К при состоянии 1 LOW на 0 должно считаться LOW, а при HIGH — HIGH…
Соответственно, если я не ошибся — в функции setup можно проверить поведение индикатора, допустим между rdigs[0] и rsegs[0] и провести соответствующие изменения переменных, которые сейчас у меня сделаны за счет директив препроцессора…
Спасибо огромное. Теперь на индикаторах отображаются правильные цифры, но так и не смог разобрать температуру. Попробовал запустить пример из архива библиотеки:
#include <DallasTemperature.h> DallasTemperature tempSensor; // You may instantiate as many copies as you require. void setup(void) { // initialize inputs/outputs // start serial port Serial.begin(9600); tempSensor.begin(12); // Data wire is plugged into port 12 on the Arduino Serial.println("Dallas Temperature IC Control Library 1.0. Miles Burton"); } void loop(void) { // Ask the library whether the device is valid switch(tempSensor.isValid()) { case 1: Serial.println("Invalid CRC"); tempSensor.reset(); // Attempts to redetect IC return; case 2: Serial.println("Not a valid device"); tempSensor.reset(); // Attempts to redetect IC return; } // getTemperature returns a float. Serial.print(tempSensor.getTemperature()); Serial.print("C"); Serial.println(); Serial.print(DallasTemperature::toFahrenheit(tempSensor.getTemperature())); // Use the inbuild Celcius to Fahrenheit conversion. Returns a float Serial.print("F"); Serial.println(); }Выдает сообщение Invalid CRC, хотя написано, что датчик DS1820 поддерживается. Есть возможность проверить DS18B20, как попробую, так отпишусь.
Тут я тебе ничего не подскажу — у меня нет эксперементальной базы — посмотреть неначем… Тут только доки вычитывать и библиотеку проверять, липо поискать версию посвежее…
Пока решил вывести температуру с ЦП.
На индикаторы для проверки вывожу 1 2 3 4:
void loop() { displ[0] = 1; displ[1] = 2; displ[2] = 3; displ[3] = 4; }Работает отлично.
Написал приложение на питоне для передачи температуры, в ардуино получаю значение следующим образом:
void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { incomingByte = Serial.read(); Serial.print(incomingByte, BYTE); } }В serial monitor вижу значение температуры. Но вместо 1 2 3 4 уже отображается ‘1 (зеркальная 9) 9 4. Только закомментирую все, что связано с serial и на индикаторах 1 2 3 4.
Вот весь код:
#include <avr/interrupt.h> //Снять комментарий для индикаторов с общим катодом #define OKAT bool LLOW = LOW; bool HHIGH = HIGH; int incomingByte = 0; int rdigs[] = {12, 11, 10, 9}; int rsegs[] = { 6, 2, 7, 4, 5, 1, 3, 8}; bool digits[15][8] = { {1, 1, 1, 1, 1, 1, 0, 0}, //0 {0, 1, 1, 0, 0, 0, 0, 0}, //1 {1, 1, 0, 1, 1, 0, 1, 0}, //2 {1, 1, 1, 1, 0, 0, 1, 0}, //3 {0, 1, 1, 0, 0, 1, 1, 0}, //4 {1, 0, 1, 1, 0, 1, 1, 0}, //5 {1, 0, 1, 1, 1, 1, 1, 0}, //6 {1, 1, 1, 0, 0, 0, 0, 0}, //7 {1, 1, 1, 1, 1, 1, 1, 0}, //8 {1, 1, 1, 1, 0, 1, 1, 0}, //9 {0, 0, 0, 0, 0, 0, 0, 0}, //none {0, 0, 0, 0, 0, 0, 1, 0}, //- {1, 0, 0, 1, 1, 1, 1, 1}, //E. {0, 0, 0, 0, 1, 0, 1, 1}, //r. {1, 1, 1, 1, 1, 1, 0, 1} //0. }; int cdigit = 0; int odigit = 0; int displ[] = {0, 8, 4, 9}; // Подпрограмма прерывания ISR(TIMER2_OVF_vect) { // return; int dig; if(cdigit < 0 || cdigit > 3) cdigit = 0; digitalWrite(rdigs[odigit], LLOW); dig = abs(displ[cdigit]); #ifdef OKAT if(displ[cdigit] < 0) digits[dig][7] = 0; else digits[dig][7] = 1; #else if(displ[cdigit] < 0) digits[dig][7] = 1; else digits[dig][7] = 0; #endif for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i])); digitalWrite(rdigs[cdigit], HHIGH); odigit = cdigit; cdigit++; TCNT2 = 0; } void setup() { int i; #ifdef OKAT int j; LLOW = HIGH; HHIGH = LOW; for(i=0; i<15; i++) { for(j=0; j<8; j++) { if(digits[i][j]==0) digits[i][j]=1; else digits[i][j]=0; } } #endif for(i=0; i<4; i++) { pinMode(rdigs[i], OUTPUT); digitalWrite(rdigs[i], LLOW); } for(i=0; i<8; i++) { pinMode(rsegs[i], OUTPUT); digitalWrite(rsegs[i], LLOW); } //Установка делителей таймера TCCR2A = 0; TCCR2B |= 1<<CS22; TCCR2B &= ~((1<<CS21) | (0<<CS20)); TCCR2B &= ~((1<<WGM21) | (1<<WGM20)); //Timer2 Overflow Interrupt Enable TIMSK2 |= 1<<TOIE2; //reset timer TCNT2 = 0; cleardisplay(); Serial.begin(9600); } void loop() { if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read(); Serial.print(incomingByte, BYTE); } displ[0] = 1; displ[1] = 2; displ[2] = 3; displ[3] = 4; } void cleardisplay() { for(int i=0; i<4; i++) displ[i] = 10; }Даже не знаю в чем может быть проблема.
Проблема может быть в работе с прерываниями…
Ругалось на этой строчке:
ISR(TIMER2_OVF_vect, , ISR_BLOCK ISR_NAKED) {исправил на:
ISR(TIMER2_OVF_vect, ISR_BLOCK ISR_NAKED) {компилируется, но проблема осталась.
Проверил вчера дома — да проблемка присутствует… Причем начинается с Serial.begin();…
Судя по HardwareSerial.cpp — надо копать в сторону приоритетов и маскирования прерываний… Вобщем читать доку по камушку…
Спасибо огромное за помощь, без Вас мне не прикрутить было бы 4 семисегментных индикатора. В планах приобретение LCD дисплея. Может кто нибудь посоветует какой лучше выбрать?
Подключил TFT WF43BTIBED0…
Но эксперименты еще не закончены…
Посему подробности позже…
Если кого заинтересует — подробности постараюсь сделать позже…
Нашел статью про прерывания, может кому нибудь пригодится
Попробуй:
// Подпрограмма прерывания ISR(TIMER2_OVF_vect, , ISR_BLOCK ISR_NAKED) { // return; int dig; cli(); if(cdigit < 0 || cdigit > 3) cdigit = 0; digitalWrite(rdigs[odigit], LLOW); dig = abs(displ[cdigit]); #ifdef OKAT if(displ[cdigit] < 0) digits[dig][7] = 0; else digits[dig][7] = 1; #else if(displ[cdigit] < 0) digits[dig][7] = 1; else digits[dig][7] = 0; #endif for(int i = 0; i < 8; i++) digitalWrite(rsegs[i], !(digits[dig][i])); digitalWrite(rdigs[cdigit], HHIGH); odigit = cdigit; cdigit++; TCNT2 = 0; // reti() }Есть неплохая библиотека предназначенная как раз для работы с датчиками компании dallas milesburton.com/index.php?title=Dallas_Temperature_Control_Library#Latest поддерживает следующие модели
DS18B20
DS1822
DS18S20*
DS1820
а пример кода выглядит так
#include <OneWire.h> #include <DallasTemperature.h> // Data wire is plugged into pin 2 on the Arduino #define ONE_WIRE_BUS 2 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); void setup(void) { // start serial port Serial.begin(9600); Serial.println("Dallas Temperature IC Control Library Demo"); // Start up the library sensors.begin(); } void loop(void) { // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus Serial.print("Requesting temperatures..."); sensors.requestTemperatures(); // Send the command to get temperatures Serial.println("DONE"); Serial.print("Temperature for Device 1 is: "); Serial.print(sensors.getTempCByIndex(0)); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire }тут видно что это по сути обертка над библиотекой onewire, и здесь нет необходимости побитно разбирать ответ чтобы узнать температуру
Привет всем! Есть три датчика DS18B20, все сидят на одной линии. Как получать температуру с трёх? Может кто привести пример кода? Помогите пожалста, сам допереть не могу.
Можете воспользоваться кодом из примера который я привел выше. Там собственно запрашиваются все датчики висящие на одной линии.А вот эта строчка
собственно выводит показания первого из датчиков, а если подставите вместо нуля 1 или 2 то получите значения со второго или третьего датчика. В примере из основной статьи тоже производится поиск по всем устройствам на линии.Можно взять от туда.
Спасибо за содержательный ответ.
А есть возможность хранить данные прямо в ардуине? Тоесть записывать показания температуры, например раз в 5 минут, и потом скидывать данные на комп? Freeduino Mega+ v.1.22, около 100 кб свободной флеш памяти.
Что-то не рабочий вариант… Ошибки лезут в строке DallasTemperature sensors(&oneWire);
А у меня выводит следующее:
далее то же самое в цикле. В чем может быть причина?
Специально сфоткал:
Видно что первой ножкой датчик подключен к земле, второй ножкой к 12 пину (в коде тоже поправил) и через резистр к питанию. Контакты между собой где не надо не замыкаются (на фото можно подумать иначе).
Подключил к другом ардуино, выдает примерно то же:
Помогите поправить библиотеку OneWire для Arduino 1.x
Ошибки компиляции:
Спасибо!
Подскажите по поводу формата вывода чисел:
например, во Franklin’е когда я хочу определить конкретный формат вывода числа пишу
printf("%+*.*f ",7,3,res);Тем самым указывая наличие знака, формат числовой переменной, количество знаков до и после запятой, саму переменную.
В wiringe не нашел подобного варианта.