# Подключаем датчик температуры DS18B20 ![image00](https://narodstream.ru/wp-content/uploads/2016/12/Image00-9.png)Продолжаем изучать датчики компании **DALLAS**. И сегодняшним представителем будет **DS18B20**, который является датчиком температуры. Измеряет температуру данный датчик в градусах по цельсию. Измерения могут быть как 9-битной дискретности, так и 12-битной. Распределяются данные биты следующим образом. Мы будем измерять температуру в 12-битном виде и самые младшие 4 бита будут хранить показания долей градуса, а остальные старшие 8 — сами градусы. Подключается данный датчик по однопроводной технологии (1-wire). Диапазон измерений — от -55 0C до +125 0C, но наивысшая точность показаний достигается в диапазоне от -10 0C до +85 0C/ Погрешность данного датчика — 0,5 градуса, поэтому нам нет смысла использовать все младшие биты и мы их будем просто отсекать. Режим 1-wire у нас не организован аппаратно в контроллере, поэтому мы будем весь протокол программировать. Чем удобен для нас данный датчик? Удобен он тем, что мы можем его в часовой модуль, использованный на прошлых занятиях просто впаять и использовать. Для этого на модуле выведен отдельный контакт. У датчика всего 3 ножки. 2 из них ножки питания и одна — ножка данных. Данный датчик существует в двух видах корпуса — Dip и TO-92. Мы будем использовать второй тип. Таких датчиков, как пишут, на 1 провод можно повесить несколько, но я не пробовал. В связи с этим у каждого датчика есть уникальный 64-битный код, записанный в его ROM, записанного постредством лазерных технологий. Но, так как мы будем использовать только один датчик, то мы будем к нему обращаться другим способом, не используя ROM. Мы подаём на датчик команду 44h последовательным кодом, тем самым заставляя датчик конвертировать температуру. Посмотрим регистры, в которых хранится значение температуры после преобразования ![image01](https://narodstream.ru/wp-content/uploads/2016/12/Image01-10.png) В четырех младших битах младшего регистра хранятся доли градусов, в четырех старших, а также в трех младших битах старшего регистра — целые значения градусов. В остальных битах — знак. Если 0, то плюс, если 1 — то минус. Также в технической документации написано, что нужно обязательно подтянуть резистор на информационную ножку датчика к питанию. Также написан ряд временных и других характеристик, которые мы уже будем рассматривать по мере того, как будем писать код. Проект был создан новый и назван **My1820LCD**, все файлы проекта [**прошлого занятия**](https://narodstream.ru/avr-urok-19-sobiraem-chasy-na-mikrosxeme-ds1307-i-lcd-1602/) **MyClock1307LCD** были вставлены в проект, а код главного модуля также был скопирован в главный модуль нового проекта. Всё это сделано потому, что мы работаем с тем же модулем, просто мы также впаяли туда датчик температуры, а время мы считывать не перестанем, пусть часы также ходят. Добавим ещё файлы **DS18B20.c** и **DS18B20.h** для созадния библиотеки работы именно с датчиком температуры и заголовочный файл также подключим в main.h по нашему обыкновению, ну и его же подключим в одноименный c-файл. Сначала создадим функцию конвертирования температуры в файле DS18B20.c. Надобность данной функции обусловлена тем, что датчик нам передаёт показания в двух байтах в определённом виде, рассмотренном нами выше, который без преобразования будет непонятен //функция преобразования показаний датчика в температуру **int dt_check(void)** **{** **}** Создадим две переменных, которые нам пригодятся в дальнейшем в теле нашей функции int dt_check(void) {   **unsigned char bt;//переменная для считывания байта**   **unsigned int tt=0;** Сразу сделаем прототип нашей функции в заголовочном файле #include «main.h» **int dt_check(void); //функция преобразования показаний датчика в температуру** Чтобы нам дальше писать тело функции, нам будет нужна ещё функция. Напишем её выше, чтобы не создавать прототип. Данная функция будет узнавать, есть ли датчик на шине //функция определения датчика на шине **char dt_testdevice(void) //dt — digital termomether | определим, есть ли устройство на шине** **{** **}** Теперь, прежде чем писать тело данной функции, самое время посмотреть схему подключения датчика к контроллера. Посмотрим опять же упрощённый вариант в протеусе (нажмите на картинку для увеличения изображения) [![image02_500](https://narodstream.ru/wp-content/uploads/2016/12/Image02_500.png)](https://narodstream.ru///wp-content/uploads/2016/12/Image02-11.png) Мы видим, что датчик своим информационным портом подключен к ножке порта МК PD1, также мы видим подтягивающее сопротивление на 4,7 килоом с данного провода на шину питания. И вот теперь на данной ножке мы должны как-то иммитировать определённые имплуьсы. То есть мы как-то должны инициализировать то ноль, то единицу. Делать мы это сможем благодаря тому, что у нас есть подтягивающий резистор. Мы будем её то отпускать от общей шины, то притягивать к ней. Соответственно, когда мы её отпустим, то через резистор на ней окажется высокий уровень, а когда подтянем к земле, то низкий. Теперь вопрос, как это всё сделать. А сделать это можно вполне с помощью определённого бита регистра DDRB, который отвечает, как мы знаем, за направление работы определённых ножек порта. Но мы воспользуемся тем, что когда мы объявляем ножку на выход, она у нас притягивается к земле, понятно что при условии, что соответствующий бит регистра PORTD будет также нулём. А если мы уже в соответствующий бит регистра DDRB записываем значение, при котором соответствующая этому биту ножка объявится на вход, то, соответственно, от общей шины она. ясное дело отпустится, иначе как контроллер определит её состояние. Ну и, как и было написано выше, на этой ножке засчет подтягивающего резистора установится высокий логический уровень. Также, начиная процесс обмена с датчиком, мы должны запретить прерывания, чтобы не получить искаженных данных. Также, прежде чем запретить прерывания, нам необходимо сохранить регистр статуса контроллера, или стек. Это регистр SREG. Поэтому в самом начале тела нашей функции проверки присутствии датчика на шине мы это и сделаем, сохранив его в определённую переменную, ну и затем уже запретим прерывания char dt_testdevice(void) //dt — digital termomether | определим, есть ли устройство на шине {   **char stektemp=SREG;// сохраним значение стека**   **cli(); //запрещаем прерывания** Добавим ещё одну переменную в теле данной функции для возврата результата функции cli(); //запрещаем прерывание **char dt;** Теперь для удобства работы с ножками порта давайте напишем некоторые дефайновые замены в заголовочном файле DS18B20.h #include «main.h» **#define PORTTEMP PORTD** **#define DDRTEMP DDRD** **#define PINTEMP PIND** **#define BITTEMP 1** Также здесь давайте сделаем ещё некоторые макроподставновки #include «main.h» **#define NOID 0xCC //Пропустить идентификацию** **#define T_CONVERT 0x44 //Код измерения температуры** **#define READ_DATA 0xBE //Передача байтов ведущему** Здесь мы присваиваем удобные имена командам датчикам. Ну давайте их посмотрим. Первая команда **0xCC** Посмотрим теперь все тайминги инициализации датчика ![image07](https://narodstream.ru/wp-content/uploads/2016/12/Image07-9.png) Здесь видно что мы здесь притягиваем шину, держим её в таком состоянии как минимум 480 микросекунд, затем отпускаем её, ждём максимум 60 мкс (мы подождём побольше — 65 мкс) ответа датчика, датчик должен нам ответить таким же притягиванием шины к земле за данное время. Если обнаружится ноль, то значит устройство на шине есть, а если шина так и останется висеть в воздухе, то данного датчика на шине нет. Ну. и на остове вышепрочитанного продолжим нашу функцию определения наличия датчика на шине char dt; **DDRTEMP |= 1<>BITTEMP; //читаем бит** До считывания следующего байта, судя по диаграмме мы ждём 45 микросекунд, затем возвращаем значение стека в стек и возвращаем результат функции считывания бита на шине   **bt = (PINTEMP & (1<>BITTEMP; //читаем бит**   **_delay_us(45);**   **SREG = stektemp;// вернем значение стека**   **return bt; //вернем результат** } Теперь, используя данную функцию, допишем тело функции считывания с датчика целого байта, последовательно занося байты в переменную **c**, применяя битовый сдвиг, а затем ещё же и возвращая.   **char i;**   **for(i=0;i<8;i++)**   **c|=dt_readbit()<>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](https://narodstream.ru/wp-content/uploads/2016/12/Image10_0500-1.png)](https://narodstream.ru///wp-content/uploads/2016/12/Image10-10.png) Посмотрим также поближе модуль с припаянным датчиком ![image12](https://narodstream.ru/wp-content/uploads/2016/12/Image12-9.png) Вот так выглядит модуль с другой стороны, желтым прямоугольником отмечен контакт данных термодатчика ![image11](https://narodstream.ru/wp-content/uploads/2016/12/Image11-11.png) Соберём код и прошьём контроллер. А вот и результат ![image13](https://narodstream.ru/wp-content/uploads/2016/12/Image13-9.png) Так как при измерении температуры у нас происходит очень большая задержка, а мы всё равно отсекаем считанные биты, впоследствии мы. когда будем писать часы с термометром, то мы данную задержку значительно убавим.