AVR Урок 20. Подключаем датчик температуры DS18B20. Часть 3



 

Урок 20

Часть 3

 

Подключаем датчик температуры DS18B20

 

В предыдущей части нашего занятия написали ряд функций для работы с датчиком DS18B20, являющимся цифровым датчиком измерения температуры.

Также мы поняли, что кроме того, что мы будем из датчика читать какие-то данные, нам придётся ещё в него и посывать какие-то данные. Одним из примером может служить передача какой-нибудь определённой команды.

Поэтому, давайте напишем функцию передачи байта в датчик, в её теле мы созадим локальную переменную для дальнейшего цикла передачи каждого байта

 

//функция записи байта на устройство

void dt_sendbyte(unsigned char bt)

{

  char i;

}

 

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

 

//функция записи бита на устройство

void dt_sendbit(char bt)

{

  char stektemp=SREG;// сохраним значение стека

  cli(); //запрещаем прерывание

}

 

Затем смотрим в диаграмму записи бита в устройство, которую мы смотрели в даташите датчика в предыдущей части. В ней видно, что мы должны также притянуть шину к земле, подождать минимум 1 микросекунду, мы опять же подождём две. Это нужно для того, чтобы датчик «успел понять» что шина притянулась.

 

cli(); //запрещаем прерывание

DDRTEMP |= 1<<BITTEMP; //притягиваем шину

_delay_us(2); //задержка как минимум на 2 микросекунды

 

Дальше мы уже будем решать, что именно мы будем посылать. если будем посылать 0, то мы шину отпускать не будем, а, если нам нужно послать 1, то будем шину отпускать

 

_delay_us(2); //задержка как минимум на 2 микросекунды

if(bt)

     DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину

 

Дальше мы ждём от 60 до 120 микросекунд, ну, подождём 65, как обычно. То есть если мы предаём ноль, то мы просто выжидаем с притянутой шиной данный временной интервал, а если передаём единицу, то делаем то же самое, но отпустив перед этим шину, так как сработает условие в коде. Затем мы отпускаем шину. То есть, если мы передаём единицу, то она уже у нас отпущена, если ноль, то отпускаем. ну и затем возвращаем сохраненное значение в стек

 

  if(bt)

      DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину

  _delay_us(65); //задержка как минимум на 60 микросекунд

  DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину

  SREG = stektemp;// вернем значение стека

}

 

Теперь продолжим, используя данную функцию, функцию записи (или отправки) байта в шину

 

  char i;

  for(i=0;i<8;i++)//посылаем отдельно каждый бит на устройство

  {

    if((bt & (1<<i)) == 1<<i)//посылаем 1

        dt_sendbit(1);

    else //посылаем 0

        dt_sendbit(0);

  }

}

 

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

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

 

 

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

 

if(dt_testdevice()==1) //если устройство нашлось

{

  dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине

 

Затем мы уже отправляем команду на считывание температуры

 

dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине

dt_sendbyte(T_CONVERT); //измеряем температуру

 

Данная команда — это только команда, а не само считывание температуры. По данной команде датчик начнёт измерение температуры и конвертацию её в цифровой код и занесение его в регистровую пару. Данный процесс у датчика длится значительно долго, около 750 милисекунд, не микро. Данное время необходимо, чтобы датчик успел записать все 12 бит

 

image09

 

Здесь мы видим, что если нам особая точность не нужна, то мы можем считать другое количество бит, тем самым сэкономив значительное количество времени. Но мы напишем идеально 750, так как мы никуда не спешим

 

dt_sendbyte(T_CONVERT); //измеряем температуру

_delay_ms(750); //в 12битном режиме преобразования — 750 милисекунд

 

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

 

_delay_ms(750); //в 12битном режиме преобразования — 750 милисекунд

dt_testdevice(); //снова используем те же манипуляции с шиной что и при проверке ее присутствия

dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине

 

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

 

dt_sendbyte(NOID); //пропустить идентификацию, тк у нас только одно устройство на шине

dt_sendbyte(READ_DATA); //даем команду на чтение данных с устройства

 

 

Затем мы считываем байты с регистра и укладываем их в переменную tt, потом, выйдя из тела условия, возвращаем их.

 

    dt_sendbyte(READ_DATA); //даем команду на чтение данных с устройства    bt = dt_readbyte(); //читаем младший бит

    tt = dt_readbyte(); //читаем старший бит MS

    tt = (tt<<8)|bt;//сдвигаем старший влево, младший пишем на его место, тем самым получаем общий результат

  }

  return tt;

}

 

