Урок 20
Часть 2
Подключаем датчик температуры DS18B20
В предыдущей части нашего занятия мы кратко изучили датчик DS18B20, который является датчиком температуры. Также мы уже создали проект и начали писать в него некоторый код и остановились на функции определения наличия датчика на шине. В данной функции мы запретили прерывания, сохранив до этого стек.
Продолжим работать с ней далее.
Добавим ещё одну переменную в теле данной функции для возврата результата функции
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
Она заставляет датчик отойти от стандартной процедуры инициализации и позволяет пропутсить считыванеие ROM для определения идентификатора. Нам идентификатор не нужен, так как датчик данный на шине у нас присутствует в единственном экземпляре.
Следующая команда 0x44
Данная команда инициализирует начало конвертирования данных в цифровой код.
Следующая команда 0xBE
Данная команда читает регистры датчика. Вот эти регистры
Вот так вообще организована вся память в датчике измерения температуры, и как мы видим, самые младшие два регистра в данной памяти и представляют собой два байта показаний температуры.
Посмотрим теперь все тайминги инициализации датчика
Здесь видно что мы здесь притягиваем шину, держим её в таком состоянии как минимум 480 микросекунд, затем отпускаем её, ждём максимум 60 мкс (мы подождём побольше — 65 мкс) ответа датчика, датчик должен нам ответить таким же притягиванием шины к земле за данное время. Если обнаружится ноль, то значит устройство на шине есть, а если шина так и останется висеть в воздухе, то данного датчика на шине нет.
Ну. и на остове вышепрочитанного продолжим нашу функцию определения наличия датчика на шине
char dt;
DDRTEMP |= 1<<BITTEMP; //притягиваем шину
_delay_us(485); //задержка как минимум на 480 микросекунд
DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину
_delay_us(65); //задержка как минимум на 60 микросекунд
if ((PINTEMP & (1<<BITTEMP))==0)//проверяем, ответит ли устройство
{
dt=1;//устройство есть
}
else dt=0;//устройства нет
В условии у нас идёт проверка, опустилась ли шина.
Затем мы стеку вернём его первоначальное состояние
else dt=0;//устройства нет
SREG = stektemp;// вернем значение стека
Отдельной командой после этого разрешать прерывания не требуется, так как все эти команды как раз и управляют определёнными битами регистра SREG. Если прерывания были разрешены до сохранения стека, то они, ясное дело, опять разрешатся, так как все биты регистра мы возвращаем в прежнее положение.
Дальше. судя по диаграмме, которая у нас находится выше, мы должны выдержать время максимум 480 микросекунд, но так как после последней задержки у нас уже прошло некоторое количество времени, то хватит и 420, тем более задержка эта не фиксированная, а максимальная. Ну, и добавим эту задержку в тело функции далее, и в оконцовке вернём результат функции.
SREG = stektemp;// вернем значение стека
_delay_us(420); //задержка как минимум на 480 микросекунд, но хватит и 420, тк это с учетом времени прошедших команд
return dt; //вернем результат
}
Теперь по окончании написания кода данной функции, мы можем продолжить начатую в предыдущей части урока функцию преобразования. Но, всё-таки, чтобы её продолжить, нам нужна будет ещё одна немаловажная, а может и вообще самая важная функция в нашем проекте — функция считывания байта с датчика температуры. Создадим данную функцию, а также в её теле сразу объявим две локальные переменные
//функция чтения байта с устройства
unsigned char dt_readbyte(void)
{
char c=0;
char i;
}
Прежде чем писать дальше тело данной функции, мы должны вспомнить что каждый байт у нас состоит из восьми бит, поэтому целесообразно для того, чтобы не нагромождать много кода в одну функцию, создать ещё одну выше для чтения отдельного бита с датчика температуры. Создадим пока каркас данной функции с комментарием, объясняющем назначение этой функции
//функция чтения бита с устройства
char dt_readbit(void)
{
}
В данной функции мы также сохраним регистр статусов микроконтроллера, запретим прерывания и создадим переменную для дальнейшего возврата результата. Результат у нас будет в виде обычной знаковой 8-битной величины, по большому счёту мы в ней будем использовать только младший бит
//функция чтения бита с устройства
char dt_readbit(void)
{
char stektemp=SREG;// сохраним значение стека
cli(); //запрещаем прерывание
char bt; //переменная хранения бита
Для написания дальнейшего кода посмотрим временную диаграмму, как вообще читать бит с данного датчика. Данная диаграмма в технической документации универсальна и для чтения и для записи бита в устройство
Мы здесь видим, что после притягивания шины к земле мы ждём минимум одну микросекунду, ну, подождём две микросекунды на всякий случай, перед этим притянув шину к земле
char bt; //переменная хранения бита
DDRTEMP |= 1<<BITTEMP; //притягиваем шину
_delay_us(2); //задержка как минимум на 2 микросекунды
Смотрим дальше. Мы должны шину отпустить от земли, а затем задержка для считывания результата. Здесь стоит общая задержка 15 микросекунд, так как мы уже две подождали, осталось нам 13
_delay_us(2); //задержка как минимум на 2 микросекунды
DDRTEMP &= ~(1<<BITTEMP); //отпускаем шину
_delay_us(13);
После данной задержки результат уже на шине должен быть. Считаем его аналогично, как в предыдущей функции мы узнавали присутствие усройства на шине
_delay_us(13);
bt = (PINTEMP & (1<<BITTEMP))>>BITTEMP; //читаем бит
До считывания следующего байта, судя по диаграмме мы ждём 45 микросекунд, затем возвращаем значение стека в стек и возвращаем результат функции считывания бита на шине
bt = (PINTEMP & (1<<BITTEMP))>>BITTEMP; //читаем бит
_delay_us(45);
SREG = stektemp;// вернем значение стека
return bt; //вернем результат
}
Теперь, используя данную функцию, допишем тело функции считывания с датчика целого байта, последовательно занося байты в переменную c, применяя битовый сдвиг, а затем ещё же и возвращая.
char i;
for(i=0;i<8;i++)
c|=dt_readbit()<<i; //читаем бит
return c;
}
Ну вот. Самая, можно сказать, важная функция написана.
Продолжим функцию конвертирования считанных байтов
unsigned int tt=0;
if(dt_testdevice()==1) //если устройство нашлось
{
}
return tt;
}
В теле условия по определению присутствия датчика на шине, прежде чем что-то считать с нашего датчика, мы должны ему будем передать команды. А чтобы их передать, мы должны будем написать также функцию отправки байта в устройство. Но так как мы и так уже много чего в данной части написали, то сделаем это в следующей части урока.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация на датчик DS18B20
Программатор, датчик температуры DS18B20, плата для его подключения с микросхемой DS1307 и дисплей можно приобрести здесь:
Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером
Модуль RTC DS1307 с микросхемой памяти
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
DDRTEMP |= 1<<BITTEMP; //притягиваем шину ?
Почему притягиваем, если в итоге на PD1 логическая единица?Объясните пожалуйста начинающему!
Тут не так всё. Мы не уровнями управляем. а направлением. Если мы меняем направление на 1 (на выход), то ножка притягиватеся, так как уровень устанавливается ноль. А если меняем направление на 0 (вход), ножка начинает мерить входные уровни, а следовательно, отрывается от земли. Всё гениальное просто!
Спасибо большое! да все так… еще вопрос по дефайну в заголовочном файле DS18B20.h #define PORTTEMP PORTD зачем если нигде не используется?
Почему здесь такая не согласованность, с начало говорится максимум 480 микросекунд, а потом в коментарии указывается минимум, хотя на рисунке написано минимум, а если минимум 480 то 420 микросекунд не может пройти, но у Вас пройдет только благодаря тому, что перед этим было 65 микросекунд и опеределенные команды займут по пару микросекунд и в сумме дадут более 480 микросекунд, но если бы не эти «НО» то от датчика Вы бы не дождались ответа и это очень запутывает.
Дальше. судя по диаграмме, которая у нас находится выше, мы должны выдержать время максимум 480 микросекунд, но так как после последней задержки у нас уже прошло некоторое количество времени, то хватит и 420, тем более задержка эта не фиксированная, а максимальная. Ну, и добавим эту задержку в тело функции далее, и в оконцовке вернём результат функции.
SREG = stektemp;// вернем значение стека
_delay_us(420); //задержка как минимум на 480 микросекунд, но хватит и 420, тк это с учетом времени прошедших команд
return dt; //вернем результат
}
Не дурите, всё правильно, внимательнее смотрите на график! Автору респект!
да, как-то всё запутано
Не понял зачем нужно отключать прерывания?
Чтобы, не дай Бог, не попасть в обработчик. Критическая секция, можно сказать.
Да, чем дальше изучаешь, тем более запутанным становится объяснение простых, по мнению автора, вещей. Это проблема всех интернет-учителей: они считают всё это понятным и простым со своей точки зрения, поэтому все «мелочи» и опускаются, и получается постоянное перескакивание с одного на другое, за объяснением следует неожиданный вывод или решение о том, что нужно ввести очередную функцию или переменную и т. д. Это похоже на то, как если бы человеку, не знакомому с сигналами светофора, сказали: «Увидишь красный сигнал светофора — стой». А на вопрос:»А почему я должен стоять?»., — ответили бы: «Просто стой, так будет надёжнее».