Работа с шиной 1-wire. Подключение термодатчика DS18B20 к AVR

Обновлено 21.01.14. Исходники переписаны под AVR Studio 6 (с тулчейном)

Сразу хочу сообщить, что в этой статье я не буду описывать принцип работы шины (описание временных слотов и т.д.). Статья будет типа «Быстрый старт». Конкретно: я выложу свою библиотеку для работы с 1-wire, опишу как ею пользоваться, как подключить устройства к шине, как производить поиск устройств, передавать данные и т.д. А так, как (вероятно) 70% радиолюбителей в первую очередь решит подключить к шине термодатчик DS18B20, я выложу библиотеку для работы с DS18B20, где надо тупо запустить преобразование, подождать пока идет преобразование и записать температуру в массив.

Теория

Итак, 1-wire(еще называют MicroLAN, dallas-wire) так назвали потому что для передачи данных в обе стороны используется только 1 провод. Среди самых популярных устройств, работающих на этой шине можно выделить термодатчик DS18B20 и электронный ключ DS1990 (которым вы в подъезде двери открываете). У сети 1-wire есть одно ведущее устройство(Master) и несколько (или 1) подчиненных (Slave). Бывают сети с несколькими ведущими, по это нам сейчас не нужно. Передачу данных начинает ведущий, только он может посылать сигнал сброса (Reset), остальные устройства могут только отвечать на его запросы. Все устройства подключаются к шине параллельно, то есть, линию данных с каждого устройства (она обозначается DATA, DQ или OW_DQ), включая линию ведущего, подключают к одному проводу, типа вот так: 

Еще линия должна быть подтянута к питанию через резистор 2,2-4,7 кОм. Каждое устройство имеет свой индивидуальный 64-битный адрес, который устанавливается при изготовлении устройства (прямо на заводе).

Практика

Работать с 1-wire мне приходится очень часто, поэтому, я написал безглючную библиотеку, которая имеет кучу функций, включая возможность эмуляции 1-wire с помощью USART. К тому же, библиотека стабильно работает даже в протеусе. Многие говорят, что 1-wire слишком медленный, а из-за того, что он требует очень точных временных задержек (в несколько микросекунд), с ним практически не возможно работать, если в микроконтроллере часто вызываются прерывания. Действительно, если одновременно использовать софтовый USB (V-USB) и 1-wire, то что то одно с них не будет стабильно работать. Я решил эту проблему эмуляцией 1-wire через USART микроконтроллера. Поэтому, если у вас будет свободный USART, то 1-wire обязательно вешайте на него. Как же будем подключать устройства? Я использую три (уже 4) способа. Первый. Эмуляция USARTом. Если вы используете длинную линию (более 20 метров) и более 4 устройств на шине, то подключать нужно вот так: Здесь сигнал усиливается с помощью двух транзисторов. На шине стоит метка OW_DQ, вот к ней и подключаем подчиненные устройства. Если длина не более 20 метров, то можно подключить вот так:Здесь резистор может быть номиналом 2,2 — 4,7 кОм. Если вы не используете USART для эмуляции 1-wire, то подключать нужно так:Здесь одна ножка МК работает в качестве и входа, и выхода. Есть еще и четвертый способ, подключение как на первом, только для эмуляции не используется USART, но мне этот способ ни разу не понадобился. Давайте теперь соберем схему в протеусе (можете скачать в прикрепленных файлах). USART будет занят терминалом, поэтому шина будет подключена по третьем способу. В качестве ведущего — ATMega8, подчиненные — термодатчики DS18B20 и DS18S20, память EEPROM на 256 Кбит, таблетка, DS18B20 и двухканальный ключ DS2413. Так же, на схеме есть терминал, в котором будут отображаться данные.

Код

Скачайте прикрепленные файлы и откройте onewire.h. В нем есть строка

#define MAXDEVICES 10

Здесь указывается максимальное количество подчиненных устройств, которые будет искать подчиненный, если выберите меньше, чем подключено, то просто не будут обнаружены все устройства, если больше — то увеличиться время процедуры поиска. Но лучше ставьте с запасом, при 32 поиск занимает не более двух секунд (а может и меньше секунды).

#define UART_AS_OneWire

Если вы не используете эмуляцию 1-wire через USART, то закомментируйте эту строку.

#define OW_DDR DDRB
#define OW_PORT PORTB
#define OW_PIN PINB
#define OW_BIT 0

Если не используется эмуляция, укажите здесь куда подключен 1-wire. В том же файле есть несколько функций:

unsigned char OW_Reset(void);
void OW_WriteBit(unsigned char bit);
unsigned char OW_ReadBit(void);
unsigned char OW_ReadByte(void);
void OW_WriteByte(unsigned char byte);
unsigned char OW_SearchROM( unsigned char diff, unsigned char *id );
void OW_FindROM(unsigned char *diff, unsigned char id[]);
unsigned char OW_ReadROM(unsigned char *buffer);
unsigned char OW_MatchROM(unsigned char *rom);

По названию функции можно определить их предназначение, поэтому не стоит их описывать. В исходниках откройте главный файл (main.c). Здесь есть функция search_ow_devices(), она производит поиск устройств на шине и записывает их адреса в многомерный массив owDevicesIDs[MAXDEVICES][8], возвращает количество найденных устройств. Вот главная функция:

int main(void)
{
	stdout = &usart_str; // указываем, куда будет выводить printf 
 
	DDRB = 0b00000000; PORTB = 0b00000000;
	DDRC = 0b00000000; PORTC = 0b00000000;
	DDRD = 0b00000010; PORTD = 0b00000000;
 
	USART_init(); // включаем uart
 
	timerDelayInit();
 
	nDevices = search_ow_devices(); // ищем все устройства
 
	printf("---------- Found %d devices ----------", nDevices);
 
	for (unsigned char i=0; i<nDevices; i++) // теперь сортируем устройства и запрашиваем данные
	{
		// узнать устройство можно по его груповому коду, который расположен в первом байте адресса
		switch (owDevicesIDs[i][0])
		{
			case OW_DS18B20_FAMILY_CODE: { // если найден термодатчик DS18B20
				printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
				printf(" - Thermometer DS18B20"); // печатаем тип устройства
				DS18x20_StartMeasureAddressed(owDevicesIDs[i]); // запускаем измерение
				timerDelayMs(800); // ждем минимум 750 мс, пока конвентируется температура
				unsigned char	data[2]; // переменная для хранения старшего и младшего байта данных
				DS18x20_ReadData(owDevicesIDs[i], data); // считываем данные
				unsigned char	themperature[3]; // в этот массив будет записана температура
				DS18x20_ConvertToThemperature(data, themperature); // преобразовываем температуру в человекопонятный вид
				printf(": %d.%d C", themperature[1],themperature[2]);
			} break;
			case OW_DS18S20_FAMILY_CODE: { // если найден термодатчик DS18B20
				printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
				printf(" - Thermometer DS18S20"); // печатаем тип устройства
			} break;
 
			case OW_DS1990_FAMILY_CODE: { // если найден электронный ключ DS1990
				printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
				printf(" - Serial button DS1990"); // печатаем тип устройства
			} break;
			case OW_DS2430_FAMILY_CODE: { // если найдена EEPROM
				printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
				printf(" - EEPROM DS2430"); // печатаем тип устройства
			} break;
			case OW_DS2413_FAMILY_CODE: { // если найден ключ
				printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
				printf(" - Switch 2413"); // печатаем тип устройства
			} break;
		}
	}
while(1){}
}

В начале у нас идет инициализация периферии, затем, производится поиск устройств на шине и найденное количество записывается в переменную nDevices. Далее, в цикле for определяется тип устройства (его можно определить по первом байту адреса). В терминале протеуса печатается адрес каждого устройства и его тип: Как видите, напротив DS18B20 написана еще и его температура, глянем в код:

printf("\r"); print_address(owDevicesIDs[i]); // печатаем знак переноса строки, затем - адрес
printf(" - Thermometer DS18B20"); // печатаем тип устройства
DS18x20_StartMeasureAddressed(owDevicesIDs[i]); // запускаем измерение
timerDelayMs(800); // ждем минимум 750 мс, пока конвентируется температура
unsigned char	data[2]; // переменная для хранения старшего и младшего байта данных
DS18x20_ReadData(owDevicesIDs[i], data); // считываем данные
unsigned char	themperature[3]; // в этот массив будет записана температура
DS18x20_ConvertToThemperature(data, themperature); // преобразовываем температуру в человекопонятный вид
printf(": %d.%d C", themperature[1],themperature[2]);

В первых двух строчках просто печатается адрес устройства и его тип. Затем, запускается измерение с помощью функции DS18x20_StartMeasureAddressed(owDevicesIDs[i]);. В эту функцию передается массив с адресом устройства. Далее, нужно подождать 750 мс, пока конвертируется температура, после чего, считываем данные с устройства DS18x20_ReadData(owDevicesIDs[i], data);. В функцию передается указатель на массив с адресом и указатель на массив, в который надо записать результат (2 байта). Теперь надо перевести эти 2 байта в человекопонятный вид, то есть, «знак, целая часть, запятая, десятая часть», это делает функция DS18x20_ConvertToThemperature(data, themperature);. Кстати, совсем не обязательно для запуска измерения температуры передавать адрес устройства, можно просто написать DS18x20_StartMeasure(). Тогда преобразование запустится на всех DS18B20, которые подключены к шине, спустя 750мс можно считать данные по очереди со всех DS18B20 без задержки. Так же, если используете эмуляцию 1-wire через USART, то обязательно устанавливайте не только DDR пина TX, а и PORT, иначе обмен на шине будет запускаться максимум в 30% случаев.

Обновление №1 (5.12.2011):

Добавил возможность подключения четвертым способом. Подключение такое же как в первых двух, но для эмуляции не используется USART. Этот способ стоит использовать если у Вас занят USART и линия длинней 15-20 метров. В файле onewire.h появились новая опция:

// Если для эмуляции 1-wire не спольльзуется USART, но используется 2 пина (вход и выход)
#define OW_TWO_PINS

Также Вам стоит немного взглянуть сюда:

	#ifndef OW_TWO_PINS //если используется один пин, укажите его номер
		#define OW_BIT 0
	#else // если используются 2 пина, укажите их номера
		#define OW_BIT_OUT 1
		#define OW_BIT_IN 0
	#endif

При настройке портов нужно установить DDR и PORT входа в 0, а выхода в 1.

Обновление №2 (3.1.2012):

Добавил возможность проверки CRC. Теперь функция DS18x20_ReadData() возвращает 0, если crc ошибочная. Зачем это нужно? Делал один проект, где на шине висит много датчиков и есть вероятность, что после прочтения адреса один из датчиков может быть отключен и система должна знать, что его нет, но функция DS18x20_ReadData() возвращает 1, если на шине есть любое устройство 1-wire, так как наличие устройства функция определяет с помощью функции OW_Reset(). Теперь с датчика считываются не только первые 2 байта, в которых температура, а все 9, вместе с контрольной суммой (CRC), контрольная сумма проверяется, и если она не совпала, то возвращается 0. Вопросы в комментариях.

Скачать исходники и проект в Proteus (84 Kb)

Вливайтесь в общение

