Разрабатываем систему контроля за системой обогрева для домашних животных на микроконтроллере Atmega8 в среде программирования Atmel Studio.

Страница на этапе разработки

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

Итак, для реализации данного проекта нам понадобиться:

  1. Блок питания.
  2. микроконтроллер Atmega8.
  3.  Тэна на 12В.
  4. семисегментный индикатор
  5. Светодиоды, кнопки, реле и др. обвязка.

Схема нашего устройства:

  1. Для начала подключим индикацию:

У нас индикация будет состоять из трех индикаторов (алгоритм работы у меня рассмотрен в уроках по микроконтроллерам).

Подключать мы будем следующим образом:

РС2, РС3, РС4 — подключаем к базам транзисторов которые управляют семисегментными индикаторами.

Ножки сдвигового регистра подключаем следующим образом:

SCK — PB5

MOSI — PB3

STCP — PB2

Управление тэном будет происходить с помощью реле которое будет включаться транзисторным ключом, который подключен к ножке PC5.

схема

Вентилятор будет управляться транзисторным ключом напрямую и подключен к ножке PD0, обратная связь подключена к ножке PD1:

схема

Светодиодная индикация подключена к ножкам:

PB0  — зеленый светодиод
PB1 — красный светодиод
PD5 — синий светодиод
PD6 — светодиод включения вентилятора
PD7  — светодиод включения тэны
Причем вместо отдельных светодиодов зеленого, красного и синего я взял один светодиод в корпусе которого встроены все три светодиода:

Кнопки подключены к ножкам:

 PC0 — кнопка +
 PC1 — кнопка —

Датчик температуры DS18B20 подключен к ножке PD2:

Прежде чем писать программный код, напишем алгоритм по которому у нас будет все работать: у нас на семисегментный индикатор будет выводиться температура воздуха которая заходит на тэну и по ней будет давать задание на включение/выключение тэны.  Контроль работы вентилятора будет происходит по импульсам с вентилятора.  С помощью кнопок мы будем выставлять желаемую температуру, значение которой будет сохраняться в памяти.  Кроме того, так как мой блок питания без охлаждение, а ток потребления тэной около 10А ( у меня 100Вт тэна) — я буду раз в 30 минут на 5 минут отключать тэну, тем самым давая блоку питания остыть.

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

Программный код:

#ifndef MAIN_H_
#define MAIN_H_

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>

#define t_inp PD2 //датчик температуры
#define but1 PC0 // первая кнопка +
#define but2 PC1 // вторая кнопка —
#define B_but1 (PINC&(1<<but1)) // считываем значение входа
#define B_but2 (PINC&(1<<but2)) // считываем значение входа
#define gisterezis 1
#define Led3_1 PB0 //зеленый
#define Led3_2 PB1 //красный
#define Led3_3 PD5 //синий
#define Led_v PD6 //светодиод ветилятора
#define Led_t PD7 //светодиод тэны
#define vent PD0 // вентилятор
//#define vent_takt PD1 // сигнал с вентилятора
#define vent_takt (PIND&(1<<PD1)) // считываем значение в
#define tena PC5 // тэна
#define time_v0 300 // время простоя вентилятора 5 минут
#define time_v1 15 // время работы вентилятора 15 секунд
#define time_t0 1200 // время работы тэны 20 минут
#define time_t1 600 // время простоя тэны 10 минут

int i=0;
int n=0;
int t[3];
int w=0;//счетчик для ошибки вентилятора
unsigned int t0=0;// метка для работы таймера
unsigned int t1=0;// время для работы устройств
unsigned int Flag;
uint8_t t_v = 0;// метка для гарантированной работы вентилятора с перехода нагрева — отключения
uint8_t m_v = 0;// метка для работы вентилятора
char metka = 0;//для вывода символа измерения или записи в память.
char T_zad = 24; // заданная температура нагрева
uint8_t EEMEM eepro_x;//переменная для передачи адресса
uint8_t eepro_x1 = 24;// переменная через которую мы читаем и записываем данные в память

#endif /* MAIN_H_ */

#include «main.h»

void zdvig (char z);
void num(unsigned int value);
void vibor(unsigned int m);

unsigned char DS18B20_init(void);
unsigned char read_18b20(void);
void write_18b20(unsigned char data);

ISR (TIMER0_OVF_vect){// прерывание по таймеру TIMER0
switch (i){
case 0:
PORTC&=~(1<<PC2);
PORTC&=~(1<<PC3);
PORTC&=~(1<<PC4);
vibor(t[0]);
PORTC|=(1<<PC2);
break;
case 1:
PORTC&=~(1<<PC2);
PORTC&=~(1<<PC3);
PORTC&=~(1<<PC4);
vibor(t[2]);
PORTC|=(1<<PC3);
break;
case 2:
PORTC&=~(1<<PC2);
PORTC&=~(1<<PC3);
PORTC&=~(1<<PC4);
vibor(t[1]);
PORTC|=(1<<PC4);
break;
}

i++;
if(i==3){i=0;}

}

ISR(TIMER1_COMPA_vect){
t0++;
if (t0 == 4294967290) t0=0;// обнуляем метку для работы вентилятора
t_v++;
if (t_v == 11) t_v=0;
}

int main(void){
unsigned int TH, TL;
unsigned int T_izm=0; // измеренное значение температуры для индикации
//unsigned int T_buf=0; // полное измеренное значение для управления
//unsigned int point; // Переменная для дробного значения температуры

DDRB = 0b00111111;//ножки SPI на выход
PORTB= 0b00000000;//низкий уровень
SPCR = ((1<<SPE)|(1<<MSTR));//Включим шину, объявим ведущим
SPDR = 0b00000000;
DDRC = 0b00111100;
PORTC= 0b00011100;
DDRD = 0b11100001;
PORTD= 0b00100000;

// настройка таймера 0
TIMSK |= (1<<TOIE0);
TCCR0 |= (1<<CS00);
//настройка таймера 1
TCCR1B |= (1<<WGM12); // устанавливаем режим СТС (сброс по совпадению)
TIMSK |= (1<<OCIE1A); //устанавливаем бит разрешения прерывания 1ого счетчика по совпадению с OCR1A(H и L)
OCR1AH = 0b10000000; //записываем в регистр число для сравнения
OCR1AL = 0b00000000;
TCCR1B |= (1<<CS12);//установим делитель.

sei();

eepro_x1 = eeprom_read_byte (&eepro_x); //считываем данные с памяти
T_zad = eepro_x1;// задаем заданную температуру.
t[2]=11; // вывод символа измерения
//выводим на некоторое время значение заданной температуры
num(T_zad);
PORTD&=~(1<<Led3_3);//включено
PORTB&=~(1<<Led3_2);
PORTB&=~(1<<Led3_1);
_delay_ms(2000);//время за которое показываем установленную температуру
PORTD|=(1<<Led3_3);//выключено
PORTB|=(1<<Led3_2);
PORTB|=(1<<Led3_1);
t[2]=10; // вывод символа измерения
t1 = t0;// записываем значение метки таймера
while (1){
if (metka == 0){t[2]=10;} // вывод символа измерения
else {t[2]=11;}// вывод символа записи в память
DS18B20_init(); // Инициализация DS18B20
write_18b20(0xCC); // Проверка кода датчика
write_18b20(0x44); // Запуск температурного преобразования
_delay_ms(780); // Задержка на опрос датчика
DS18B20_init(); // Инициализация DS18B20
write_18b20(0xCC); // Проверка кода датчика
write_18b20(0xBE); // Считываем содержимое ОЗУ
TL = read_18b20(); // Читаем первые 2 байта блокнота
TH = read_18b20();
// Вычисляем целое значение температуры
T_izm = ((TH << 4) & 0x70)|(TL >> 4);
// point = (TL & 0x0F)* 625 / 1000; // вычисляем дробную часть
// T_buf =T_izm *10 + point;//слаживаем целое и дробное значение.
num(T_izm);
if ((B_but1 == 0) && (B_but2 == 0)){ // факт, что мы хотим работать с памятью.
// отключаем тэну и через паузу вентилятор, светодиод переключается по цветам
PORTC&=~(1<<tena); // выключаем тэну
// добавить watchdog если забыли выйти с режима записи в память
metka = 1;
t[2]=11;// вывод символа записи в память
eepro_x1 = eeprom_read_byte (&eepro_x);
T_zad = eepro_x1;
}
while (metka == 1) {// пока работаем с памятью остальная программа не работает.
num(T_zad);
PORTD&=~(1<<Led3_3);//включено
PORTB&=~(1<<Led3_2);
PORTB&=~(1<<Led3_1);
if (B_but1 == 0){
T_zad++;
if (T_zad>=30){T_zad=30;}
}
if (B_but2 == 0){
T_zad—;
if (T_zad<=16){T_zad=16;}
}
_delay_ms(180);// защита от дребезга и если держать кнопку плавно увеличивать
if ((B_but1 == 0) && (B_but2 ==0)){ // заканчиваем работать с памятью
eepro_x1 = T_zad;
eeprom_write_byte (&eepro_x, eepro_x1);// записываем в память выбранное значение
t[2]=10;
metka=0;// выходим с цикла
PORTD|=(1<<Led3_3);//выключено
PORTB|=(1<<Led3_2);
PORTB|=(1<<Led3_1);
}
}
if (T_izm <= T_zad){
m_v=0;
PORTB|=(1<<Led3_1);// температура в норме
PORTD&=~(1<<Led3_3);// температура не в норме
//включаем тэну при условии что работает,гестерезис не нужен, дробная часть и будет гистерезисом
PORTD|=(1<<vent);//вентилятор включен
PORTD|=(1<<Led_v); //светодтод вентилятора включен
w=0;
while (m_v <=10){
if (vent_takt == 0) m_v++;
//прописать ошибку
w++;
if (w >= 100){
PORTC&=~(1<<tena); // выключаем тэну
PORTD&=~(1<<Led_t);
PORTD|=(1<<Led_v);
_delay_ms(500);
PORTD|=(1<<Led_t);
PORTD&=~(1<<Led_v);
_delay_ms(500);
}
}
// m_v = 0;//сбрасываем метку обязательной работы вентилятора
// проверка работы вентилятора, если не работает выдаем ошибку
if (t0-t1 <= time_t0){// запускаем время 30 минут
PORTD|=(1<<Led_t); // включаем тэну
PORTC|=(1<<tena); // включаем тэну
}
else if (t0-t1 <= (time_t0+time_t1)) {// отключаем тэну на 5 минут
PORTD&=~(1<<Led_t); // выключаем тэну
PORTC&=~(1<<tena); // выключаем тэну
}
else {
t1 = t0;// записываем значение метки таймера
}
}
// если температура внорме включаем вентилятор каждые 5 минут на пару 10 секунд прогоняя воздух для проверки окружающей температуры
if ((T_izm) >= (T_zad+gisterezis)){
PORTD&=~(1<<Led_t);// выключаем тэну
PORTC&=~(1<<tena); // выключаем тэну
PORTB&=~(1<<Led3_1);// температура в норме
PORTD|=(1<<Led3_3);// температура не в норме
if (m_v == 0){
t_v = 0;
m_v = 10;
while (t_v < 10){
PORTD|=(1<<vent);//вентилятор включен
PORTD|=(1<<Led_v); //время работы вентилятора
w=0;
while (m_v <=10){
if (vent_takt == 0) m_v++;
//прописать ошибку
w++;
if (w >= 100){
PORTC&=~(1<<tena); // выключаем тэну
PORTD&=~(1<<Led_t);
PORTD|=(1<<Led_v);
_delay_ms(500);
PORTD|=(1<<Led_t);
PORTD&=~(1<<Led_v);
_delay_ms(500);
}
}
}
}

if (t0-t1 <= time_v0){//время простоя вентилятора
PORTD&=~(1<<vent); // вентилятор выключен
PORTD&=~(1<<Led_v); // вентилятор выключен
}
else if (t0-t1 <= (time_v0+time_v1)) {//вентилятор включен
PORTD|=(1<<vent);//вентилятор включен
PORTD|=(1<<Led_v); //время работы вентилятора
}
else {
t1 = t0;// записываем значение метки таймера
}
}
}
}

void zdvig(char z) {
SPDR = z;
while(!(SPSR & (1<<SPIF)));//подождем пока данные передадутся
//сгенерируем отрицательный фронт для записи в STORAGE REGISTER
PORTB |= (1<<PB2); //высокий уровень
PORTB &= ~(1<<PB2); //низкий уровень
}
void num(unsigned int value){
// t[2]=((value/100)%10);
t[1]=((value/10)%10);
t[0]=(value%10);
}
void vibor(unsigned int m){
switch (m){// 3 бит — точка
case 0:
zdvig(0b11010111);
break;
case 1:
zdvig(0b00010001);
break;
case 2:
zdvig(0b10100111);
break;
case 3:
zdvig(0b10110101);
break;
case 4:
zdvig(0b01110001);
break;
case 5:
zdvig(0b11110100);
break;
case 6:
zdvig(0b11110110);
break;
case 7:
zdvig(0b10010001);
break;
case 8:
zdvig(0b11110111);
break;
case 9:
zdvig(0b11110101);
break;
case 10:
zdvig(0b11000110);
break;
case 11://символ Е, — запись в память
zdvig(0b11100110);
break;

}
}
unsigned char DS18B20_init(void){// Инициализация DS18B20
DDRD |= (1 << t_inp); // выход
PORTD &= ~(1 << t_inp); // Устанавливаем низкий уровень
_delay_us(490);
DDRD &= ~(1 << t_inp); // вход
_delay_us(68);
Flag = (PIND & (1 << t_inp)); // Ловим импульс присутствия датчика
// если Flag = 0 датчик подключен, Flag = 1 датчик не подключен
_delay_us(422);
return Flag;
}
unsigned char read_18b20(void){// Функция чтения байта из DS18B20
unsigned char i, data = 0;
for(i = 0; i < 8; i++)
{
DDRD |= (1 << t_inp); // выход
_delay_us(2);
DDRD &= ~(1 << t_inp); // вход
_delay_us(4);
data = data >> 1; // Следующий бит
if(PIND & (1 << t_inp)) data |= 0x80;
_delay_us(62);
}
return data;
}
void write_18b20(unsigned char data){// Функция записи байта в DS18B20
unsigned char i;
for(i = 0; i < 8; i++)
{
DDRD |= (1 << t_inp); // выход
_delay_us(2);
if(data & 0x01) DDRD &= ~(1 << t_inp);
else DDRD |= (1 << t_inp);
data = data >> 1; // Следующий бит
_delay_us(62);
DDRD &= ~(1 << t_inp); // вход
_delay_us(2);
}
}

<<—  Предыдущий урок

СОДЕРЖАНИЕ

Следующий урок —>>

<<—  Предыдущий урок

СОДЕРЖАНИЕ

Следующий урок —>>