В этой статье займемся изучением практического применения цифровых датчиков температуры 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) принять 12 байт(по умолчанию);
10) выделить и проанализировать бит десятых долей градуса с установленной точностью, в нашем примере это 0,0625;
11) проанализировать бит знака;
12) если знак отрицательный, то перевести значение температуры в дополнительный код;
13) делаем преобразование целой и дробной части значения температуры и выводим на дисплей.

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

Рисунок 1.

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

Для общения микроконтроллера с датчиком понадобятся три функции:

  • функция инициализации или сброса датчика DS18B20_init;
  • функция чтения одного байта из датчика read_18b20;
  • функция записи одного байта в датчик write_18b20;

Для отсчета временных задержек используем стандартную функцию AVRStudio util/delay.h, также для корректной работы датчика необходимо прописать в компиляторе реальную тактовую частоту микроконтроллера:

#define F_CPU 8000000UL

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

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

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

// Массив значениий для семисегментного индикатора
char SEGMENTE[] = 
{
0x3F, // 0 
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F, // 9
0x40, // "минус" 
0x00  // "пусто"
};
unsigned char segcounter = 0;
volatile unsigned char display_1, display_2, display_3, display_4;

// Прерывание по переполнению T2, динамическая индикация
ISR (TIMER2_OVF_vect)
{  
PORTD = 0xFF;
PORTB = (1 << segcounter);
     
switch (segcounter)
{  
case 0:
PORTD = ~(SEGMENTE[display_1]);
break;
case 1:
PORTD = ~(SEGMENTE[display_2]);
break; 
case 2:
PORTD = ~((SEGMENTE[display_3])|0x80); // добавляем точку
break;     
case 3:
PORTD = ~(SEGMENTE[display_4]);
break;     
}
if ((segcounter++) > 2) segcounter = 0; 
}

unsigned char Temp_MSB, Temp_LSB, OK_Flag, temp_flag;

// Инициализация DS18B20
unsigned char DS18B20_init(void)
{
DDRC |= (1 << PC0); // PC0 - выход
PORTC &= ~(1 << PC0); // Устанавливаем низкий уровень
_delay_us(490);
DDRC &= ~(1 << PC0); // 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, data = 0;
for(i = 0; i < 8; i++)
{      
DDRC |= (1 << PC0); // PC0 - выход
_delay_us(2);       
DDRC &= ~(1 << PC0); // PC0 - вход
_delay_us(4);       
data = data >> 1; // Следующий бит      
if(PINC & (1 << PC0)) data |= 0x80;
_delay_us(62);
}  
return data;
}

// Функция записи байта в DS18B20
void write_18b20(unsigned char data)
{
unsigned char i;   
for(i = 0; i < 8; i++)
{
DDRC |= (1 << PC0); // PC0 - выход
_delay_us(2);          
if(data & 0x01) DDRC &= ~(1 << PC0);
else DDRC |= (1 << PC0);
data = data >> 1; // Следующий бит
_delay_us(62);
DDRC &= ~(1 << PC0); // PC0 - вход 
_delay_us(2);
}  
}

// Главная функция 
int main(void)           
{
// Настройка портов ввода/вывода
DDRB |= (1 << PB0)|(1 << PB1)|(1 << PB2)|(1 << PB3); // Разряды
PORTB = 0x00;
DDRD = 0xFF; // Сегменты
PORTD = 0x00;
// Настройка Т2
TIMSK |= (1 << TOIE2); // Разрешение прерывания по Т2
TCCR2 |= (1 << CS21);  // Предделитель на 8
     
unsigned int buffer;
unsigned int temp_int_1,temp_int_2,temp_int_3; // Переменные для целого значения температуры
unsigned int temp_point; // Переменная для дробного значения температуры

sei(); // Глобально разрешаем прерывания
     
while(1)
{
// Если датчик не ответил выводим "минус" во всех разрядах                      
if(OK_Flag) display_1 = display_2 = display_3 = display_4 = 10;

DS18B20_init();        // Инициализация DS18B20
write_18b20(0xCC);     // Проверка кода датчика
write_18b20(0x44);     // Запуск температурного преобразования
_delay_ms(1000);       // Задержка на опрос датчика
DS18B20_init();        // Инициализация DS18B20
write_18b20(0xCC);     // Проверка кода датчика
write_18b20(0xBE);     // Считываем содержимое ОЗУ
Temp_LSB = read_18b20(); // Читаем первые 2 байта блокнота
Temp_MSB = read_18b20();
temp_flag = 1;         // Флаг знака температуры равен 1(плюс)

// Вычисляем отрицательную температуру
if(Temp_MSB &(1 << 3))   // Проверяем бит знака температуры на равенство единице
{          
signed int temp;
temp_flag = 0;      // Флаг знака равен 0(минус)
temp = (Temp_MSB << 8)|Temp_LSB;
temp = -temp; // Переводим дополнительный код в прямой
Temp_LSB = temp;
Temp_MSB = temp >> 8;
}      

// Вычисляем целое значение температуры
buffer = ((Temp_MSB << 4) & 0x70)|(Temp_LSB >> 4);
temp_int_1 = buffer % 1000 / 100; 
temp_int_2 = buffer % 100 / 10; 
temp_int_3 = buffer % 10;

// Вычисляем дробное значение температуры       
if(temp_flag == 0) buffer = (Temp_LSB & 0x0F) + 1; 
else buffer = (Temp_LSB & 0x0F);
temp_point = buffer * 625 / 1000; // Точность темпер.преобразования(0.0625)

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

// Выводим значения на дисплей
display_1 = temp_int_1;
display_2 = temp_int_2;
display_3 = temp_int_3;
display_4 = temp_point;

}
}