178 комментариев

  1. Статья понравилась
    единсвенное не нашел в ней упоминания про среду прогаммирования вот и гадаю… вроде WinAvr?

    1. угадали:). Точней в связки AVR Studio 4 + AVR Toolchain

  2. Доброго времени суток)Очень понравилась мне ваша статья)но…есть но..не как у меня датчики не ищутся…ну вообщем не чего не происходит))ткините мея носом есле не сложно что я нетак делаю))юарты все заняты так что эмуляция их отпадает))тоесть закоментил как указано выше) пин изменил на 7 …и тишина

  3. может это звучит нагло)) но всётаки попрошу используется ATMEGA 128 порт В7 тоесть один пин и именно этот если не трудно подскажите дефайны

    1. а про порт не забыли? что именно вы закомментировали в исходнике?

  4. // Если для эмуляции шины используется USART
    //#define UART_AS_OneWire

    // Если для эмуляции 1-wire не спольльзуется USART, но используется 2 пина (вход и выход)
    //#define OW_TWO_PINS

    #ifdef UART_AS_OneWire
    #define USART_BAUDRATE_57600 (((F_CPU / (57600 * 16UL))) — 1)
    #define USART_BAUDRATE_115200 (((F_CPU / (115200 * 16UL))) — 1)
    #define USART_BAUDRATE_9600 (((F_CPU / (9600 * 16UL))) — 1)
    #else
    #include
    #define OW_DDR DDRB
    #define OW_PORT PORTB
    #define OW_PIN PINB
    #ifndef OW_TWO_PINS //если используется один пин, укажите его номер
    #define OW_BIT 7
    #else // если используются 2 пина, укажите их номера
    #define OW_BIT_OUT 1
    #define OW_BIT_IN 0
    #endif
    #endif
    я сделал так….. подскажите плиз..,с уважением)

  5. все правильно.
    А как вы проверяете?

  6. в main.c покачто добавил

    timerDelayInit();
    nDevices = search_ow_devices(); // ищем все устройства
    и в протеусе смотрю ммм..ну вообщем буферы в которых должны быть ром коды,а так же попробовал посмотреть активность ослогрофом на pin 7 port B и в общем не чего не там не там,таймер 0 более не где не использован avr studio 4.18

    1. здесь таймер 0 не нужен вообще.
      Если вы правильно задали частоту, то это глюки протекса

  7. тоесть строку timerDelayInit(); закоментить или удалить а частота задана в свойсвах проекта 8 мгц я правильно вас понял?

  8. мне очень не удобно вас отвлекать но может комуто еще пригодится,а может гдето как то еще инициализировать порт к которому подключены датчики

  9. ммм а каким образом это сделать я может плохо искал но не нашёл ваших координатов

  10. тоесть вот на эту почту?admin@kibermaster.net

  11. Большое спасибо всё работает! проблема была в оптимизации

  12. Доброго дня, можно вопрос — как в протеусе Вы проверяете
    работу по двум пинам?

  13. отбой вопроса сори посмотрел вложенный архив, недавно обновили! «Доброго дня, можно вопрос — как в протеусе Вы проверяете
    работу по двум пинам?»

  14. в шпротеусе вообще есть замчательная вещь!! и2с дебуггер))

  15. Что-то как-то не то… Прикладываю ключ от домофона Метаком универсальный, читает и показывает нормально (01 FF FF FF FF FF FF 2F). А вот если я прикладываю другие ключики от домофона, то показывает множество устройств и код из семи FF и CRC, то же самое будет, если закоротить на землю вход. В чем может быть причина?

    1. Наверно, им нужно паразитное питание, а эта прошивка его не обеспечивает.

  16. Пробовал подключать и 3 и 4 способами, один и несколько датчиков ds18b20, всегда находит 0 устройств (((.
    Питание не паразитное.
    Код пишу и компилирую в Atmel Studio 6.
    Подскажите пожалуйста где может быть ошибка.

    1. а прошивку настраивали для конкретного способа?

      1. Да, настраивал. Может из-за того что меня ATMega16A и библиотека просто не знает этого микроконтроллера?

  17. Огромнейшее спасибо автору за чудо-библиотеку.
    Применил к своему устройству — работает на ура, но все-таки нашел глюк в конвертировании температуры. В файле ds18x20.c написал так:
    themp[2] = data[0]&0xf;
    themp[2] *= 0.625;//дробная часть будет от 0 до 9
    if (data[1]>0xFB){
    themp[2] = 10-themp[2];//при минусовой тоже глюк
    themp[1] = 127-themp[1];
    themp[0] = ‘-‘;

  18. Проблема так же как и у zoomerland
    Настроил под Atmel Studio 6, себе вставил только поиск устройств. При компиляции в протеусе пин даже не дёргается. Всегда найдено 0 устройств. Железо будет готов через неделю-полторы, поэтому пока проверяю только в протеусе.
    Может есть какой-то нюанс совместимости кода.

    1. если скомпилировалось без ошибок, то вообще то, должно работать. Возможно, вы схему в протеусе не правильно собрали? Попробуйте с одним устройством на шине

      1. Компилируется без ошибок и предупреждений.
        Есть только лог.0 длительностью 60мкс. и всё. Он при каждой попытке найти устройства.
        Как delay.h завязан на формировании задержек? У меня подключён стандартный delay.h и указана частота 8МГц. Ну в протеусе соответственно указано что контроллер тактируется от 8МГц.

        1. получается, что ведущий формирует ресет (устанавливает 0 на 60 мкс), но термодатчик не отвечает, значит проблемы в нем

  19. Не работало сначало в Avre 5. долго мучался пока не разобрался с delay.h . очень чувствительна к задержкам.

  20. Скачал архив, сразу же запустил в Proteus’е. РАБОТАЕТ!!!
    Открыл проект в AVR Studio 4 и, не меняя код, скомпилировал. Возвращаюсь в Proteus — «Найдено 0 устройств». Что я сделал не так?)

    1. а что используется в качестве компилятора? Winavr или тулчейн?

      1. ЗАРАБОТАЛО!!! 🙂

        Решение:
        — снёс Toolchain
        — скачал свежую версию с офф.сайта
        — установил

  21. Я попытался перенести ваш код на at90usb162, однако как я понял там нет U2X. Возможно ли переделать Ваш код чтобы он заработал на этом камне?

    1. по-мойму, у этого МК нужно указывать U2X0.
      PS: несколько лет назад работал и ним, проблем там было много, советую применить другой МК

      1. К сожалению такого регистра там нет.

        1. лучше возьмите другой мк)) Кстати, at90usb162 не поддерживался WinAVR и хз, поддерживается ли сейчас тулчейном

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

  22. Здравствуйте. Скажите пожалуйста, «Так же, если используете эмуляцию 1-wire через USART, то обязательно устанавливайте не только DDR пина TX, а и PORT, иначе обмен на шине будет запускаться максимум в 30% случаев.» — в каком месте это делается?
    Если здесь:
    #ifdef UART_AS_OneWire
    #define USART_BAUDRATE_57600 (((F_CPU / (57600 * 16UL))) — 1)
    #define USART_BAUDRATE_115200 (((F_CPU / (115200 * 16UL))) — 1)
    #define USART_BAUDRATE_9600 (((F_CPU / (9600 * 16UL))) — 1)
    #else
    #include
    #define OW_DDR DDRD
    #define OW_PORT PORTD
    #define OW_PIN PIND
    #ifndef OW_TWO_PINS
    #define OW_BIT 1
    #else
    #define OW_BIT_OUT 1
    #define OW_BIT_IN 0
    #endif
    #endif
    то то, что касается пинов в случае использования USART не используется же…
    Ни как не удается запустить (

      1. Увы, не помогло =( Здесь
        char DS18x20_StartMeasure(void)
        {
        //Reset, skip ROM and start temperature conversion
        if (!OW_Reset()) return 0;
        функция сброса возвращает ноль, хотя и сам датчик и усилятор на мосфетах работают — проверено на USB-UART адаптере. Да и на анализаторе видно, что МК формирует сброс, датчик присутствие обозначает.

        1. Покажите код инициализации и укажите МК и частоту

          1. В main`е так:
            #define XTAL 8000000L
            #define baudrate 9600L
            #define bauddivider (XTAL/(16*baudrate)-1)
            #define HI(x) ((x)>>8)
            #define LO(x) ((x)& 0xFF)

            UBRRL = LO(bauddivider);
            UBRRH = HI(bauddivider);

            UCSRA = 0;
            UCSRB = 1<<TXEN;
            UCSRC = 1<<URSEL|1<<UCSZ0|1<<UCSZ1;
            Частота, соответственно 8 МГц, atmega16.

          2. а инит ножек МК? Кстати, каким способом подключен 1-wire?

          3. Ножки — как вы и подсказали, сразу за uart`ом — DDRD = 0b00000010; PORTD = 0b00000010; (при PORTD = 0b00000000 картина та же) Подключил по схеме с полевиками, по трем проводам, только BSS138 использовал.

          4. подключите по второй схеме (переделывать не много) и добейтесь стабильной работы. Советую для этого использовать код из статьи

          5. Увы, все варианты пробовал, видно не судьба. Буду свой вариант пилить, спасибо за то, что ответили! =)

  23. Здравствуйте. Помогите пожалуйста разобраться. Собрал проект в Winavr, используется один пин, 8 мгц, запустил на atmega8 — работает как положено. Но мне нужно это все запустить на atmega88, я пересобрал для это мк, подключил к соответствующему пину — устройств не находит, в процедуре OW_Reset переменная status получает значение 0x08, а должен быть, как я понял, ноль. Где может крыться проблема? Спасибо!

      1. Сейчас сконфигурировано так:

        //#define UART_AS_OneWire
        //#define OW_TWO_PINS

        #define OW_DDR DDRB
        #define OW_PORT PORTB
        #define OW_PIN PINB
        #define OW_BIT 3

        DDRB = 0b00000000; PORTB = 0b00000000;
        DDRC = 0b00000000; PORTC = 0b00000000;
        DDRD = 0b00000000; PORTD = 0b00000000;

        Я где-то что-то упустил?

          1. По третьему способу, только к пину PB3, два датчика DS18B20, на atmega8 все отлично работает, меняю в Makefile на atmega88, в протеусе меняю мк, ставлю частоту, подключаю так же — не работают. Где-то что-то не правильно делаю?

          2. Если не ошибаюсь, то в 88 есть фьюз, который делит частоту на 8 и по умолчанию он включен

          3. Спасибо, не внимательность… Скажите, а можно это все запустить на 1 Мгц ?

          4. У меня есть проект, который работает на 1 Мгц, хотелось бы этот код туда добавить. Там сейчас есть работа по 1-wire, но только с одним устройством. Это очень сложно? UART мне не нужен

          5. Если ставлю в Makefile F_CPU = 1000000, компилирую, в мк ставлю деление на 8, данных не получаю, сейчас попробую сравнить дебагом..

  24. думаю, что из-за такой малой частоты не корректно работают микросекундные задержки. Сделай через UART

    1. В проекте, в который я хочу добавить этот код, UART уже занят, там много чего занято. А почему задержки могут не корректно работать? ведь util/delay.h работает в зависимости от F_CPU . Спасибо.

      1. как раз он работает зависимо от F_CPU. При такой малой частоте мк просто не успевает сделать задержку.

  25. Скажите пожалуйста, как быть с atmega64 при работе через USART? У него в регистре UCSRnC нету бита URSEL. Спасибо.

    1. а где, собственно, вы увидели использование бита URSEL?

      1. void USART_init()
        {
        // Set baud rate
        UBRRH = 0;
        UBRRL = 51;
        UCSRA = 0;
        // Enable receiver and transmitter
        UCSRB = (1<<TXEN);
        // Set frame format
        UCSRC = (1<<UCSZ1) | (1<<UCSZ0) | (1<<URSEL);
        }

        1. Если используете USART в качестве 1wire, то это функция вовсе не нужна

          1. Спасибо, убрал, запустил на Atmega64, через USART0, способ 2, 8 Мгц. Добавил проверку в цикл. Порты сконфигурированы так: DDRF = 0b00000010; PORTF = 0b00000000; Оставил два датчика температуры DS18B20. Тестирую в Протеусе. Через некоторое, всегда разное, время программа виснет на строке while(!CheckBit(UCSR0A, RXC)); в процедуре OW_ReadBit, а в протеусе, в этов время, иногда падает ошибка (AVR USART 0) RX Frame Error. В чем может быть проблема? Еще раз спасибо.

          2. такое случается когда датчик не отвечает. Попробуйте на реальном устройстве

          3. Иногда виснет на while(!CheckBit(UCSR0A, RXC)) OthersTasks(); в процедуре OW_WriteBit без ошибок в протеусе. С чем это может быть связано?

          4. я сказать не могу, так как нужно протестировать на реальном устройстве. Попробуйте еще отключить сторожевой таймер

  26. Подскажите, надо ли делать какой-то сброс состояния датчика после измерения? Вычитываю данные ds18b20 в цикле и с большой долей вероятности через раз в data[0] находится 0xff. Пауза 1с в цикле не помогла.

    Еще интересует почему на схеме в протеусе используются два пина PB0 и PB1 в статье только PB0? Дело в том что на одном пине у меня не завелся датчик, а на двух и с OW_TWO_PINS завелся, но пока что с проблемами (см выше).

    1. На схемах изображено несколько вариантов подключения, в протеусе — один с них (не помню какой).
      Советую вам использовать код из этой статьи, если заведется, то переносите его в ваш проект.
      А эта пауза 1с у вас между запуском измерения и считыванием данных?

      1. Код точно такой же, за исключением за исключением датчика, подключен у другому порту, поэтому в openwire.h поставил
        #define OW_DDR DDRC
        #define OW_PORT PORTC
        #define OW_PIN PINC

        1с это пауза перед следующим измерением (в конце бесконечного цикла), а между запуском и считыванием как и в статье 800мс.

        1. а каким из вышеперечисленных способов подключен 1wire?

          1. Вот так
            http://imageshack.us/a/img27/6406/lsst.png

            Если использовать классическую схему с одним проводом и резистор 4К7 на «+», то датчик не находится (#define OW_TWO_PINS комментировал).

            В протеусе только в самом начале проскакивает 0xff.

          2. Такс, а частота мк у вас какая?
            OW_BIT_OUT и OW_BIT_IN указаны?

          3. F_CPU = 8000000
            В SinaProg выставил Fuses: Int. 8MHz

            #define OW_TWO_PINS

            #define OW_DDR DDRC
            #define OW_PORT PORTC
            #define OW_PIN PINC

            #define OW_BIT_OUT 1
            #define OW_BIT_IN 0

          4. а в самом протеусе в настройках мк установили частоту 8мгц?

          5. Вроде все правильно. Попробуйте DS18x20_StartMeasureAddressed(owDevicesIDs[i]); заменить на DS18x20_StartMeasure();
            Я спать, если что, завтра отвечу.

          6. В общем ситуация с DS18x20_StartMeasure() не изменилась.
            Попробую еще раз пересмотреть схему, возможно что-то упустил. Если не получится — придется использовать в качестве датчика температуры термистор 🙂
            В любом случае спасибо большое за помощь!

          7. Вроде победил, проблема была в использовании прерываний для динамической индикации (о чем собственно и написано в статье.)
            Решилось вешаньем датчика на USART. По крайней мере теперь в протеусе нет глюков. Осталось проверить на макете.
            Еще раз спасибо!

  27. Здравствуйте, искал адекватную библиотеку для работы по 1-wire, ваша понравилась очень, но запустить никак не выходит. Работаю сразу в железе.
    Инициализация:
    // Максимальное количество устройств на шине
    #define MAXDEVICES 8

    // Если для эмуляции шины используется USART
    //#define UART_AS_OneWire

    // Если для эмуляции 1-wire не спольльзуется USART, но используется 2 пина (вход и выход)
    //#define OW_TWO_PINS

    #ifdef UART_AS_OneWire
    #define USART_BAUDRATE_57600 (((F_CPU / (57600 * 16UL))) — 1)
    #define USART_BAUDRATE_115200 (((F_CPU / (115200 * 16UL))) — 1)
    #define USART_BAUDRATE_9600 (((F_CPU / (9600 * 16UL))) — 1)
    #else
    #include
    #define OW_DDR DDRA
    #define OW_PORT PORTA
    #define OW_PIN PINA
    #ifndef OW_TWO_PINS //если используется один пин, укажите его номер
    #define OW_BIT 0
    #else // если используются 2 пина, укажите их номера
    #define OW_BIT_OUT
    #define OW_BIT_IN
    #endif
    #endif

    частота проинициализирована как #define F_CPU 8000000UL

    Ссылка на архив с проектом: https://dl.dropboxusercontent.com/u/34620857/MQ.rar
    Заранее спасибо!

      1. Не находит устройства, хотя на шину повесил 3 штуки, на осцилографе на шине меандр, низкий уровень — 60 мкс, высокий — 580 мкс, по коду проследить не удалось причину этого явления, вроде все правильно

      2. Решил проблему. у меня 6.1 студия и для адекватной работы нужно было сделать глобальный дефайн опорной частоты процессора, иначе onewire либа думала что частота 1мгц.

  28. void DS18x20_ConvertToThemperature(unsigned char* data, unsigned char* themp)
    {
    unsigned int tmp = 0;
    if ((data[0]==0x00)&&(data[1]==0x00)){
    themp[0] = ‘ ‘;
    }
    else if(data[1]>0xFB){
    themp[0] = ‘-‘;
    tmp = ((unsigned int)data[1]<>8;
    }
    else themp[0] = ‘+’;
    //Store temperature integer digits and decimal digits
    themp[1] = ((data[1]&7)<>4);
    //Store decimal digits
    themp[2] = (data[0]&15);
    themp[2] = (themp[2]<<1) + (themp[2]<>4);
    }
    подправил,при минусовых значениях разница в 1 была

  29. void DS18x20_ConvertToThemperature(unsigned char* data, unsigned char* themp)
    {
    unsigned int tmp = 0;
    if ((data[0]==0x00)&&(data[1]==0x00)){
    themp[0] = ' ';
    }
    else if(data[1]>0xFB){
    themp[0] = '-';
    tmp = ((unsigned int)data[1]<>8;
    }
    else themp[0] = '+';
    //Store temperature integer digits and decimal digits
    themp[1] = ((data[1]&7)<>4);
    //Store decimal digits
    themp[2] = (data[0]&15);
    themp[2] = (themp[2]<<1) + (themp[2]<>4);
    }

      1. с отображением кода на сайте, не знаю почему)

  30. Подскажите пожалуйста, использую Вашу библиотеку через USART, работает все довольно стабильно и хорошо. Теперь возник вопрос, подключения питания к датчику не отдельным проводом +5В,а брать питание от линии данных, как это возможно реализовать?
    Спасибо.

  31. Может возможно после передачи запроса на запуск измерения подтягивать 1 на ножку, чтобы по линии данных шли 5В, а по достижению 750мс читать?

    1. насколько помню, так и нужно делать, посмотрите в даташите.

      1. да, но сама библиотека будет работать? Т.е используя USART я могу после подачи запроса на измерение перевести ногу (RX) в 1 (как в даташите написанно не познее чем 10мс), подождать 750мс, перевести обратно и читать данные?

        1. нужно просто передавать ноли в порт.

          1. можно пожалуйста поподробнее?
            Пробовал проверить работу в протеус, но незнаю там как не подключи они читаются, даже если Vss и GND не подключать.

          2. для того, чтобы питать устройство через DQ, нужно чтобы он был постоянно в высоком уровне, для этого нужно посылать OW_WriteByte(0xFF).

          3. Как часто посылать? А не легче просто поставить на время задержи по измерению темп порт в 1, чем посылать постоянно OW_WriteByte(0xFF).
            Спасибо

          4. Смотрю в протеусе, у меня и так выходит что после получения и всех монипуляций, все время на DQ подается 5в, ну т.е порт в 1 стоит.

          5. можно, но для этого нужно отключить usart

          6. Ага, а если будем слать команду постоянно OW_WriteByte(0xFF), как часто ее слать?
            Подскажите, для чего именно OW_WriteByte(0xFF).

            Спасибо

          7. постоянно слать, пока идет измерение. Для чего она, писал выше

          8. Ага, понял. Будем слать. А не подскажите еще пожалуйста, как в протеусе можно проверить работу через паразитное питание? то при отключении GND и Vss всеравно все работает, они как я понял питаются через скрытое питание как на МК например.

  32. наверно, никак, можете разве что в ноге DQ подключить осциллограф и посмотреть, есть ли высокий уровень во время измерения.

    1. Спасибо. Пока попробовать в живую нет возможности, нахожусь не дома, а вот протеус и его осциллограф показывает что после получения и отправки команд, нога находиться в 1, и поступает 4,999вольта

  33. Какие файлы нужно добавить в свой проект (Atmel studio 6) для использования библиотеки. Какими функциями пользоваться?

  34. есть библиотека для работы с DS2413?
    какие вообще у вас есть библиотеки по даласу кроме DS18x20?

  35. Скажите как обрабатывать данные с двух датчиков DS18B20 подключенных к одной шине?

      1. Можно по поподробней, это значение взамен owDevicesIDs[i] ?

  36. Здравствуйте!Возникает странная проблема,при подключении микроконтроллера к компу с помощью преобразователя FT232 и открытии порта из значимых данных приходит только серийный номер,а вместо значения температуры символ ‘?’.Тестил только на железе,МК AtMega32.Такая же история наблюдалась с датчиком SHT75,но с другой программой.Помогите,пожалуйста,разобраться,в чем может быть проблема такого поведения.

    1. Попробовать другой преобразователь. Я вообще FT232 не применяю из-за его глюков, вместо него применяю CP2102

      1. Он у меня на PinBoard-e стоит,в будущем буду пускать через ENC28J60 сигнал от датчиков по сети,пока хотел отладить по uart-у,попроще думал будет.Спасибо за ответ,попробую сразу через сеть,т.к. cp2102 в наличии нет,а enc уже на плате и работает(тестил udp),может так что путное выйдет!

  37. Для начала хочу сказать спасибо за лаконичную и функциональную библиотеку, аналоги этими качествами не блещут =)

    У меня небольшая проблема:
    в протеусе UART-режим работает крайне нестабильно, при поиске устройств на шине с двумя датчиками часто либо зависает ( while(!CheckBit(UCSRA, T/RXC)) ), либо не находит ни одного (OW_DATA_ERR), либо находит один. Иногда срабатывает как надо. В софтварном режиме всё работает отлично. У вас случаем нет мыслей по поводу того, в чём может быть затык?

    Заранее признаетелен.

    1. поторопился я с выводами, в софтварном режиме тоже нестабильно 🙁 не виснет, но находит от нуля о двух

      1. я тебе больше скажу. в железе оно тоже не хило так сбоит )

        1. Ребят, не знаю, что у вас там сбоит, но у меня работает нормально. Правда, я всегда использую подключение через 2 транзистора и резистор на 1,5К.

    2. А версия протеуса какая? И как вы проверяли в UART режиме?

      1. Тоже Proteus 7.10 SP0. Проверить не получается нормально, ибо инициализация проходит раз из пяти.

    3. Аналогичная проблема, в Proteus 7.10 SP0, в режиме UART виснет на ( while(!CheckBit(UCSRA, RXC)) ), в обычном режиме нормально, иногда проскакивают ошибочные значения. На железе через UART пока не проверял.

          1. Получается, глюк протеуста, так как у меня нормально работает.
            В реальном устройстве я ставлю резистор не 4,7, а 2,2К. А если использую подключение через транзисторы, как в первом варианте, то вместо 4,7К ставлю 1,5К. У меня так работает не меньше сотни устройств.

          2. Проверил на железе через UARТ, вроде всё отлично!

  38. Взял последний код из прикрепленного архива, у меня почему-то первое значение считывается 85, а потом правильное. С обоих датчиков.

    1. 85 — это значение темепратуры при инициализации. Тоесть, если подать питание и считать, не запустив измерение, будет получено 85

      1. Мне не понятно почему оно считывается в таком случае

        // работа с устройствами 1-wire
        void sensors(void){

        timerDelayInit();
        nDevices = search_ow_devices(); // ищем все устройства

        for (unsigned char i=0; i<nDevices; i++){ // теперь сортируем устройства и запрашиваем данные
        // узнать устройство можно по его групповому коду, который расположен в первом байте адресса
        switch (owDevicesIDs[i][0]){
        case OW_DS18B20_FAMILY_CODE: { // если найден термодатчик DS18B20
        DS18x20_StartMeasure(owDevicesIDs[i]); // запускаем измерение
        timerDelayMs(800); // ждем минимум 750 мс, пока конвертируется температура
        // _delay_ms(800);
        unsigned char data[2]; // переменная для хранения старшего и младшего байта данных
        DS18x20_ReadData(owDevicesIDs[i], data); // считываем данные
        float themperature = DS18x20_ConvertToThemperatureFl(data); // преобразовываем температуру в человекопонятный вид
        temper[0+i]=themperature;
        break;
        }
        }
        }
        }

        а если использовать _delay_ms(800); вместо timerDelayMs(800); и timerDelayInit(); все нормально

        Не судите строго, я новичок)

        1. понял, потому, что timerDelayMs не работает у меня

  39. Если использовать _delay_ms вместо timerDelayMs все нормально, для чего был добавлен timerDelayMs?

    1. Для задержки с помощью таймера, чтобы во время задержки можно было что то делать.

  40. У меня еще один вопрос, как выполнить быстрое прерывание цикла с запуском процедуры считывания температуры. Я разрешаю прерывание по кнопке, но оно срабатывает с третьего раза.

    1. Этого лучше не делать, так как считывание занимает довольно продолжительное время.

      1. Но считывание температуры не главная функция моего устройства, должен ведь быть способ прервать цикл, пусть даже один раз и считается не верное значение.

        1. Или лучше делать 1-wire на отдельном МК и связать их по какому-либо протоколу?

          1. Подскажите пожалуйста как лучше сделать.

          2. Почему нельзя постоянно измерять температуру? Запускаешь измерение, затем 750 мсек тупишь в цикле, выполняя остальные задачи, потом читаешь измерения и так по кругу. Моментально получить текущее реальное значение температуры все равно не получится из-за температурной инертности датчика (если ты его греешь не газовой горелкой).

  41. Доброго Здоровья!

    Kibermaster!
    Если дляВас не сильно обременительно, не могли-бы Вы пересобрать пример с использованием AVR Studio 4.19 с задействованным вариантом работы с 1Ware по USART?

      1. Дело в том, что я пишу в среде Bascom, с «Си» не знаком абсолютно. Единственное, что стоит, так это AVR Studio 4.19, да и то, только, как программатор.

        Прошу прощения за безпокойство, уже не надо. Написал реализацию работы сам, согласно документации «#AN124 (Using a UART to Implement a 1-Wire Bus Master)».

        Если Вас не сильно затруднит, алгоритм поиска ID устройств больше 1? Что-то этот момент никак не хочет поддаваться. Принимаю всё время «1», хотя в то-же время, поиск и чтение одного устройства, работает без проблем.

  42. Вопрос может показаться глупым, но прошу простить, я начинающий.
    DS18x20_ReadData(owDevicesIDs[i], data); // считываем данные
    float t = DS18x20_ConvertToThemperatureFl(data); // преобразовываем температуру в человекопонятный вид
    printf(": %3.2f C", t);

    Если не ошибся, переменная t и есть значение температуры. Это значение нужно вывести на LCD дисплей с помощью функции lcd_string(0x93, ); Как это можно осуществить?

    1. Вместо последней строки:
      char str[20];
      sprintf(str, «t = %3.2f C», t);
      lcd_string(0x93, str);

  43. Спасибо! Правильно понял, что это перевод из float в char? Правда на дисплей все-равно не выводится, черные квадраты вместо значений (остальные слова и символы выводятся нормально).

    1. это переводит число в строку, почему на дисплее не видно — смотрите функцию lcd_string

  44. Перевел Вашу библиотеку в CodeVisionAVR. Эмуляция USARTом. Схема 2. Запуск измерения DS18x20_StartMeasure, программная задержка 750 мс — тогда работает. Если запуск измерения делаю по прерыванию INT1 (запуск от внешнего таймера 1Гц) — перестает работать. Другие функции в прерывании тоже перестают обрабатываться. Идея — раз в секунду запускать измерение (не надо ждать 750 мс, пока конвертируется температура), а потом вывод на LCD. Подкажите, пожалуйста, в чем может быть дело.

    1. В функции запуска измерения есть цикл while, для ожидания приема данных, поэтому использовать функцию в прерывании нельзя

  45. Замечательная библиотека, отлично работает. Но подключил датчик DS18B20 с паразитным питанием и не работает. Пробовал ардуиновскую бибилиотеку, работает. Что нужно поменять в библиотеке?

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

  46. Добрый день. Большое спасибо вам за библиотеку.

    У меня такая проблема при подключении.

    Подключаюсь методом 3 (одна ножка МК работает в качестве и входа, и выхода). Питание не паразитное. Найдено 1 устройство, как и ожидалось, но вот вместо температуры возвращается пустое значение:

    ———- Found 1 devices ———-
    28 61 4A AB 06 00 00 52 — Thermometer DS18B20: ? C

    Можете ли подсказать, в чем может быть причина?
    Спасибо.

    1. Поправка. Возвращается не пустое значение. Сделал вывод первых 2х байтов, вот что получилось:

      28 FF C5 1B A3 15 03 F7 — Thermometer DS18B20: data[0]: cd, data[1]: 1
      28 FF C5 1B A3 15 03 F7 — Thermometer DS18B20: data[0]: cd, data[1]: 1
      28 FF C5 1B A3 15 03 F7 — Thermometer DS18B20: data[0]: cc, data[1]: 1
      28 FF C5 1B A3 15 03 F7 — Thermometer DS18B20: data[0]: cc, data[1]: 1
      28 FF C5 1B A3 15 03 F7 — Thermometer DS18B20: data[0]: cc, data[1]: 1

  47. В функции search_ow_devices выполняем поиск устройств пока diff != OW_LAST_DEVICE && sensors_count < MAXDEVICES. При этом OW_LAST_DEVICE всегда = 0, соответственно и вся правая часть всегда = 0? Я правильно понял?
    Проблема — находит только одно устройство…

    1. Как я понимаю, если 0, значит нового устройства не найдено.

    1. Почитайте доку на DS18B20, многое станет ясно)))
      PS: есть на русском.

  48. Читал, и не раз. И на разных языках..
    Хочется понять этот код. Он несколько шире чем DS18B20.

  49. А Вы за основу что брали? Какой AppNote?

    1. Это было лет 5 назад, я уже не помню))).
      Данный код использую во всех твоих разработках, где есть OW, там поиск всегда происходил без сбоев со стороны программной части

  50. Через USART у меня тоже все работает. Теперь пробую через DS2482-100. Возникли проблемы, стал подробно изучать код. Появились дополнительные вопросы. Сами как думаете, какая идея заложена в diff != OW_LAST_DEVICE && sensors_count < MAXDEVICES ?

    1. sensors_count < MAXDEVICES думаю, понятно))). diff != OW_LAST_DEVICE нужен для обнаружения последнего найденного устройства.

  51. Совершенно бы согласился. Но OW_LAST_DEVICE константа…

    1. Я в курсе, и если diff равно 0 (этой константе), то новых устройств не обнаружено и цикл завершается

  52. Тогда условие цикла сокращается до diff != 0.
    diff — номер бита последнего несоответствия (от 64 до 1). Ниже 2, а тем более = 0, diff как бы и не может… И цикл получается бесконечным. Но на деле он завершатся. Как не пойму.

  53. Подскажите почему при компиляции Вашего проекта все нормально работает, но переношу Ваш код к себе в проект ругается на delay.c не определен F_CPU, в этом файле пишу F_CPU 8000000UL компилируется но не работает пишет foud 0 device в консоле протеуса? Может где то нужно глобально объявить я новичек подскажите пожалуйста?

    1. F_CPU надо определить в настройках проекта

  54. Здравствуйте! Не могу вывести доли градусов на семисегментный индикатор. Целые числа выводятся правильно. Пробовал так: raz4 = ((temp & 0x0F)*625/1000);

  55. У меня atmega32 макетная плата+lcd1602. Нужно прикрутить датчик ds18b20. Пишу на С в Atmel Studio 4. Но как-то все не получается. Очень нужна помощь!!! У кого есть рабочий пример на С??? Что-то я на этом датчике затормозился. Пример куча в интернете, но что-то не как работать их заставить не могу.
    Нужно помощь….

    1. В статье как раз рабочий пример на си)))

  56. Доброго Здоровья!

    Если Вам не сильно затруднительно, не могли бы Вы пересобрать исходники с USART вариантом работы с Ds18b20.

    Спасибо!

  57. А здесь случайно не ошибка у Вас:
    #ifdef UART_AS_OneWire
    #define USART_BAUDRATE_57600 (((F_CPU / (57600 * 16UL))) — 1)
    #define USART_BAUDRATE_115200 (((F_CPU / (115200 * 16UL))) — 1)
    #define USART_BAUDRATE_9600 (((F_CPU / (9600 * 16UL))) — 1)
    #else
    #include
    #define OW_DDR DDRC
    #define OW_PORT PORTC
    #define OW_PIN PINC
    #ifndef OW_TWO_PINS //если используется один пин, укажите его номер
    #define OW_BIT 0
    #else // если используются 2 пина, укажите их номера
    #define OW_BIT_OUT 1
    #define OW_BIT_IN 0
    #endif
    #endif

    Если задан макрос #define OW_TWO_PINS то мы используем 2 пина, для интерфейса 1-wire, так же,
    а у вас написано:
    #ifndef OW_TWO_PINS //если используется один пин, укажите его номер
    #define OW_BIT 0
    #else // если используются 2 пина, укажите их номера
    #define OW_BIT_OUT 1
    #define OW_BIT_IN 0
    #endif
    то есть если есть макрос #ifndef OW_TWO_PINS то задаётся номер пина контроллера на котором будет 1-wire, а должны задавать если есть этот макрос не один, а два пина, а одни задавать если этого макроса нет.

    Может должно быть вот так:
    #ifndef OW_TWO_PINS //если используется один пин, укажите его номер
    #define OW_BIT_OUT 1
    #define OW_BIT_IN 0
    #else // если используются 2 пина, укажите их номера
    #define OW_BIT 0
    #endif

  58. Великодушно простите, там написано #ifndef всё верно!

Оставьте комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *