Последние комментарии

Изучаем DS1820/DS18B20. Делаем простой термометр

В этой статье займемся изучением практического применения цифровых датчиков температуры DS18B20. Сделаем простой термометр на семисегментных индикаторах, который будет показывать положительную и отрицательную температуру с разрешением 0,1 градус Цельсия. Для этой цели используем микроконтроллер Atmega8, который работает от внутреннего генератора частотой 8 МГц, семисегментный индикатор с общим анодом(четырехразрядный) и датчик температуры DS18B20. Схема устройства показана на рисунке 1. Шину данных датчика подключаем к порту PC0, а также подключаем к плюсу питания через резистор R1 номиналом 4,7 кОм, поскольку выходной транзистор датчика имеет открытый сток. При питании датчика от шины данных(паразитное питание) вывод 3 датчика остается свободным.

Как уже известно последовательность действий при работе с одним датчиком будет такая:

1) послать сигнал обнуления линии (480...960 мкc);
2) принять импульс присутствия или заполнить время паузой (60...240 мкc);
3) послать команду пропуска идентификации 0xCC;
4) послать команду начала преобразования 0x44;
5) пауза не менее 500 мкc для завершения процесса преобразования;
6) обнулить линию;
7) послать команду пропуска идентификации 0xCC;
8) послать команду считывания блокнота 0xBE;
9) принять 9 байт;
10) выделить и проанализировать бит десятых долей градуса с установленной точностью, в нашем примере это 0,0625;
11) проанализировать бит знака;
12) если знак отрицательный, то перевести значение температуры в дополнительный код;
13) делаем преобразование целой и дробной части значения температуры и выводим на дисплей.

Подключение термодатчиков DS1820, DS18B20 к микроконтроллерам AVR (часть 2). Делаем простой термометр

Рисунок 1.

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

Для общения микроконтроллера с датчиком понадобятся три функции: функция инициализации или сброса датчика(DS18B20_init();), функция чтения одного байта из датчика(read_18b20();) и функция записи одного байта в датчик(write_18b20();). Для отсчета временных задержек используем стандартную функцию WINAVR util/delay.h, также для корректной работы датчика необходимо прописать в компиляторе реальную тактовую частоту микроконтроллера:

#define F_CPU 8000000UL

или же установить ее в настройках проекта.

Полный текст программы смотрите ниже:

/*** Практическое применение термодатчиков DS18B20. Простой термометр ***/
#define F_CPU 8000000UL // устанавливаем рабочую частоту контроллера
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

//------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp---minus-blank                      
char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80, 0x40, 0x00};
volatile unsigned char segcounter = 0;
volatile int display1 = 0, display2 = 0, display3 = 0, display4 = 0;
/*** Прерывание по переполнению T2, динамическая индикация ***/
ISR (TIMER2_OVF_vect)
{	
PORTD = 0xFF;
PORTB = (1 << segcounter);
	
switch (segcounter)
{	
case 0:
PORTD = ~(SEGMENTE[display1]);
break;
case 1:
PORTD = ~(SEGMENTE[display2]);
break;	
case 2:
PORTD = ~((SEGMENTE[display3])|0x80); // добавляем точку 
break;		
case 3:
PORTD = ~(SEGMENTE[display4]);
break;		
}
if ((segcounter++) > 2) segcounter = 0;	
}
unsigned char Temp_H = 0,Temp_L = 0,OK_Flag = 0,temp_flag;
/*** Инициализация DS18B20 ***/
unsigned char DS18B20_init(void)
{
PORTC &= ~(1 << PC0); // устанавливаем низкий уровень
DDRC |= (1 << PC0);
_delay_us(490);
DDRC &= ~(1 << PC0);
_delay_us(68);
OK_Flag = (PINC & (1 << PC0)); // ловим импульс присутствия датчика
// если OK_Flag = 0 датчик подключен, OK_Flag = 1 датчик не подключен
_delay_us(422);
return OK_Flag;
}
/*** Функция чтения байта из DS18B20 ***/
unsigned char read_18b20(void)
{
unsigned char i;   
unsigned char dat = 0;
for(i = 0;i < 8;i++) 
{		
DDRC |= (1 << PC0);
_delay_us(2);        
DDRC &= ~(1 << PC0);
_delay_us(4);        
dat = dat >> 1;	    
if(PINC & (1 << PC0))
{			
dat |= 0x80; 
}
_delay_us(62);
}	
return dat;
}
/*** функция записи байта в DS18B20 ***/
void write_18b20(unsigned char dat)
{
unsigned char i;	
for(i = 0;i < 8;i++)
{
DDRC |= (1 << PC0);
_delay_us(2);      		
if(dat & 0x01)
{
DDRC &= ~(1 << PC0);	
}
else
{
DDRC |= (1 << PC0);
}
dat = dat >> 1; 
_delay_us(62); 
DDRC &= ~(1 << PC0);
_delay_us(2); 
}	
}
/*** Главная функция ***/
int main(void)            
{
DDRD = 0xFF;
DDRB |= (1 << PB0)|(1 << PB1)|(1 << PB2)|(1 << PB3);
PORTD = 0x00;
PORTB = 0x00;
TIMSK |= (1 << TOIE2); // разрешение прерывания по таймеру2
TCCR2 |= (1 << CS21);  // предделитель на 8 
    
_delay_ms(50);
	
unsigned int tempint = 0,tempint1,tempint2,tempint3; // переменные для целого значения температуры
unsigned int temppoint = 0,temppoint1; // переменные для дробного значения температуры
sei(); //глобально разрешаем прерывания
	
while(1)
{				    	
if(OK_Flag == 1) // если датчик не ответил
{
// ставим прочерки во всех разрядах
display1 = 11; display2 = 11;
display3 = 11; display4 = 11;
}
DS18B20_init();        // инициализация DS18B20
write_18b20(0xCC);     // проверка кода датчика
write_18b20(0x44);     // запуск температурного преобразования
_delay_ms(1000);
DS18B20_init();        // инициализация DS18B20
write_18b20(0xCC);     // проверка кода датчика
write_18b20(0xBE);     // считываем содержимое ОЗУ
Temp_L = read_18b20(); // читаем первые 2 байта блокнота
Temp_H = read_18b20(); 
temp_flag = 1;         // флаг знака температуры равен 1(плюс)
if(Temp_H &(1 << 3))   // проверяем бит знака температуры на равенство единице 
{			
signed int tmp;
temp_flag = 0;      // флаг знака равен 0(минус)
tmp = (Temp_H << 8) | Temp_L;
tmp = -tmp;
Temp_L = tmp;
Temp_H = tmp >> 8; 
}		
tempint = ((Temp_H << 4) & 0x70)|(Temp_L >> 4); // вычисляем целое значение температуры
tempint1 = tempint % 1000 / 100;  
tempint2 = tempint % 100 / 10;  
tempint3 = tempint % 10;        
temppoint = Temp_L & 0x0F; // вычисляем дробное значение температуры
temppoint = temppoint * 625;       // точность температуры 
temppoint1 = temppoint / 1000;        
		
if(temp_flag == 0) // если флаг знака температуры равен 0, в первом разряде ставим минус
tempint1 = 11;
if(tempint1 < 1) // если первая цифра значения температуры меньше 1, то гасим 1 разряд индикатора
tempint1 = 12;
if(tempint1 == 12 && tempint2 < 1) // если первая цифра погашена и вторая цифра значения температуры меньше 1, то гасим 2 разряд индикатора
tempint2 = 12;
if(tempint2 < 1 && temp_flag == 0) // если вторая цифра значения температуры меньше 1 и знак равен "минус", то гасим 2 разряд индикатора
tempint2 = 12;
// выводим значения на дисплей
display1 = tempint1;
display2 = tempint2; 
display3 = tempint3;
display4 = temppoint1;
}
}

Архив для статьи "Изучаем термодатчики DS1820/DS18B20" HOT
Проект AVRStudio и Proteus
File Size 53.04 KB Download 4 984 Download

Печать

Комментарии  

+1 #101 Kot Mattroskin II 30.12.2014 15:23
Датчик постоянно показывает 12 -, пробовал греть и остужать.
Сообщить модератору
+1 #102 Vlad44 30.05.2015 22:13
а как изменить код,чтобы использовать индикатор с общим катодом?
Сообщить модератору
0 #103 AntonChip 31.05.2015 15:50
Цитирую Vlad44:
а как изменить код,чтобы использовать индикатор с общим катодом?

Читайте комментарий №29
Сообщить модератору
0 #104 sergeyav 30.11.2015 06:25
Здравствуйте. Как изменить код, чтобы использовать два датчика на одной линии? Заранее спасибо.
Сообщить модератору
+1 #105 kiber 04.01.2016 17:55
Здравствуйте, собрал схему на макетной плате.
При включении питания на индикаторе загораются прочерки.
Как то так - - - . -
Я так понимаю что датчик по какой то причине не определяется и срабатывает вот этот цикл:

if(OK_Flag == 1){ // если датчик не ответил
// ставим прочерки во всех разрядах
display1 = 11; display2 = 11;
display3 = 11; display4 = 11;
}

Что с ним не так? И можно ли как то проверить датчик на работоспособность?
Если разорвать контакт dq отвечающий за передачу данных то на индикаторе загорается 0.0
Сообщить модератору
0 #106 Koy747 04.01.2016 21:35
kiber, Добрый вечер, а как подключаете? Между ножкой данных и + резистор ставили. Я как то тоже долго воевал с датчиком, на макетке всё работало, а плату развёл и никак. В итоге вышло, что промахнулся ножкой на микроконтроллер е :lol:
Сообщить модератору
0 #107 kiber 07.01.2016 15:27
Koy747, да нет с ножкой я не промахнулся)) если на другую ставишь то он высвечивает 0.0, да и резистор стоит (всё как на схеме ), а если всё правильно подключить то он выдаёт вот такие значения: - - - . -
Пробовал убрать это условие:

if(OK_Flag == 1){ // если датчик не ответил
// ставим прочерки во всех разрядах
display1 = 11; display2 = 11;
display3 = 11; display4 = 11;
}

тогда он выдаёт следующее:
- 0 0.

Вопрос собственно в том, почему он не отвечает?
Может он навернулся? Можно ли его как то мультиметром проверить?
Сообщить модератору
0 #108 si4karuk 10.01.2016 15:22
А кто то пробовал запускать эту программу на Attiny2313 ???
Сообщить модератору
0 #109 Алексей2 07.02.2016 09:13
А как сделать, чтобы при превышении температуры выше 50 грудусов загоралась лампочка?
Сообщить модератору
0 #110 Gennady 30.09.2016 21:38
Добрый день. Пробовал откомпилировать проект, но пишет ошибку, что не найден #include где его взять? или как подключить. моя почта С уважением и надеждой на Вашу помощь, Геннадий.
Сообщить модератору
0 #111 Gennady 30.09.2016 21:39
Добрый день. Пробовал откомпилировать проект, но пишет ошибку, что не найден [ #include ]где его взять? или как подключить. моя почта С уважением и надеждой на Вашу помощь, Геннадий.
Сообщить модератору
0 #112 Сичкарук 04.10.2016 23:38
Читайте уроки з самого початку ;-)
Сообщить модератору
0 #113 Севастополь 17.12.2016 11:15
Добр день,

если у кого-то точность будет +\- 0.5 С, то добавьте перед основным циклом настройку разрешения датчика:
Код:
DS18B20_init();
write_18b20(0xCC);
write_18b20(0x4e);
write_18b20(0x0);
write_18b20(0x0);
write_18b20(0xef);

у меня датчик "с завода" шел с низким разрешением...
Сообщить модератору
-2 #114 Madison 28.12.2016 12:47
Собрал термометр на индикаторе с ОК. Все отлично работает. Спасибо!
Сообщить модератору
0 #115 Doublman 11.02.2017 20:34
Добрый день.
Собрал в железе и термометр дает погрешность плюс 3-5гр Пробовал с 4-мя датчиками. Как можно скорректировать показания?
И еще один вопрос: не нашол какие установить фьюзы high & low?
Сообщить модератору
0 #116 ISEMAN 20.04.2017 14:09
Всем привет.Решил повторить на ATmega 8 проект ,проект создавал в CodeVisionAVR C Compiler .Все перевёл вроде бы как надо ошибок нет.плюсовую показывает нормально а с минусовой проблема при -3 показание на индикаторе -15 .НЕ ПОЙМУ В ЧЕМ ПРОБЛЕМА вот сам код:
Код:
#include <mega8.h>
#include <delay.h>
#define F_CPU 8000000UL // устанавливаем рабочую частоту контроллера
/*** Практическое применение термодатчиков DS18B20. Простой термометр ***/
// 1 Wire Bus functions
#asm
.equ __w1_port=0x15 ;PORTC
.equ __w1_bit=0
#endasm
#include <1wire.h>

// DS1820 Temperature Sensor functions
#include <ds1820.h>

//------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp---minus-blank
char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80, 0x40, 0x00};

volatile unsigned char zamer = 0,segcounter = 0;
volatile int display1 = 0, display2 = 0, display3 = 0, display4 = 0;
unsigned int tempint,tempint1,tempint2,tempint3; // переменные для целого значения температуры
unsigned int temppoint,temppoint1; // переменные для дробного значения температуры

/*** Прерывание по переполнению T2, динамическая индикация ***/
// Timer 2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{

PORTD = 0xFF;
PORTB = (1 << segcounter);

switch (segcounter)
{
case 0:
PORTD = ~(SEGMENTE[display1]);
break;
case 1:
PORTD = ~(SEGMENTE[display2]);
break;
case 2:
PORTD = ~((SEGMENTE[display3])|0x80); // добавляем точку
break;
case 3:
PORTD = ~(SEGMENTE[display4]);
break;
}
if ((segcounter++) > 2) segcounter = 0;
}

volatile char temp_flag = 1, Temp_H, Temp_L, OK_Flag;

/*** Инициализация DS18B20 ***/
unsigned char DS18B20_init(void)
{
PORTC &= ~0x01; // устанавливаем низкий уровень
DDRC |= 0x01;
delay_us(490);
DDRC &= ~0x01;
delay_us(68);
OK_Flag = (PINC & 0x01); // ловим импульс присутствия датчика
// если OK_Flag = 0 датчик подключен, OK_Flag = 1 датчик не подключен
delay_us(422);
return OK_Flag;
}

/*** Функция чтения байта из DS18B20 ***/
unsigned char read_18b20(void)
{
unsigned char i;
unsigned char dat = 0;
for(i = 0;i < 8;i++)
{
DDRC |= 0x01;
delay_us(2);
DDRC &= ~0x01;
delay_us(4);
dat = dat >> 1;
if(PINC & 0x01)
{
dat |= 0x80;
}
delay_us(62);
}
return dat;
}

/*** функция записи байта в DS18B20 ***/
void write_18b20(unsigned char dat)
{
unsigned char i;
for(i = 0;i < 8;i++)
{
DDRC |= 0x01;
delay_us(2);
if(dat & 0x01)
{
DDRC &= ~0x01;
}
else
{
DDRC |= 0x01;
}
dat = dat >> 1;
delay_us(62);
DDRC &= ~0x01;
delay_us(2);
}
}

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
switch (zamer)
{
case 0:
if(OK_Flag == 0)
{
DS18B20_init();
write_18b20(0xCC); // проверка кода датчика
write_18b20(0x44); // запуск температурного преобразования
}
break;

case 1:
DS18B20_init();
write_18b20(0xCC); // проверка кода датчика
write_18b20(0xBE); // считываем содержимое ОЗУ

Temp_L = read_18b20(); // читаем первые 2 байта блокнота
Temp_H = read_18b20();

if(Temp_H &(1 << 3)) // проверяем бит знака температуры на равенство единице
{
signed int tmp;
temp_flag = 0; // флаг знака равен 0(минус)
tmp = (Temp_H << 8) | Temp_L;
tmp = -tmp;
Temp_L = tmp;
Temp_H = tmp >> 8;
}
else
{
temp_flag = 1; // флаг знака равен 0(минус)
}
break;
}
if ((zamer++) > 1) zamer = 0;
}

/*** Главная функция ***/
int main(void)
{

DDRD = 0xFF;
DDRB |= 0x0F;
PORTD = 0x00;
PORTB = 0x00;

// Clock value: 1000,000 kHz
TCCR1A=0x00;
TCCR1B=0x03;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x1E;
OCR1AL=0x85;
OCR1BH=0x00;
OCR1BL=0x00;

ASSR=0x00;
TCCR2=0x02;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x50;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;


// 1 Wire Bus initialization
w1_init();


//Global enable interrupts
#asm("sei")

while(1)
{
if(OK_Flag) // Если датчик не ответил
{
display1 = 11; display2 = 11;
display3 = 11; display4 = 11;
}

tempint = ((Temp_H << 4) & 0x70)|(Temp_L >> 4); // вычисляем целое значение температуры
tempint1 = tempint % 1000 / 100;
tempint2 = tempint % 100 / 10;
tempint3 = tempint % 10;
temppoint = Temp_L & 0x0F; // вычисляем дробное значение температуры
temppoint = temppoint * 625; // 625 // точность температуры
temppoint1 = temppoint / 1000;

if(temp_flag == 0) // если флаг знака температуры равен 0, в первом разряде ставим минус
tempint1 = 11;

if(tempint1 < 1) // если первая цифра значения температуры меньше 1, то гасим 1 разряд индикатора
tempint1 = 12;

if(tempint1 == 12 && tempint2 < 1) // если первая цифра погашена и вторая цифра значения температуры меньше 1, то гасим 2 разряд индикатора
tempint2 = 12;

if(tempint2 < 1 && temp_flag == 0) // если вторая цифра значения температуры меньше 1 и знак равен "минус", то гасим 2 разряд индикатора
tempint2 = 12;

// выводим значения на дисплей
display1 = tempint1;
display2 = tempint2;
display3 = tempint3;
display4 = temppoint1;
}
}
Сообщить модератору
0 #117 ivan_man 02.06.2017 21:11
Спасибо за код и схему огромное, делать курсовую о микропроцессорн ой системе не имея под рукой ни датчика, ни микропроцессора не очень то просто, а ваш пример мне очень помогает заставить эту абстрактную схему работать хотя бы в теории.
Сообщить модератору

Советуем посмотреть...

Авторизация