Мы получили хоть и не раздельный результат в виде двух различных байтов, но все равно результат этот сырой, так как в нём есть и биты знака, и также шестнадцатые доли градуса, которые мы не можем показать на дисплее. Поэтому нужна будет ещё одна функция конвертирования результата в удобный для глаза вид.

Мы вообще упростим нашу задачу и не будем использовать доли, а будем использовать только целые показания, и бит отрицания мы также не будем использовать, мы будем измерять только положительные температуры. Вот такая поэтому будет простая у нас функция

 

//преобразование температуры в единицы

char converttemp (unsigned int tt)

{

  char t = tt>>4;//сдвиг и отсечение части старшего байта

  return t;

}

 

Напишем на данную функцию прототип в хедер-файле

 

int dt_check(void); //функция преобразования показаний датчика в температуру

char converttemp (unsigned int tt); //преобразование температуры в единицы

 

Теперь мы, наконец-то вернёмся в наш главный модуль в его главную функцию main(), а то мы уже тут очень давно не были. Создадим здесь локальную переменную для хранения показаний температуры

 

int main(void)

{

  unsigned int tt=0; //переменная для хранения температуры

 

Напишем код для вызова функции измерения температуры в бесконечном цикле

 

sendcharlcd(sec%10+0x30);//Преобразуем число в код числа

sendcharlcd(' ');

tt = converttemp(dt_check()); //измеряем температуру

 

Отобразим показания температуры на дисплее

 

  tt = converttemp(dt_check()); //измеряем температуру

  sendcharlcd(tt/10+0x30);//Преобразуем число в код числа

  sendcharlcd(tt%10+0x30);//Преобразуем число в код числа

  sendcharlcd('*');

  sendcharlcd('C');

}

 

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

 

image10_0500

 

Посмотрим также поближе модуль с припаянным датчиком

 

image12

 

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

 

image11

 

Соберём код и прошьём контроллер. А вот и результат

 

image13

 

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

 

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

 

Техническая документация на датчик DS18B20

 

Программатор, датчик температуры DS18B20 на плате и дисплей можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

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

Дисплей LCD 16×2

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR Подключаем датчик температуры DS18B20