Архив для статьи "Изучаем термодатчики DS1820/DS18B20"
Описание: Проект AVRStudio и Proteus
Размер файла: 53.04 KB Количество загрузок: 7 458 Скачать

Комментарии  

0 #121 Михаил_001 22.08.2018 20:30
И все-таки как вы решили, проблему что на датчике -0.2 показывает -0.1, в протеусе?
Сообщить модератору
0 #122 AntonChip 26.02.2019 21:52
Цитирую Михаил_001:
И все-таки как вы решили, проблему что на датчике -0.2 показывает -0.1, в протеусе?

только пока так
Код:
// Вычисляем дробное значение температуры
if(temp_flag == 0) buffer = (Temp_LSB & 0x0F) + 1; // Корректировка
else buffer = (Temp_LSB & 0x0F);
temp_point = buffer * 625 / 1000; // Точность темпер.преобразования(0.0625)
Сообщить модератору
0 #123 Леха 19.12.2019 11:05
tempint = ((Temp_MSB > 4); а для чего 0х70??? без это части сделал, работает)
Сообщить модератору
0 #124 AntonChip 26.12.2019 08:35
Цитирую Леха:
tempint = ((Temp_MSB > 4); а для чего 0х70??? без это части сделал, работает)

Этой операцией
Код:
((Temp_MSB << 4) & 0x70)

выделяем из старшего байта регистра температуры 8,9 и 10 биты, т.е. сначала сдвигаем их в левую часть и применяем маску 0x70, чтобы убрать все не нужные биты
Сообщить модератору
0 #125 Алексаша 16.01.2020 18:57
Здравствуйте. Кто занимается программировани ем на МК? Меня интересует схема 2-х канального термометра на МК. Я ищу любителя-програ ммиста, с которым я бы смог воплотить в жизнь свою задумку. Немного расширить функционал термометра, использовать более крупный ЖК дисплей, добавить некоторые модуля. Если кто в принципе согласен, то можно пообщаться далее. А заодно скажите пожалуйста регион вашего проживания и почту для общения.
Сообщить модератору
0 #126 Oleg Oleg 23.06.2021 17:08
Скетч не компилируется. Выдает ошибку: 'TIMSK' was not declared in this scope. В строке: TIMSK |= (1
Сообщить модератору
0 #127 AntonChip 23.06.2021 21:55
Для ATmega328 регистр будет TIMSK2
Сообщить модератору
0 #128 Влад55 21.12.2021 12:57
можно asm файл кода на данную статью ,чисто для написания курсового проекта не более,спасибо.
Сообщить модератору
0 #129 gmgm0053 06.09.2022 04:35
скажите пожалуйста как поменять распиновку контактов
Сообщить модератору
0 #130 AntonChip 06.09.2022 21:49
Цитирую gmgm0053:
скажите пожалуйста как поменять распиновку контактов

Изучаете информацию о портах ввода/вывода данного микроконтроллер а, а за управление портами отвечают регистры такие как PORTx, DDRx
Сообщить модератору
-1 #131 максимушка 29.11.2022 18:08
спасибо ,помогло !
Сообщить модератору
0 #132 максимушка 29.11.2022 21:13
Скажите пожалуйста как найти ALARM флаг срабатывающий при разнице температуры TH and TL в DS18B20 ?
Сообщить модератору
0 #133 Эмиль 20.12.2023 17:42
Здравствуйте, возник вопрос, можете ли вы показать блок-схему кода, очень хотелось бы посмотреть на то как она там устроена
Сообщить модератору