Один комментарий на “AVR Урок 20. Подключаем датчик температуры DS18B20. Часть 3
  1. Алексей:

    А как быть если точность не нужна, а нужна скорость. Как снизить до 9 бит. Никак не выходит!

    • Да легко!
      Не использовать младшие 3 бита и всё и посчитать по даташиту, какая должна быть без них задержка

      В регистры биты записываются последовательно, начиная со старшего. Это принцип АЦП.

  2. Андрей:

    а для отображения отрицательных и положитьельных выше 99 градусов какие преобразования нужны?

  3. Дмитрий:

    Не интересно…Хотелось бы полнее! Долю градуса, знак, несколько устройств! Уважаемый Автор, не соизволили бы дополнить урок?

  4. Роман:

    Как подключить два датчика температуры?

    • Можно параллельно, можно каждый к своей ножке порта. По параллельному подключению нескольких датчиков есть урок 94 для STM. Пусть это другой контроллер, но принцип, думаю, поймёте. Я там рассказываю много об адресации и считывании ROM-кодов для дальнейшего обращения по ним к датчикам.

  5. Рома:

    Очень интересно и познавательно! Ч то на етом сайте что на канале. С 0 тут научился кое как программировать аврки . но вот вопрос уменя к етим строчкам _delay_ms(750); //в 12битном режиме преобразования — 750 милисекунд. Я так понимаю ета задержка останавливает весь код (не только в библиотеке DS18B20) почти на 1 сек (0.75) сек + 0.05 мс остальные задержки . Отсюда у меня возникают проблемы так как и использую не LSD а LED 7-ми сегментный 3 разряда реализовал я его на TC2 прерывания у меня управляют частотой смены регистров индикатора. Ну а так как задержка почти в 1 сек останавливает весь код то на дисплее получается как АВТОР говорит ерунда. Как выйти с данной ситуаци?

    • Aleksander:

      Роман, поделюсь свежим опытом.
      Точно выдерживать интервал в 750 миллисекунд нет нужды, это минимальный интервал преобразования. Таким образом, во время этой задержки можно разрешить, например, прерывание от Таймера0, которое будет осуществлять вывод на семисегментный дисплей.
      Весь остальной цикл опроса датчика занимает существенно меньше времени, поэтому прерывание на индикацию можно разрешить только на время этой задержки. Мерцания практически не будет заметно.

  6. Рома:

    Функцию опроса датчика DS18B20 гоняю в цыкле , а смену регистров дисплея в прерываниях по таймеру. В протеусе не ночень корректно работает иногда подмигивают цыфры. хотя и частоту поднял тестил в диапазоне 20-100 Гц на каждый регистр дисплея и в настройках индикатора менял обновление от 1-0.0001ms. вроде рабатает но иногда подмигивают цыфры. Может проблема протеуса?

  7. Алексей:

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

  8. Sergio:

    Спасибо за урок. По мне, то он достаточно трудный, но тем более полезно разбираться. У меня небольшое замечание (хотя может быть я не прав).
    Функция чтения бита:

    _delay_us(2); //задержка как минимум на 2 микросекунды
    DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину
    _delay_us(13);
    После данной задержки результат уже на шине должен быть.
    bt = (PINTEMP & (1<>BITTEMP; //читаем бит

    В даташите указно, что данные для считывания будут действительны в течение 15 мкс с момента когда мы притянем шину. Мы уже использовали 2 мкс и затем читаем бит после 13 мкс, т.е. на самой границе. Может быть есть смысл вместо 13 мкс задержаться на 8-10 мкс. Это место может быть тонким.

  9. Маркус:

    Спасибо за урок. Сложно и очень интересно. у меня возникла небольшая проблема: вместо показаний датчика на экран выводятся нули. Экран подключен через I2C. Подскажите пожалуйста куда копать?

    • Маркус:

      Экран здесь не при чем, сам датчик не отзывается. Датчик подключен напрямую к порту D. Не подскажете в чём может быть дело?

  10. oleg:

    Добрый вечер!
    функция чтения датчика family 0x28. На его присутствие на шине.

    void family_Reader_cycle()

    {
    uint8_t i;
    dt_testdevice();
    dt_sendbyte (READ_ROM);
    for (i=0; i<1; i++)
    {
    byte_data_family = dt_readbyte ();

    перемнная глобальная unsigned char byte_family = 0x28;

    А дальше в цикле while (1) проверяем ds18B20 на присутствие на шине и если он присутствует, то тогда читаем с него данные.

    family_Reader_cycle();
    if (byte_family == byte_data_family)
    {
    tt = converttemp(dt_check()); //измеряем температуру
    LCDGotoXY(0,1);
    LCDdata('D');
    LCDdata('S');
    LCDdata('=');
    LCDdata(tt/10+0x30);//Преобразуем число в код числа
    LCDdata(tt%10+0x30);//Преобразуем число в код числа
    LCDdata('C');
    LCDdata(' ');
    LCDdata(' ');
    }
    else
    {
    if (byte_family != byte_data_family)
    {
    LCDGotoXY(0,1);
    LCDdata('D');
    LCDdata('S');
    LCDdata('=');
    LCDdata('E');
    LCDdata('R');
    LCDdata('R');
    LCDdata('0');
    LCDdata('R');
    }
    }

  11. oleg:

    Спасибо за ваше уделенное время и труд.
    Продолжайте по avr уроки.
    Здоровья вам и удачи!!!

  12. oleg:

    Лично я ни когда не делал два DS18B20 на одну шину. Всегда использую один порт-один датчик.
    На одной шине, чревато последствиями в производственных масштабах и промышленных сетях с индуктивными нагрузками. Обжигался не один рас.

  13. Влад:

    Здравствуйте. А зачем в нескольких функциях было создавать одинаковые переменные? Это очень затрудняет разбор программы.

  14. Влад:

    Код не работает, да и автор уже почти два года как исчез. На LCD только чёрные квадраты в верхней строке.

  15. Температура выводится положительная.Отрицательная показует какие то иероглифы.?

  16. Температура в «-» не правильно отображается.

  17. ivan:

    Не правильно на дисплее отображается температура отрицательная.

Добавить комментарий

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

*