Например TDA7294

Форум РадиоКот • Просмотр темы - Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
Форум РадиоКот
Здесь можно немножко помяукать :)





Текущее время: Вт апр 23, 2024 22:53:45

Часовой пояс: UTC + 3 часа


ПРЯМО СЕЙЧАС:



Начать новую тему Ответить на тему  [ Сообщений: 8 ] 
Автор Сообщение
Не в сети
 Заголовок сообщения: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Чт ноя 16, 2017 18:42:17 
Родился
Аватар пользователя

Зарегистрирован: Чт ноя 16, 2017 17:04:14
Сообщений: 15
Откуда: Сургут
Рейтинг сообщения: 0
День добрый, ваяю тут жестянку которая будет в строго определенное время и по требованию (по UART) включать/выключать нагрузку отчитываться об этом на LCD и по UART. LCD и RTC живут на шине TWI и с последовательным опросом проблем нет, но вот некоторые сообщения выводятся на LCD порядка 10 секунд и есть шанс профукать момент включения/выключения нагрузки.

На данный момент не придумал ничего лучше как опрашивать RTC по таймеру раз в 0,5 секунды, но так как при этом переключаю устройство с которым веду диалог по TWI, то при возврате из прерывания получаю висяк в цикле ожидания TWINT.

Спойлер
Код:
void I2C_Init(void)
{
   TWSR = 0;
   TWBR = ((F_CPU/F_SCL)-16)/2;
}
void I2C_TWINT_Wait(void)
{
   while(!(TWCR &(1<<TWINT)))
   {
              Вот тут вот и повисаем
   }
}
void I2C_Start(void)
{
   TWCR =(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);   
   I2C_TWINT_Wait();
}
void I2C_Stop(void)
{
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}
void I2C_Send_Device_Address(uint8_t dev_address, bool write)
{
   I2C_last_device = dev_address;
   I2C_last_write_mode = write;
   
   if (write == true)
   {
      TWDR = dev_address;
   }
   else
   {
      TWDR = dev_address|1;
   }
   
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
}
void I2C_Send_Register_Address(uint8_t reg_address)
{
   TWDR = reg_address;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
}
void I2C_Send_Data_ACK(uint8_t data)
{
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
}
void I2C_Send_Data_NACK(uint8_t data)
{
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
}
uint8_t I2C_Read_Data_ACK(void)
{
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
}
uint8_t I2C_Read_Data_NACK(void)
{
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
}

ISR(TWI_vect)
{
   UART_Send_String("TWI interrupt ");
   TWCR =(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
}


попробовал в прерывании таймера после опроса RTC повторно стартовать работу с последним устройством которое прервали

Спойлер
Код:
SR(TIMER1_OVF_vect)
{
   TCNT1 = 65536-31250;
   
   uint8_t I2C_reurn_device = I2C_last_device;
   bool I2C_return_write_mode = I2C_last_write_mode;
   
   RTC_Get_System_Date_Time();
   
   I2C_Start();
   I2C_Send_Device_Address(I2C_reurn_device,I2C_return_write_mode);
}


стало немного лучше, но периодически все равно подвисает. подскажите в какую сторону копать?

Запись и чтение не выполняю через прерывание по тому что необходимо плясать с бегущими строками и побуквенным выводом

Добавлено after 1 hour 16 minutes 57 seconds:
сейчас запретил прерывания при выполнении атомарных операций TWI

Спойлер
Код:
void I2C_Init(void)
{
   TWSR = 0;
   TWBR = ((F_CPU/F_SCL)-16)/2;
}
void I2C_TWINT_Wait(void)
{
   while(!(TWCR &(1<<TWINT)))
   {

   }
}
void I2C_Start(void)
{
   cli();
   TWCR =(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);   
   I2C_TWINT_Wait();
   sei();
}
void I2C_Stop(void)
{
   cli();
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
   sei();
}
void I2C_Send_Device_Address(uint8_t dev_address, bool write)
{
   cli();
   I2C_last_device = dev_address;
   I2C_last_write_mode = write;
   
   if (write == true)
   {
      TWDR = dev_address;
   }
   else
   {
      TWDR = dev_address|1;
   }
   
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Register_Address(uint8_t reg_address)
{
   cli();
   TWDR = reg_address;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Data_ACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Data_NACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
uint8_t I2C_Read_Data_ACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}
uint8_t I2C_Read_Data_NACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}


работает до зависания чуть дольше


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Чт ноя 16, 2017 22:02:55 
Поставщик валерьянки для Кота

Карма: 16
Рейтинг сообщений: 329
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Сообщений: 2222
Откуда: Tashkent
Рейтинг сообщения: 0
Цитата:
но вот некоторые сообщения выводятся на LCD порядка 10 секунд и есть шанс профукать момент включения/выключения нагрузки.

Это чтож за LCD такой, в который слать картинку 10 сек надо?

Цитата:
подскажите в какую сторону копать?

В сторону отделения мух от котлет.
Либо вы делаете опрос программный, либо через автомат по прерываниям. Всё остальное - хороший способ запутаться самому и запутать нас.
Очерёдность посылок между устройствами должна разруливаться самой программой, а не обрывом передачи и приведения интерфейса в непонятное состояние.
Контроль состояния производится посредством анализа кодов статусного регистра TWSR.

Впрочем возможно организовать ещё один I2C канал с помощью программного ногодрыга. Если конечно лапки свободные есть.

Еще когда существует вероятность зависания периферийных микросхем заводят дополнительный таймер(можно виртуальный) и по таймауту вызывается подпрограмма, которая должна что-то там пнуть или иное сделать.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Чт ноя 16, 2017 22:34:35 
Поставщик валерьянки для Кота
Аватар пользователя

Карма: 20
Рейтинг сообщений: 648
Зарегистрирован: Пт май 31, 2013 17:14:38
Сообщений: 2081
Откуда: Украина, Винница
Рейтинг сообщения: 0
пару месяцев назад на работе была задача переписать глючную прошивку под один ведомый МК. Я не нашел ничего лучше как скопипастить автомат от DiHalt`a, ну а для мастера заюзал атмеловкий аппноут (потому что мастером Хмега). Ну это всё мелочи в данном случае. Объясните что это за LCD такой, сообщения на который выводятся 10 секунд? Модель, контроллер можно в студию? Ну а если там действительно 10 сек и никуда не деться, то рекомендую Вам поступить следующим образом: конфигурируете внутренний таймер МК на отсчет времени, скажем секунд 60. Вычитываете свои часы, видите что включать надо через 3 секунды, ставите свой таймер на 3 секунды и идете пулять свой и2с в дисплей. Через 3 секунды включаете/выключаете нагрузку. Только Вам надо будет при входе в прерывание и2си включать глобальное разрешение прерываний, чтобы не профукать свой таймер. Но я ставлю на то что у Вас неверная работа с дисплеем/и2си.


Вернуться наверх
 
PCBWay - всего $5 за 10 печатных плат, первый заказ для новых клиентов БЕСПЛАТЕН

Сборка печатных плат от $30 + БЕСПЛАТНАЯ доставка по всему миру + трафарет

Онлайн просмотровщик Gerber-файлов от PCBWay + Услуги 3D печати
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Пт ноя 17, 2017 00:29:33 
Родился
Аватар пользователя

Зарегистрирован: Чт ноя 16, 2017 17:04:14
Сообщений: 15
Откуда: Сургут
Рейтинг сообщения: 0
Тут дело не в дисплее, а в размере сообщения выводимом на него, дисплей самый обычный 16*2 Hitachi что ли, рулится платой PCF8574AT. То что обрывать передачу плохо прекрасно понимаю, но как этого избежать если честно не очень.

с другой стороный, судя по даташиту PCF8574AT посылки к дисплею все полностью идут с ACK. тоесть не вижу проблемы в том, чтобы завершить передачу посылки в дисплей, уйти в прерывание где опрашивается RTC. стопнуть TWI стартовать TWI указать устройством назначения RTC, опросить RTC стартовать TWI указать устройством назначения LCD и продолжить гадить туда буквами. опять же судя по тому что я вычитал: можно вообще не передавать стоп в шину (если конечно там только один мастер) а просто повторно выдавать старт и адрес с признаком r/w ну и далее по пьесе.

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

вот полный листинг

Спойлер
Код:
/*
*  main.cpp
*  Project: G.E.C.K.
*  Created: 21.10.2017 20:18:55
*  Author:  Сергей Набоков
*/

//КОНСТАНТЫ
#define F_CPU 16000000UL          //задаем частоту МК
#define F_SCL  100000L         //задаем частоту I2C

#define UART_Start_symbol &#39;*&#39;     //Старт символ пакета
#define UART_Stop_symbol &#39;#&#39;      //Стоп символ пакета
#define UART_buffer_size 255     //Буфер для служебного обмена данными с внутренними устройствами

#define Input_command_size 13      //Размер входного пакета данных

#define String_end_symbol &#39;\0&#39;    //Стоп символ строки


#define RTC_module  0xD0        // Адрес RTC на шине I2C
#define LCD_module  0x7e        // Адрес LCD на шине I2C

#define LCD_RS 0
#define LCD_RW 1
#define LCD_E 2
#define LCD_BL 3

//----------
#define Device_type         {"G.E.C.K. - HFG"}
#define Firmware_version           {"FW: v1.5b"}
//----------

//БИБЛИОТЕКИ
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>

//СТРУКТУРЫ
typedef struct {uint8_t HH;
            uint8_t MM;
            uint8_t SS;} Time;
            
typedef struct {uint8_t DD;
            uint8_t MM;
            uint8_t YY;} Date;
            
typedef struct {char Name[17];
            uint8_t State;
            
            uint8_t Auto_Enable;
            uint8_t *EEPROM_AE;
            
            Time Enable_Time;
            uint8_t *EEPROM_ET_HH;
            uint8_t *EEPROM_ET_MM;
            uint8_t *EEPROM_ET_SS;
            
            uint8_t Auto_Disable;
            uint8_t *EEPROM_AD;
            
            Time Disable_Time;
            uint8_t *EEPROM_DT_HH;
            uint8_t *EEPROM_DT_MM;
            uint8_t *EEPROM_DT_SS;
            
            uint8_t Cycle;
            uint8_t *EEPROM_CYCLE;
            
            uint8_t Cycle_Start_Day;
            uint8_t *EEPROM_CSD;
            
            uint8_t Water;
            uint8_t Force_Enable;} Channel;
 
 //ЭНЕРГОНЕЗАВИСИМАЯ ПАМЯТЬ
 uint8_t System_first_run      EEMEM = 0;

 uint8_t White_light_AE         EEMEM = 0;
 uint8_t White_light_ET_HH      EEMEM = 0;
 uint8_t White_light_ET_MM      EEMEM = 0;
 uint8_t White_light_ET_SS      EEMEM = 0;
 uint8_t White_light_AD         EEMEM = 0;
 uint8_t White_light_DT_HH      EEMEM = 0;
 uint8_t White_light_DT_MM      EEMEM = 0;
 uint8_t White_light_DT_SS      EEMEM = 0;
 
 uint8_t White_light_CYCLE      EEMEM = 0;
 uint8_t White_light_CSD      EEMEM = 1;

 uint8_t Grow_light_AE         EEMEM = 0;
 uint8_t Grow_light_ET_HH      EEMEM = 0;
 uint8_t Grow_light_ET_MM      EEMEM = 0;
 uint8_t Grow_light_ET_SS      EEMEM = 0;
 uint8_t Grow_light_AD         EEMEM = 0;
 uint8_t Grow_light_DT_HH      EEMEM = 0;
 uint8_t Grow_light_DT_MM      EEMEM = 0;
 uint8_t Grow_light_DT_SS      EEMEM = 0;
 
 uint8_t Grow_light_CYCLE      EEMEM = 0;
 uint8_t Grow_light_CSD         EEMEM = 1;

 uint8_t Water_pump_AE         EEMEM = 0;
 uint8_t Water_pump_ET_HH      EEMEM = 0;
 uint8_t Water_pump_ET_MM      EEMEM = 0;
 uint8_t Water_pump_ET_SS      EEMEM = 0;

 uint8_t Water_pump_AD         EEMEM = 0;
 uint8_t Water_pump_DT_HH      EEMEM = 0;
 uint8_t Water_pump_DT_MM      EEMEM = 0;
 uint8_t Water_pump_DT_SS      EEMEM = 0;

 uint8_t Water_pump_CYCLE      EEMEM = 0;
 uint8_t Water_pump_CSD         EEMEM = 1;
 
//ПЕРЕМЕННЫЕ
uint8_t Calendar[12] = {31,28,31,30,31,30,31,31,30,31,30,31};      //Календарь
uint8_t Calendar_leap[12] = {31,29,31,30,31,30,31,31,30,31,30,31};   //Високосный календарь

bool RTC_error = false;                                    //Ошибка модуля RTC

char UART_input_buffer[50];
bool UART_start_symbol_detected = false;                            //Обнаружен стартовый символ
bool UART_stop_symbol_detected = false;                        //Обнаружен стоповый символ
bool UART_user_control = true;

char Input_command[Input_command_size];                        //Входящая команда
uint8_t IC_c = 0;

Time System_time = {0,0,0};                                 //Системное время
bool SST_command = false;
Time t_time = {0,0,0};
   
Date System_date = {0,0,0};                                         //Системная дата
bool SSD_command = false;
Date t_date = {0,0,0};
 
Channel White_light =   {"White light:",                     //Холодный свет
                   0,
                   0,
                   &White_light_AE,
                   {0,0,0},
                   &White_light_ET_HH,
                   &White_light_ET_MM,
                   &White_light_ET_SS,
                   0,
                   &White_light_AD,
                   {0,0,0},
                   &White_light_DT_HH,
                   &White_light_DT_MM,
                   &White_light_DT_SS,
                   0,
                   &White_light_CYCLE,
                   1,
                   &White_light_CSD,
                   0,
                   0};   
Channel Grow_light =    {"Grow light:",                        //Фитосвет
                   0,
                   0,
                   &Grow_light_AE,
                   {0,0,0},
                   &Grow_light_ET_HH,
                   &Grow_light_ET_MM,
                   &Grow_light_ET_SS,
                   0,
                   &Grow_light_AD,
                   {0,0,0},
                   &Grow_light_DT_HH,
                   &Grow_light_DT_MM,
                   &Grow_light_DT_SS,
                    0,
                   &Grow_light_CYCLE,
                   1,
                   &Grow_light_CSD,
                   0,
                   0};   
Channel Water_pump =    {"Water pump:",                        //Насос
                   0,
                   0,
                   &Water_pump_AE,
                   {0,0,0},
                   &Water_pump_ET_HH,
                   &Water_pump_ET_MM,
                   &Water_pump_ET_SS,
                   0,
                   &Water_pump_AD,
                   {0,0,0},
                   &Water_pump_DT_HH,
                   &Water_pump_DT_MM,
                   &Water_pump_DT_SS,
                  
                   0,
                   &Water_pump_CYCLE,
                  
                   1,
                   &Water_pump_CSD,
                  
                   1,
                   0};                              
Channel *t_channel;

bool SET_command = false;
bool SDT_command = false;
bool RCS_command = false;
bool SAE_command = false;
bool SAD_command = false;
bool SCS_command = false;
bool SCC_command = false;

bool RSI_command = false;
bool WP_ON_command = false;
Time WP_manual_time = {0,0,30};

bool LDS_command = false;

bool First_run = false;
bool Flash_time_string = true;

uint8_t I2C_last_device;
bool I2C_last_write_mode;
            
//===РАЗНОЕ===
void Segment_Normalize(char *d_t_segment)
{
   if (strlen(d_t_segment)<2)
   {
      d_t_segment[2]=String_end_symbol;
      d_t_segment[1]=d_t_segment[0];
      d_t_segment[0]=&#39;0&#39;;
   }
}
uint8_t BCD_to_INT(uint8_t BCD)
{
   return (((BCD>>4)& 0b00001111)*10+(BCD & 0b00001111));
}
uint8_t INT_to_BCD(uint8_t Integer)
{
   uint8_t BCD_1=0;
   uint8_t BCD_2=0;
   uint8_t j=0;

   for (uint8_t i=0; i<90; i=i+10)
   {
      if (Integer-i<10 && Integer-i>=0)
      {
         BCD_1 = j;
         BCD_2 = Integer-i;
      }
      j++;
   }
   return (((BCD_1<<4)&0b11110000)|(BCD_2&0b00001111));
}    
void DATE_to_STRING(char *d_string, Date *date)
{
   char Date_string[3];
   char Month_string[3];
   char Year_string[3];

   itoa(date->DD,Date_string,10);
   itoa(date->MM,Month_string,10);
   itoa(date->YY,Year_string,10);

   //Нормализация строки даты
   Segment_Normalize(Date_string);
   Segment_Normalize(Month_string);
   Segment_Normalize(Year_string);
   
   int dsi = 0;

   //Дата
   for (int di=0;di<strlen(Date_string);di++)
   {
      d_string[dsi] = Date_string[di];
      dsi++;
   }
   d_string[dsi] = &#39;.&#39;;
   dsi++;
   for (int mi=0;mi<strlen(Month_string);mi++)
   {
      d_string[dsi] = Month_string[mi];
      dsi++;
   }
   d_string[dsi] = &#39;.&#39;;
   dsi++;
   for (int yi=0;yi<strlen(Year_string);yi++)
   {
      d_string[dsi] = Year_string[yi];
      dsi++;
   }
   d_string[dsi]= String_end_symbol;
}
void TIME_to_STRING(char *t_string, Time *time, bool show_sec=false, bool flash = false)
{
   char Hours_string[3];
   char Minutes_string[3];
   char Seconds_string[3];
   
   itoa(time->HH,Hours_string,10);
   itoa(time->MM,Minutes_string,10);
   itoa(time->SS,Seconds_string,10);

   //Нормализация строки времени
   Segment_Normalize(Hours_string);
   Segment_Normalize(Minutes_string);
   Segment_Normalize(Seconds_string);
   
   char dots = &#39; &#39;;
   
   if ((time->SS % 2) == 1 && flash == true)
   {
      dots = &#39; &#39;;
   }
   else
   {
      dots = &#39;:&#39;;
   }
   
   int tsi = 0;
   
   //Время
   for (int hi=0;hi<strlen(Hours_string);hi++)
   {
      t_string[tsi] = Hours_string[hi];
      tsi++;
   }
   t_string[tsi] = dots;
   tsi++;
   for (int mi=0;mi<strlen(Minutes_string);mi++)
   {
      t_string[tsi] = Minutes_string[mi];
      tsi++;
   }
   
   if (show_sec == true)
   {
      t_string[tsi] = dots;
      tsi++;
      for (int si=0;si<strlen(Seconds_string);si++)
      {
         t_string[tsi] = Seconds_string[si];
         tsi++;
      }
   }
   t_string[tsi]= String_end_symbol;
}
void DATE_TIME_to_STRING(char *d_t_string, Date *date, Time *time, bool show_sec=false, bool flash = false)
{
   char date_string[9];
   char time_string[6];
   
   DATE_to_STRING(date_string, date);
   TIME_to_STRING(time_string, time, show_sec, flash);
   
   int dtsi = 0;
   
   //Дата
   for (int i=0;i<strlen(date_string);i++)
   {
      d_t_string[dtsi] = date_string[i];
      dtsi++;
   }
   d_t_string[dtsi] = &#39; &#39;;
   dtsi++;
   d_t_string[dtsi] = &#39; &#39;;
   dtsi++;
   d_t_string[dtsi] = &#39; &#39;;
   dtsi++;
   //Время
   for (int i=0;i<strlen(time_string);i++)
   {
      d_t_string[dtsi] = time_string[i];
      dtsi++;
   }
   d_t_string[dtsi]= String_end_symbol;
}      
bool String_Compare(char *ds1, char *ds2, int ds1_start_pos, int ds2_start_pos, int length)
{
   bool Result = true;

   for (int i = 0; i<length; i++)
   {
      if (ds1[i+ds1_start_pos] != ds2[i+ds2_start_pos])
      {
         Result = false;
      }
   }
   
   return Result;
}
uint8_t Days_In_Month(uint8_t month, uint8_t year)
{
   if (year%4 == 0 && year != 0)
   {
      return Calendar_leap[month-1];
   }
   else
   {
      return Calendar[month-1];
   }
}
void Time_Add(Time *time, Time *add_time)
{
   if ((time->SS + add_time->SS) >= 60)
   {
      time->MM++;
      time->SS = time->SS + add_time->SS-60;
   }
   else
   {
      time->SS = time->SS + add_time->SS;
   }
   if ((time->MM + add_time->MM) >= 60)
   {
      time->HH++;
      time->MM = time->MM + add_time->MM-60;
   }
   else
   {
      time->MM = time->MM + add_time->MM;
   }
   if ((time->HH + add_time->HH) >= 24)
   {
      time->HH = time->HH + add_time->HH - 24;
   }
   else
   {
      time->HH = time->HH + add_time->HH;
   }
}
void Date_Add(Date *date, uint8_t days)
{
   for (uint8_t i=0; i<days;i++)
   {
      date->DD++;
      uint8_t d_i_m = Days_In_Month(date->MM, date->YY);
      if (date->DD > d_i_m)
      {
         date->MM++;
         if(date->MM >12)
         {
            date->YY++;
            if(date->YY >99)
            {
               date->YY = date->YY - 99;
            }
            date->MM = date->MM -12;
         }
         date->DD = date->DD - d_i_m;
      }
   }
}
void (*Reset)(void) = 0x0000;

void Light_Channel_Enable_String(char *data_string, Channel *channel)
{
   int chsi = 0;
   
   char En_caption[] = {"On:        "};
   char En_time[9];

   TIME_to_STRING(En_time, &channel->Enable_Time, 0);

   for (int i=0;i<strlen(En_caption);i++)
   {
      data_string[chsi] = En_caption[i];
      chsi++;
   }
   for (int i=0;i<strlen(En_time);i++)
   {
      data_string[chsi] = En_time[i];
      chsi++;
   }
   data_string[chsi]= String_end_symbol;
}
void Light_Channel_Disable_String(char *data_string, Channel *channel)
{
   int chsi = 0;
      
   char Dis_caption[] = {"Off:       "};
   char Dis_time[9];

   TIME_to_STRING(Dis_time, &channel->Disable_Time , 0);

   for (int i=0;i<strlen(Dis_caption);i++)
   {
      data_string[chsi] = Dis_caption[i];
      chsi++;
   }
   for (int i=0;i<strlen(Dis_time);i++)
   {
      data_string[chsi] = Dis_time[i];
      chsi++;
   }         
   data_string[chsi]= String_end_symbol;
}
void Water_Channel_Enable_String(char *data_string, Channel *channel)
{
   int chsi = 0;
   
   char En_caption[] = {"On:     "};
   char En_time[9];

   TIME_to_STRING(En_time, &channel->Enable_Time, 1);

   for (int i=0;i<strlen(En_caption);i++)
   {
      data_string[chsi] = En_caption[i];
      chsi++;
   }
   for (int i=0;i<strlen(En_time);i++)
   {
      data_string[chsi] = En_time[i];
      chsi++;
   }   
   data_string[chsi]= String_end_symbol;
}
void Water_Channel_Disable_String(char *data_string, Channel *channel)
{
   int chsi = 0;
   
   char Dis_caption[] = {"Off:    "};
   char Dis_time[9];

   TIME_to_STRING(Dis_time, &channel->Disable_Time , 1);

   for (int i=0;i<strlen(Dis_caption);i++)
   {
      data_string[chsi] = Dis_caption[i];
      chsi++;
   }
   for (int i=0;i<strlen(Dis_time);i++)
   {
      data_string[chsi] = Dis_time[i];
      chsi++;
   }            
   data_string[chsi]= String_end_symbol;
}
void Channel_AE_String(char *data_string, Channel *channel)
{
   char caption_ON[] =  {"Auto on:      On"};
   char caption_OFF[] = {"Auto on:     Off"};
      
   if (channel->Auto_Enable == 1)
   {
      strcpy(data_string,"Auto on:      On");
   }
   else
   {
      strcpy(data_string,"Auto on:     Off");
   }
}
void Channel_AD_String(char *data_string, Channel *channel)
{   
   if (channel->Auto_Disable == 1)
   {
      strcpy(data_string,"Auto off:     On");
   }
   else
   {
      strcpy(data_string,"Auto off:    Off");
   }
}
void Channel_State_String(char *data_string, Channel *channel)
{
   if (channel->State == 1)
   {
      strcpy(data_string,"Activated");
   }
   else
   {
      strcpy(data_string,"Deactivated");
   }
}
void Channel_Cycle_String(char *data_string, Channel *channel)
{
   uint8_t dsi = 0;

   char cycle[3];
   char cycle_caption_1[]= {"Cycle:"};
   char cycle_caption_2_1[]= {" day"};
   char cycle_caption_2_2[]= {" days"};
   
   itoa(channel->Cycle, cycle, 10);
   
   if (channel->Cycle != 0)
   {
      if (channel->Cycle > 1)
      {
         if (channel->Cycle > 9)
         {
            for (uint8_t i=0; i<strlen(cycle_caption_1); i++)
            {
               data_string[dsi]= cycle_caption_1[i];
               dsi++;
            }
            data_string[dsi]= &#39; &#39;;
            dsi++;
            data_string[dsi]= &#39; &#39;;
            dsi++;
            data_string[dsi]= &#39; &#39;;
            dsi++;
            
            for (uint8_t i=0; i<strlen(cycle); i++)
            {
               data_string[dsi]= cycle[i];
               dsi++;
            }
            for (uint8_t i=0; i<strlen(cycle_caption_2_2); i++)
            {
               data_string[dsi]= cycle_caption_2_2[i];
               dsi++;
            }
            data_string[dsi] = String_end_symbol;
         }
         else
         {
            for (uint8_t i=0; i<strlen(cycle_caption_1); i++)
            {
               data_string[dsi]= cycle_caption_1[i];
               dsi++;
            }
            data_string[dsi]= &#39; &#39;;
            dsi++;
            data_string[dsi]= &#39; &#39;;
            dsi++;
            data_string[dsi]= &#39; &#39;;
            dsi++;
            data_string[dsi]= &#39; &#39;;
            dsi++;
            
            for (uint8_t i=0; i= strlen(cycle); i++)
            {
               data_string[dsi]= cycle[i];
               dsi++;
            }
            for (uint8_t i=0; i<strlen(cycle_caption_2_1); i++)
            {
               data_string[dsi]= cycle_caption_2_1[i];
               dsi++;
            }
            data_string[dsi] = String_end_symbol;
         }
      }
      else
      {
         strcpy(data_string,"Cycle:     1 day");
      }
   }
   else
   {
      strcpy(data_string,"Cycle:  everyday");
   }
}
void Channel_Cycle_Start_Day_String(char *data_string, Channel *channel)
{
   uint8_t dsi = 0;
   char cycle_start_day[3];
   char cycle_start_day_caption[]= {"Next cycle:   "};
   
   itoa(channel->Cycle_Start_Day, cycle_start_day, 10);
   Segment_Normalize(cycle_start_day);
   
   for (uint8_t i=0; i<strlen(cycle_start_day_caption); i++)
   {
      data_string[dsi]= cycle_start_day_caption[i];
      dsi++;
   }
   
   for (uint8_t i=0; i<strlen(cycle_start_day); i++)
   {
      data_string[dsi]= cycle_start_day[i];
      dsi++;
   }
   data_string[dsi] = String_end_symbol;
}

//TIMERS
void TC1_Init(void)
{
   TCCR1B = (1 << CS12);
   TCNT1 = 65536-31250;       
   TIMSK1 = (1 << TOIE1);
}   

//===UART===
void UART_Send_Num(uint8_t data_num)
{
   char data_string[3];
   itoa(data_num,data_string,16);
   
   PORTB = 1<< PORTB5;
   for (int i=0; i<strlen(data_string); i++)
   {
      while( !(UCSR0A & (1<<UDRE0)))
      {
      }
      UDR0 = data_string[i];
   }
   PORTB = 0<< PORTB5;
}
void UART_Send_Char(char data_char)
{
   PORTB = 1<< PORTB5;
   
   while( !(UCSR0A & (1<<UDRE0)))
   {
   }
   UDR0 = data_char;
   
   PORTB = 0<< PORTB5;
}
void UART_Send_String(char *data_string)
{
   PORTB = 1<< PORTB5;
   for (int i=0; i<strlen(data_string); i++)
   {
      while( !(UCSR0A & (1<<UDRE0)))
      {
      }
      UDR0 = data_string[i];
   }
   PORTB = 0<< PORTB5;
}
void UART_Init(void)
{
   UBRR0H = 0;
   UBRR0L = 8;                                                                        //скорость передачи 115200 бит/с
   UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);                                 //активируем Tx, Rx и прерывания
   UCSR0C = (0<<UPM01)|(0<<UPM00)|(0<<USBS0)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00);     //8 bit, 1 stop bit
}

//EEPROM
void EEPROM_Init(void)
{
   if (eeprom_read_byte(&System_first_run) != 0)
   {
      eeprom_write_byte(&White_light_AE, 0);
      eeprom_write_byte(&White_light_ET_HH, 0);
      eeprom_write_byte(&White_light_ET_MM, 0);
      eeprom_write_byte(&White_light_ET_SS, 0);
      
      eeprom_write_byte(&White_light_AD, 0);
      eeprom_write_byte(&White_light_DT_HH, 0);
      eeprom_write_byte(&White_light_DT_MM, 0);
      eeprom_write_byte(&White_light_DT_SS, 0);
      eeprom_write_byte(&White_light_CYCLE, 0);
      eeprom_write_byte(&White_light_CSD, 1);
      
      eeprom_write_byte(&Grow_light_AE, 0);
      eeprom_write_byte(&Grow_light_ET_HH, 0);
      eeprom_write_byte(&Grow_light_ET_MM, 0);
      eeprom_write_byte(&Grow_light_ET_SS, 0);
      
      eeprom_write_byte(&Grow_light_AD, 0);
      eeprom_write_byte(&Grow_light_DT_HH, 0);
      eeprom_write_byte(&Grow_light_DT_MM, 0);
      eeprom_write_byte(&Grow_light_DT_SS, 0);
      eeprom_write_byte(&Grow_light_CYCLE, 0);
      eeprom_write_byte(&Grow_light_CSD, 1);
      
      eeprom_write_byte(&Water_pump_AE, 0);
      eeprom_write_byte(&Water_pump_ET_HH, 0);
      eeprom_write_byte(&Water_pump_ET_MM, 0);
      eeprom_write_byte(&Water_pump_ET_SS, 0);
      eeprom_write_byte(&Water_pump_CYCLE, 0);
      eeprom_write_byte(&Water_pump_CSD, 1);
      
      eeprom_write_byte(&Water_pump_AD, 0);
      eeprom_write_byte(&Water_pump_DT_HH, 0);
      eeprom_write_byte(&Water_pump_DT_MM, 0);
      eeprom_write_byte(&Water_pump_DT_SS, 0);
      
      eeprom_write_byte(&System_first_run, 0);
      
      First_run = true;
   }
}
void EEPROM_Read_Data(void)
{
   White_light.Auto_Enable = eeprom_read_byte(&White_light_AE);
   White_light.Enable_Time.HH = eeprom_read_byte(&White_light_ET_HH);
   White_light.Enable_Time.MM = eeprom_read_byte(&White_light_ET_MM);
   White_light.Enable_Time.SS = eeprom_read_byte(&White_light_ET_SS);
   
   White_light.Auto_Disable = eeprom_read_byte(&White_light_AD);
   White_light.Disable_Time.HH = eeprom_read_byte(&White_light_DT_HH);
   White_light.Disable_Time.MM = eeprom_read_byte(&White_light_DT_MM);
   White_light.Disable_Time.SS = eeprom_read_byte(&White_light_DT_SS);
   
   Grow_light.Auto_Enable = eeprom_read_byte(&Grow_light_AE);
   Grow_light.Enable_Time.HH = eeprom_read_byte(&Grow_light_ET_HH);
   Grow_light.Enable_Time.MM = eeprom_read_byte(&Grow_light_ET_MM);
   Grow_light.Enable_Time.SS = eeprom_read_byte(&Grow_light_ET_SS);
   
   Grow_light.Auto_Disable = eeprom_read_byte(&Grow_light_AD);
   Grow_light.Disable_Time.HH = eeprom_read_byte(&Grow_light_DT_HH);
   Grow_light.Disable_Time.MM = eeprom_read_byte(&Grow_light_DT_MM);
   Grow_light.Disable_Time.SS = eeprom_read_byte(&Grow_light_DT_SS);
   
   Water_pump.Auto_Enable = eeprom_read_byte(&Water_pump_AE);
   Water_pump.Enable_Time.HH = eeprom_read_byte(&Water_pump_ET_HH);
   Water_pump.Enable_Time.MM = eeprom_read_byte(&Water_pump_ET_MM);
   Water_pump.Enable_Time.SS = eeprom_read_byte(&Water_pump_ET_SS);
   Water_pump.Cycle = eeprom_read_byte(&Water_pump_CYCLE);
   Water_pump.Cycle_Start_Day = eeprom_read_byte(&Water_pump_CSD);
   
   Water_pump.Auto_Disable = eeprom_read_byte(&Water_pump_AD);
   Water_pump.Disable_Time.HH = eeprom_read_byte(&Water_pump_DT_HH);
   Water_pump.Disable_Time.MM = eeprom_read_byte(&Water_pump_DT_MM);
   Water_pump.Disable_Time.SS = eeprom_read_byte(&Water_pump_DT_SS);
}

//===IO Ports===
void IO_Init(void)
{
   DDRB  = 1<<PORTB5;                                               //Индикатор UART
   DDRD  = 1<<PORTD5 |1<<PORTD6|1<<PORTD7;                          //Исполнительные механизмы
}

//===I2C====
void I2C_Init(void)
{
   TWSR = 0;
   TWBR = ((F_CPU/F_SCL)-16)/2;
}
void I2C_TWINT_Wait(void)
{
   while(!(TWCR &(1<<TWINT)))
   {
      
   }
}
void I2C_Start(void)
{
   cli();
   _delay_us(10);
   TWCR =(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);   
   I2C_TWINT_Wait();
   sei();
}
void I2C_Stop(void)
{
   cli();
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
   _delay_us(10);
   sei();
}
void I2C_Send_Device_Address(uint8_t dev_address, bool write)
{
   cli();
   I2C_last_device = dev_address;
   I2C_last_write_mode = write;
   
   if (write == true)
   {
      TWDR = dev_address;
   }
   else
   {
      TWDR = dev_address|1;
   }
   
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Register_Address(uint8_t reg_address)
{
   cli();
   TWDR = reg_address;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Data_ACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
   sei();
}
void I2C_Send_Data_NACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
uint8_t I2C_Read_Data_ACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}
uint8_t I2C_Read_Data_NACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}

bool I2C_Check_Device(uint8_t dev_address)
{
   bool Result = false;
   I2C_Start();
   TWDR = dev_address;
   TWCR =(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
   while(!(TWCR &(1<<TWINT)))
   {
   }
   if((TWSR & 0xF8)== 0x18)
   {
      Result = true;
   }
   I2C_Stop();
   return Result;
}
void I2C_Device_Map(void)
{
   UART_Send_String("I2C network:");
   
   for (uint8_t dev_address=0x00; dev_address<0xFF; dev_address++)
   {
      if (I2C_Check_Device(dev_address)==true)
      {
         char text[25];
         itoa(dev_address,text,16);
         UART_Send_String(" 0x");
         UART_Send_String(text);
      }
   }
}

//===RTC===
void RTC_Set_System_Time(Time *time)
{
   I2C_Start();
   I2C_Send_Device_Address(RTC_module, 1);
   I2C_Send_Register_Address(0x00);
   I2C_Send_Data_ACK(INT_to_BCD(time->SS));
   I2C_Send_Data_ACK(INT_to_BCD(time->MM));
   I2C_Send_Data_NACK(INT_to_BCD(time->HH));
   I2C_Stop();
}
void RTC_Set_System_Date(Date *date)
{
   I2C_Start();
   I2C_Send_Device_Address(RTC_module, 1);
   I2C_Send_Register_Address(0x04);
   I2C_Send_Data_ACK(INT_to_BCD(date->DD));
   I2C_Send_Data_ACK(INT_to_BCD(date->MM));
   I2C_Send_Data_NACK(INT_to_BCD(date->YY));
   I2C_Stop();
}
void RTC_Get_System_Date_Time(void)
{
   RTC_error = !I2C_Check_Device(RTC_module);
   if (RTC_error == false)
   {
      I2C_Start();
      I2C_Send_Device_Address(RTC_module, 1);
      I2C_Send_Register_Address(0x00);
      I2C_Start();
      I2C_Send_Device_Address(RTC_module, 0);
      System_time.SS = BCD_to_INT(I2C_Read_Data_ACK());
      System_time.MM = BCD_to_INT(I2C_Read_Data_ACK());
      System_time.HH = BCD_to_INT(I2C_Read_Data_ACK());
      int8_t day_of_week = BCD_to_INT(I2C_Read_Data_ACK());
      System_date.DD = BCD_to_INT(I2C_Read_Data_ACK());
      System_date.MM = BCD_to_INT(I2C_Read_Data_ACK());
      System_date.YY = BCD_to_INT(I2C_Read_Data_NACK());
      I2C_Stop();
   }
   else
   {
      White_light.State = 0;
      Grow_light.State = 0;
      Water_pump.State = 0;
      PORTD = White_light.State<<PORTD5|Grow_light.State<<PORTD6| Water_pump.State<<PORTD7;
      Reset();
   }
}

//===LCD===
void LCD_Write_Command(uint8_t command)
{
   command = command & ~(1<<LCD_E);
   command = command | (1<<LCD_E);
   I2C_Send_Data_ACK(command);
   _delay_us(40);
   command = command & ~(1<<LCD_E);
   command = command | (0<<LCD_E);
   I2C_Send_Data_ACK(command);
   _delay_us(40);
}
void LCD_Send_Command(uint8_t data, bool BL, bool RS, bool RW)
{
   uint8_t LCD_input_buffer = 0;
   uint8_t data_H = data & 0b11110000;
   uint8_t data_L = (data<<4) & 0b11110000;

   LCD_input_buffer = data_H | BL<<LCD_BL| RS<<LCD_RS| RW<<LCD_RW;
   I2C_Send_Data_ACK(LCD_input_buffer);
   LCD_Write_Command(LCD_input_buffer);
   
   LCD_input_buffer = data_L | BL<<LCD_BL| RS<<LCD_RS| RW<<LCD_RW;
   I2C_Send_Data_ACK(LCD_input_buffer);
   LCD_Write_Command(LCD_input_buffer);
}
void LCD_Command_Clear_Display(void)
{
   LCD_Send_Command(0b00000001,1,0,0);      //Clear display
   _delay_ms(2);
}
void LCD_Command_Cursor_Return(void)
{
   LCD_Send_Command(0b00000010,0,0,0);
}
void LCD_Command_Put_Cursor(uint8_t x, uint8_t y)
{
   uint8_t addr = 0;  // line 0 begins at addr 0x00
   switch (y)
   {
      case 0: addr = 0x00;  break;
      case 1: addr = 0x40;  break;
   }
   
   LCD_Send_Command(0b10000000 + addr + x,1,0,0);
}
void LCD_Command_Display_Shift_Right(uint8_t count)
{
   for (uint8_t i=0;i<count;i++)
   {
      LCD_Send_Command(0b00011000,1,0,0);      //Cursor/display shift
   }
}
void LCD_Command_Cursor_Shift_Right(uint8_t count)
{
   for (uint8_t i=0;i<count;i++)
   {
      LCD_Send_Command(0b00010100,1,0,0);      //Cursor/display shift
   }
}
void LCD_Command_Cursor_Direction_Right(void)
{
   LCD_Send_Command(0b00000110,1,0,0);      //Entry mode set
}
void LCD_Command_Cursor_Direction_Left(void)
{
   LCD_Send_Command(0b00000100,1,0,0);      //Entry mode set
}

void LCD_Command_Send_Char(char symbol)
{
   uint8_t LCD_input_buffer = 0;
   uint8_t data_H = symbol & 0b11110000;
   uint8_t data_L = (symbol<<4) & 0b11110000;

   LCD_input_buffer = data_H | 1<<LCD_BL| 1<<LCD_RS| 0<<LCD_RW;
   I2C_Send_Data_ACK(LCD_input_buffer);
   LCD_Write_Command(LCD_input_buffer);
   
   LCD_input_buffer = data_L | 1<<LCD_BL| 1<<LCD_RS| 0<<LCD_RW;
   I2C_Send_Data_ACK(LCD_input_buffer);
   LCD_Write_Command(LCD_input_buffer);
}
void LCD_Command_Send_String_With_Delay(char *data_string, int8_t num)
{
   int delay = 100;
   LCD_Command_Put_Cursor(0,num);
   LCD_Command_Cursor_Direction_Right();
   for (int8_t i=0; i<strlen(data_string); i++)
   {
      LCD_Command_Send_Char(data_string[i]);
      _delay_ms(delay);
   }
}
void LCD_Command_Send_String(char *data_string, int8_t num)
{
   LCD_Command_Put_Cursor(0,num);
   LCD_Command_Cursor_Direction_Right();
   for (int8_t i=0; i<strlen(data_string); i++)
   {
      LCD_Command_Send_Char(data_string[i]);
   }
}

void LCD_Command_Clear_String_With_Delay(int8_t num)
{
   int delay = 25;
   LCD_Command_Put_Cursor(15,num);
   LCD_Command_Cursor_Direction_Left();
   for (int8_t i=0; i<16; i++)
   {
      LCD_Command_Send_Char(&#39; &#39;);
      _delay_ms(delay);
   }
   LCD_Command_Put_Cursor(0,num);
}
void LCD_Command_Clear_String(int8_t num)
{
   LCD_Command_Put_Cursor(15,num);
   LCD_Command_Cursor_Direction_Left();
   for (int8_t i=0; i<16; i++)
   {
      LCD_Command_Send_Char(&#39; &#39;);
   }
   LCD_Command_Put_Cursor(0,num);
}
void LCD_Init()
{
   _delay_ms(50);

   //Инициализация LCD в соответствии с блок-схемой в даташите
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Send_Command(0b00110011,0,0,0);
   _delay_us(100);
   LCD_Send_Command(0b00110010,0,0,0);
   _delay_ms(5);

   //Настройка LCD
   LCD_Send_Command(0b00101000,0,0,0);      //Function set
   LCD_Send_Command(0b00001000,0,0,0);      //Display on/off control
   LCD_Send_Command(0b00000001,0,0,0);      //Clear display
   _delay_ms(2);
   LCD_Send_Command(0b00000110,0,0,0);      //Entry mode set
   //Включение LCD
   LCD_Send_Command(0b00001100,1,0,0);      //Display on/off control
   I2C_Stop();
}

void LCD_Show_String(char *data_string, int8_t num)
{
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Command_Send_String_With_Delay(data_string, num);
   I2C_Stop();
}
void LCD_Clear_Display(void)
{
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Command_Clear_Display();
   I2C_Stop();
}
void LCD_Flash_String(char *data_string, int8_t num)
{
   int delay = 500;
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Command_Send_String_With_Delay(data_string, num);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(num);
   I2C_Stop();
}

void LCD_Flash_First_Run_Message(void)
{
   if (First_run == true)
   {
      int delay = 500;
      
      I2C_Start();
      I2C_Send_Device_Address(LCD_module,1);
      
      LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
      LCD_Command_Send_String_With_Delay("defaults loaded", 1);
      _delay_ms(delay);      
      I2C_Stop();
      
      First_run = false;
   }
}
void LCD_Flash_Reset_Message(void)
{
   int delay = 500;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
   LCD_Command_Send_String_With_Delay("loading defaults", 1);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
   LCD_Command_Send_String_With_Delay("restarting", 1);
   _delay_ms(delay);
   
   I2C_Stop();      
}
void LCD_Flash_Start_Message(void)
{
   int delay = 500;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
   LCD_Command_Send_String_With_Delay("started", 1);
   _delay_ms(delay);
   
   I2C_Stop();
}
void LCD_Flash_RTC_Error_Message(void)
{
   int delay = 1000;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   LCD_Command_Send_String_With_Delay("G.E.C.K. error!", 0);
   LCD_Command_Send_String_With_Delay("check RTC", 1);
   _delay_ms(delay);
   
   I2C_Stop();
}

void LCD_Flash_System_Time_Updated_Message(void)
{
   int delay = 500;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
   LCD_Command_Send_String_With_Delay("Time updated", 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_System_Date_Updated_Message(void)
{
   int delay = 500;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay("G.E.C.K.", 0);
   LCD_Command_Send_String_With_Delay("Date updated", 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_Info_Message(Channel *channel)
{
   int delay = 500;
   
   char Enable_Data[17];
   char Disable_Data[17];
   char State_Data[17];
   char AE_Data[17];
   char AD_Data[17];
   char Cycle_Data[17];
   char Cycle_Start_Day_Data[17];

   Channel_State_String(State_Data, channel);
   Channel_AE_String(AE_Data, channel);
   Channel_AD_String(AD_Data, channel);
   
   if (channel->Water == 1)
   {
      Water_Channel_Enable_String(Enable_Data, channel);
      Water_Channel_Disable_String(Disable_Data, channel);
      Channel_Cycle_String(Cycle_Data, channel);
      Channel_Cycle_Start_Day_String(Cycle_Start_Day_Data, channel);
   }
   else
   {
      Light_Channel_Enable_String(Enable_Data, channel);
      Light_Channel_Disable_String(Disable_Data, channel);
   }
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(State_Data, 1);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Send_String_With_Delay(AE_Data, 1);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Send_String_With_Delay(Enable_Data, 1);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Send_String_With_Delay(AD_Data, 1);
   _delay_ms(delay);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Send_String_With_Delay(Disable_Data, 1);
   _delay_ms(delay);
   if (channel->Water == 1)
   {
      LCD_Command_Clear_String_With_Delay(1);
      LCD_Command_Send_String_With_Delay(Cycle_Data, 1);
      _delay_ms(delay);
      if (channel->Cycle>0)
      {
         LCD_Command_Clear_String_With_Delay(1);
         LCD_Command_Send_String_With_Delay(Cycle_Start_Day_Data, 1);
         _delay_ms(delay);
      }   
   }
   I2C_Stop();

   Flash_time_string = true;
}
void LCD_Flash_Ch_ET_Set_Message(Channel *channel)
{
   int delay = 500;
   
   char Enable_Data[17];
   
   if (channel->Water == 1)
   {
      Water_Channel_Enable_String(Enable_Data, channel);
   }
   else
   {
      Light_Channel_Enable_String(Enable_Data, channel);
   }
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(Enable_Data, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_AE_Message(Channel *channel)
{
   int delay = 500;
   
   char Data[17];
   
   if (channel->Auto_Enable == 1)
   {
      strcpy(Data,"Auto enable ON");
   }
   else
   {
      strcpy(Data,"Auto enable OFF");
   }
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(Data, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_DT_Set_Message(Channel *channel)
{
   int delay = 500;
   
   char Enable_Data[17];
   char Disable_Data[17];
   
   if (channel->Water == 1)
   {
      Water_Channel_Disable_String(Disable_Data, channel);
   }
   else
   {
      Light_Channel_Disable_String(Disable_Data, channel);
   }
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(Disable_Data, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_AD_Message(Channel *channel)
{
   int delay = 500;
   
   char Data[17];
   
   if (channel->Auto_Disable == 1)
   {
      strcpy(Data,"Auto disable ON");
   }
   else
   {
      strcpy(Data,"Auto disable OFF");
   }
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(Data, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_State_Message(Channel *channel)
{
   int delay = 500;
   
   char Data[17];
   
   Channel_State_String(Data, channel);

   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(Data, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_Ch_Cycle_Message(Channel *channel)
{
   int delay = 500;
   
   char cycle_data[17];
   char cyc_st_day_data[17];
   
   Channel_Cycle_String(cycle_data, channel);
   Channel_Cycle_Start_Day_String(cyc_st_day_data, channel);

   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(channel->Name, 0);
   LCD_Command_Send_String_With_Delay(cycle_data, 1);
   _delay_ms(delay);
   if (channel->Cycle>0)
   {
      LCD_Command_Clear_String_With_Delay(1);
      LCD_Command_Send_String_With_Delay(cyc_st_day_data, 1);
      _delay_ms(delay);
   }
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Flash_System_Info_Message(void)
{
   int delay = 500;
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(Device_type, 0);
   LCD_Command_Send_String_With_Delay(Firmware_version, 1);
   _delay_ms(delay);
   
   I2C_Stop();
   
   Flash_time_string = true;
}

void LCD_Show_Time(void)
{
   int delay = 500;
   char Date_Time_Caption[] = {"Date & time:    "};
   char Date_Time_Data[17];
   
   DATE_TIME_to_STRING(Date_Time_Data, &System_date, &System_time, false, true);
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);
   
   if (Flash_time_string == true)
   {
      LCD_Command_Clear_String_With_Delay(1);
      LCD_Command_Clear_String_With_Delay(0);
      LCD_Command_Send_String_With_Delay(Date_Time_Caption, 0);
      LCD_Command_Send_String_With_Delay(Date_Time_Data, 1);
      Flash_time_string = false;
   }
   else
   {
      LCD_Command_Send_String(Date_Time_Caption, 0);
      LCD_Command_Send_String(Date_Time_Data, 1);
   }
   
   I2C_Stop();
}
void LCD_Show_Watering_Message(void)
{
   char watering_caption[] = {"Watering..."};
   
   I2C_Start();
   
   I2C_Send_Device_Address(LCD_module,1);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   LCD_Command_Send_String_With_Delay(watering_caption, 0);
   
   I2C_Stop();
   
   Flash_time_string = true;
}
void LCD_Show_Timer(Time *stop_time)
{
   char timer_caption [] = {"Time:   "};
   char timer_time [9];
   char timer[17];
      
   int interval_s = 0;
   int start = 3600*System_time.HH+60*System_time.MM+System_time.SS;
   int stop = 3600*stop_time->HH+60*stop_time->MM+stop_time->SS;
   
   if (stop>=start)
   {
      interval_s = stop-start;
   }
   else
   {
      interval_s = 86400 - start + stop;
   }
   
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);   
   
   while (interval_s > 0)
   {
      int HH = interval_s/3600;
      int MM = (interval_s - HH*3600)/60;
      int SS = interval_s - HH*3600 - MM*60;
      
      Time interval = {HH,MM,SS};
      TIME_to_STRING(timer_time, &interval, true, false);

      uint8_t tsi = 0;

      for (uint8_t i=0; i<strlen(timer_caption); i++)
      {
         timer[tsi] = timer_caption[i];
         tsi++;
      }
      for (uint8_t i=0; i<strlen(timer_time); i++)
      {
         timer[tsi] = timer_time[i];
         tsi++;
      }
      timer[tsi]=String_end_symbol;

      LCD_Command_Send_String_With_Delay(timer, 1);
      
      int start = 3600*System_time.HH+60*System_time.MM+System_time.SS;
      
      if (stop>=start)
      {
         interval_s = stop-start;
      }
      else
      {
         interval_s = 86400 - start + stop;
      }
   }
   I2C_Stop();
}
void LCD_Close_Watering_Message(void)
{
   I2C_Start();
   I2C_Send_Device_Address(LCD_module,1);   
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Send_String_With_Delay("Finished",1);
   _delay_ms(500);
   LCD_Command_Clear_String_With_Delay(1);
   LCD_Command_Clear_String_With_Delay(0);
   
   I2C_Stop();
}

//===ОБРАБОТКА ВХОДНОЙ КОМАНДЫ
void Command_GET(char *command_string, char *input_command)
{
   for (uint8_t i=0; i<3; i++)
   {
      command_string[i]=input_command[i];
   }
}
void Channel_GET(char *channel_string, char *input_command)
{
   for (uint8_t i=3; i<5; i++)
   {
      channel_string[i-3]=input_command[i];
   }
}
void State_GET(uint8_t *state, char *input_command)
{
   if (input_command[5]==&#39;1&#39;)
   {
      (*state) = 1;
   }
   else
   {
      (*state) = 0;
   }
}
void Cycle_GET(uint8_t *cycle, uint8_t *cycle_start_day, char *input_command)
{
   uint8_t cycle_result;
   char cycle_string[3];
   for (uint8_t i=5; i<7; i++)
   {
      cycle_string[i-5]=input_command[i];
   }
   cycle_string[2]=String_end_symbol;
   cycle_result = atoi(cycle_string);
      
   uint8_t day_result;
   char day_string[3];
   for (uint8_t i=7; i<9; i++)
   {
      day_string[i-7]=input_command[i];
   }
   day_string[2]=String_end_symbol;
   day_result = atoi(day_string);
   
   if (day_result > 27)
   {
      day_result = 27;
   }
   if (day_result == 0)
   {
      day_result = 1;
   }
   if (cycle_result > 27)
   {
      cycle_result = 27;
   }
   
   (*cycle) = cycle_result;
   (*cycle_start_day) = day_result;
}

uint8_t Time_HH_GET(char *input_command)
{
   uint8_t result;
   char HH[3];
   for (int i=6; i<8; i++)
   {
      HH[i-6]=input_command[i];
   }
   HH[2]=String_end_symbol;
   result = atoi(HH);
   
   if (result > 24)
   {
      return 0;
   }
   else
   {
      return result;
   }
}
uint8_t Time_MM_GET(char *input_command)
{
   uint8_t result;
   char MM[3];
   for (int i=8; i<10; i++)
   {
      MM[i-8]=input_command[i];
   }
   MM[2]=String_end_symbol;
   result =   atoi(MM);
   
   if (result > 59)
   {
      return 0;
   }
   else
   {
      return result;
   }
}
uint8_t Time_SS_GET(char *input_command)
{
   uint8_t result;
   char SS[3];
   for (int i=10; i<12; i++)
   {
      SS[i-10]=input_command[i];
   }
   SS[2]=String_end_symbol;   
   result = atoi(SS);
   
   if (result > 59)
   {
      return 0;
   }
   else
   {
      return result;
   }
}
void Time_GET(Time *time, char *input_command)
{
   time->HH = Time_HH_GET(input_command);
   time->MM = Time_MM_GET(input_command);
   time->SS = Time_SS_GET(input_command);
}

uint8_t Date_DD_GET(char *input_command)
{
   uint8_t result;
   char DD[3];
   for (int i=6; i<8; i++)
   {
      DD[i-6]=input_command[i];
   }
   DD[2]=String_end_symbol;
   result = atoi(DD);
   
   if (result > 31 || result == 0)
   {
      return 1;
   }
   else
   {
      return result;
   }
}
uint8_t Date_MM_GET(char *input_command)
{
   uint8_t result;
   char MM[3];
   for (int i=8; i<10; i++)
   {
      MM[i-8]=input_command[i];
   }
   MM[2]=String_end_symbol;
   result = atoi(MM);
   
   if (result > 12 || result == 0)
   {
      return 1;
   }
   else
   {
      return result;
   }
}
uint8_t Date_YY_GET(char *input_command)
{
   char YY[3];
   for (int i=10; i<12; i++)
   {
      YY[i-10]=input_command[i];
   }
   YY[2]=String_end_symbol;
   return atoi(YY);
}
void Date_GET(Date *date, char *input_command)
{
   date->DD = Date_DD_GET(input_command);
   date->MM = Date_MM_GET(input_command);
   date->YY = Date_YY_GET(input_command);
}

void Channel_Auto_Enable_SET(Channel *channel, uint8_t *state)
{
   channel->Auto_Enable = (*state);
   uint8_t *eeprom_AE = channel->EEPROM_AE;
   eeprom_write_byte(eeprom_AE, (*state));
}
void Channel_Auto_Disable_SET(Channel *channel, uint8_t *state)
{
   channel->Auto_Disable = (*state);
   uint8_t *eeprom_AD = channel->EEPROM_AD;
   eeprom_write_byte(eeprom_AD, (*state));
}
void Channel_Enable_Time_SET(Channel *channel, Time *time)
{
   channel->Enable_Time.HH = time->HH;
   channel->Enable_Time.MM = time->MM;
   channel->Enable_Time.SS = time->SS;
   
   uint8_t *eeprom_ET_HH = channel->EEPROM_ET_HH;
   uint8_t *eeprom_ET_MM = channel->EEPROM_ET_MM;
   uint8_t *eeprom_ET_SS = channel->EEPROM_ET_SS;
   
   eeprom_write_byte(eeprom_ET_HH, time->HH);
   eeprom_write_byte(eeprom_ET_MM, time->MM);
   eeprom_write_byte(eeprom_ET_SS, time->SS);
}
void Channel_Disable_Time_SET(Channel *channel, Time *time)
{
   channel->Disable_Time.HH = time->HH;
   channel->Disable_Time.MM = time->MM;
   channel->Disable_Time.SS = time->SS;
   
   uint8_t *eeprom_DT_HH = channel->EEPROM_DT_HH;
   uint8_t *eeprom_DT_MM = channel->EEPROM_DT_MM;
   uint8_t *eeprom_DT_SS = channel->EEPROM_DT_SS;
   
   eeprom_write_byte(eeprom_DT_HH, time->HH);
   eeprom_write_byte(eeprom_DT_MM, time->MM);
   eeprom_write_byte(eeprom_DT_SS, time->SS);
}
void Channel_Cycle_SET(Channel *channel, uint8_t *cycle, uint8_t *cycle_start_day)
{
   channel->Cycle = (*cycle);
   channel->Cycle_Start_Day = (*cycle_start_day);
   
   uint8_t *eeprom_CYCLE = channel->EEPROM_CYCLE;
   uint8_t *eeprom_CSD = channel->EEPROM_CSD;
   
   eeprom_write_byte(eeprom_CYCLE, (*cycle));
   eeprom_write_byte(eeprom_CSD, (*cycle_start_day));
}


//===ЛОГИКА ВЫХОДНЫХ КАНАЛОВ===
void Channel_ENABLE(Channel *channel, bool force_enable = false)
{
   channel->State = 1;
   if (force_enable == true)
   {
      channel->Force_Enable = 1;
   }
}
void Channel_DISABLE(Channel *channel, bool force_disable = false)
{
   channel->State = 0;
   channel->Force_Enable = 0;
   
   if (force_disable == true)
   {
      Channel_Auto_Enable_SET(channel, 0);
   }
}
void Outputs_SET(void)
{
   PORTD = White_light.State<<PORTD5|Grow_light.State<<PORTD6| Water_pump.State<<PORTD7;
}
void Light_Channel_Logick(Channel *channel)
{
   if (channel->Auto_Enable == 1)
   {
      if (channel->Enable_Time.HH == System_time.HH)
      {
         if (channel->Enable_Time.MM == System_time.MM)
         {
            Channel_ENABLE(channel);
         }
      }
   }
   if (channel->Auto_Disable == 1)
   {
      if (channel->Disable_Time.HH == System_time.HH)
      {
         if (channel->Disable_Time.MM == System_time.MM)
         {
            Channel_DISABLE(channel);
         }
      }
   }
   Outputs_SET();
}
void Water_Channel_Timer_ON (Channel *channel)
{
   Time disable_time;
   if (channel->Force_Enable == 1)
   {
      disable_time = {System_time.HH, System_time.MM, System_time.SS};
      Time_Add(&disable_time, &WP_manual_time);
   }
   else
   {
      disable_time = {channel->Disable_Time.HH, channel->Disable_Time.MM, channel->Disable_Time.SS};
   }

   LCD_Show_Watering_Message();
   LCD_Show_Timer(&disable_time);
   Channel_DISABLE(channel);
   Outputs_SET();
   LCD_Close_Watering_Message();
}
void Water_Channel_Logick(Channel *channel)
{   
   bool start_timer = false;   

   if (((channel->Cycle > 0 && channel->Cycle_Start_Day == System_date.DD)||(channel->Cycle ==0))&&(channel->Auto_Enable == 1 && channel->Auto_Disable == 1))
   {
      if (channel->Enable_Time.HH == System_time.HH)
      {
         if (channel->Enable_Time.MM == System_time.MM)
         {
            if (channel->Enable_Time.SS == System_time.SS)
            {
               Channel_ENABLE(channel);
               WP_ON_command = true;
            }
         }
      }   
   }
}
void Cycle_Start_Day_Update(Channel *channel)
{
   if (channel->Cycle >0)
   {
      Date cycle_start_date = {channel->Cycle_Start_Day, System_date.MM, System_date.YY};
   
      while (System_date.DD>cycle_start_date.DD && System_date.MM==cycle_start_date.MM)
      {
         Date_Add(&cycle_start_date, channel->Cycle+1);
      }
      uint8_t cycle_start_day = cycle_start_date.DD;
      uint8_t cycle = channel->Cycle;
   
      Channel_Cycle_SET(channel, &cycle, &cycle_start_day);
   }
}
void Output_Logick_Execute(void)
{
   Light_Channel_Logick(&White_light);
   Light_Channel_Logick(&Grow_light);
   Water_Channel_Logick(&Water_pump);
}

//ВЫПОЛНЕНИЕ ПОЛЬЗОВАТЕЛЬСКИХ КОММАНД
void IC_Set_System_Date(char *input_command)
{
   Date_GET(&t_date, input_command);
   SSD_command = true;
}
void IC_Set_System_Time(char *input_command)
{
   Time_GET(&t_time, input_command);
   SST_command = true;
}
void IC_Set_Ch_Enable_Time(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   Time time = {0,0,0};
   Time_GET(&time, input_command);
   SET_command = true;
   
   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      Channel_Enable_Time_SET(&White_light, &time);
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      Channel_Enable_Time_SET(&Grow_light, &time);
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      Channel_Enable_Time_SET(&Water_pump, &time);
      t_channel = &Water_pump;
   }
}
void IC_Set_Ch_Auto_Enable_State(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   uint8_t state = 0;
   State_GET(&state, input_command);
   SAE_command = true;
   
   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      Channel_Auto_Enable_SET(&White_light, &state);
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      Channel_Auto_Enable_SET(&Grow_light, &state);
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      Channel_Auto_Enable_SET(&Water_pump, &state);
      t_channel = &Water_pump;
   }
}
void IC_Set_Ch_Disable_Time(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   Time time = {0,0,0};
   Time_GET(&time, input_command);
   SDT_command = true;

   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      Channel_Disable_Time_SET(&White_light, &time);
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      Channel_Disable_Time_SET(&Grow_light, &time);
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      Channel_Disable_Time_SET(&Water_pump, &time);
      t_channel = &Water_pump;
   }
}
void IC_Set_Ch_Auto_Disable_State(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   uint8_t state = 0;
   State_GET(&state, input_command);
   SAD_command = true;
   
   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      Channel_Auto_Disable_SET(&White_light, &state);
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      Channel_Auto_Disable_SET(&Grow_light, &state);
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      Channel_Auto_Disable_SET(&Water_pump, &state);
      t_channel = &Water_pump;
   }
}
void IC_Set_Ch_State(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   uint8_t state = 0;
   State_GET(&state, input_command);
   SCS_command =true;
   
   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      if (state == 0)
      {
         Channel_DISABLE(&White_light, true);
      }
      else
      {
         Channel_ENABLE(&White_light, true);
      }
      Outputs_SET();
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      if (state == 0)
      {
         Channel_DISABLE(&Grow_light, true);
      }
      else
      {
         Channel_ENABLE(&Grow_light, true);
      }
      Outputs_SET();
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      if (state == 0)
      {
         Channel_DISABLE(&Water_pump, true);
      }
      else
      {
         Channel_ENABLE(&Water_pump, true);
         WP_ON_command = true;
      }
      t_channel = &Water_pump;
   }
}
void IC_Set_Ch_Cycle(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   uint8_t cycle = 0;
   uint8_t cycle_start_day = 1;
   Cycle_GET(&cycle, &cycle_start_day, input_command);
   SCC_command =true;

   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      Channel_Cycle_SET(&Water_pump, &cycle, &cycle_start_day);
      t_channel = &Water_pump;
   }
}
void IC_Load_Defaults(void)
{
   eeprom_write_byte(&System_first_run, 1);
   LDS_command = true;
}
void IC_Report_Ch_Settings(char *input_command)
{
   char channel_type[2];
   Channel_GET(channel_type, input_command);
   RCS_command = true;
   
   if (String_Compare(channel_type, "wl", 0, 0, 2) == true)
   {
      t_channel = &White_light;
   }
   if (String_Compare(channel_type, "gl", 0, 0, 2) == true)
   {
      t_channel = &Grow_light;
   }
   if (String_Compare(channel_type, "wp", 0, 0, 2) == true)
   {
      t_channel = &Water_pump;
   }
}
void IC_Report_System_Info()
{
      UART_Send_String(Device_type);
      UART_Send_String("\n\r");
      UART_Send_String(Firmware_version);
      UART_Send_String("\n\r");
      
      RSI_command = true;
}
void Input_Command_Execute(char *input_command)
{
   char command_type[3];
   Command_GET(command_type, input_command);
   
   if (String_Compare(command_type, "ssd", 0, 0, 3) == true)   //Установка системной даты
   {
      IC_Set_System_Date(input_command);
   }
   if (String_Compare(command_type, "sst", 0, 0, 3) == true)   //Установка системного времени
   {
      IC_Set_System_Time(input_command);
   }
   if (String_Compare(command_type, "set", 0, 0, 3) == true)   //Установка времени автоматического включения
   {
      IC_Set_Ch_Enable_Time(input_command);
   }
   if (String_Compare(command_type, "sae", 0, 0, 3) == true)   //Разрешения автоматического включения
   {
      IC_Set_Ch_Auto_Enable_State(input_command);
   }
   if (String_Compare(command_type, "sdt", 0, 0, 3) == true)   //Установка времени автоматического выключения
   {
      IC_Set_Ch_Disable_Time(input_command);
   }
   if (String_Compare(command_type, "sad", 0, 0, 3) == true)   //Разрешения автоматического выключения
   {
      IC_Set_Ch_Auto_Disable_State(input_command);
   }
   if (String_Compare(command_type, "scs", 0, 0, 3) == true)   //Включение или выключение канала вручную
   {
      IC_Set_Ch_State(input_command);      
   }
   if (String_Compare(command_type, "scc", 0, 0, 3) == true)   //Цикличность включения канала
   {
      IC_Set_Ch_Cycle(input_command);
   }
   if (String_Compare(command_type, "rsi", 0, 0, 3) == true)   //Запрос состояния системы
   {
      IC_Report_System_Info();
   }
   if (String_Compare(command_type, "lds", 0, 0, 3) == true)   //Сброс настроек на умолчание
   {
      IC_Load_Defaults();
   }
   if (String_Compare(command_type, "rcs", 0, 0, 3) == true)   //Запрос настроек канала
   {
      IC_Report_Ch_Settings(input_command);
   }
}   

//ОБРАБОТКА ПАКЕТОВ ОТ ВНУТРЕННИХ УСТРОЙСТВ


//===ПРЕРЫВАНИЯ===
ISR(USART_RX_vect)
{   
   char UART_symbol = UDR0;
   
   if (UART_user_control == true)
   {
      if (UART_symbol == UART_Stop_symbol)
      {
         UART_stop_symbol_detected = true;
         Input_command[IC_c]=String_end_symbol;
      }
      if(UART_start_symbol_detected == true && UART_stop_symbol_detected == false)
      {
         Input_command[IC_c]=UART_symbol;
         IC_c++;
      }
      if (UART_symbol == UART_Start_symbol)
      {
         UART_start_symbol_detected = true;
         UART_stop_symbol_detected = false;
         IC_c = 0;
      }
      if (UART_start_symbol_detected == true && UART_stop_symbol_detected == true)
      {
         Input_Command_Execute(Input_command);
         
         UART_start_symbol_detected = false;
         UART_stop_symbol_detected = false;
      }
      if (IC_c == Input_command_size)
      {
         IC_c = 0;
         UART_start_symbol_detected = false;
         UART_stop_symbol_detected = false;
      }
   }
   else
   {
      
   }      
         
}
ISR(TIMER1_OVF_vect)
{
   TCNT1 = 65536-31250;
   
   uint8_t I2C_reurn_device = I2C_last_device;
   bool I2C_return_write_mode = I2C_last_write_mode;

   I2C_Stop();
   
   RTC_Get_System_Date_Time();
   Output_Logick_Execute();
   
   I2C_Start();
   I2C_Send_Device_Address(I2C_reurn_device,I2C_return_write_mode);
}   
ISR(TWI_vect)
{
}

//===ОСНОВНАЯ ПРОГРАММА===
int main(void)
{
   //Инициализация
   TC1_Init();
   I2C_Init();
   UART_Init();
   LCD_Init();
   IO_Init();
   EEPROM_Init();

   //Если выполняется первый запуск
   LCD_Flash_First_Run_Message();
   
   //Чтение из EEPROM
   EEPROM_Read_Data();

   //Отображение приветствия на LCD
   LCD_Flash_Start_Message();
   
   sei();
   
   while (RTC_error == true)
   {
      LCD_Flash_RTC_Error_Message();
      cli();
   }         
   while (RTC_error != true)
   {
      //Отображение информации
      LCD_Show_Time();
      //Обновление цикла включения
      Cycle_Start_Day_Update(&Water_pump);
      
      if (LDS_command == true)
      {
         LCD_Flash_Reset_Message();
         Reset();
      }
      if (SSD_command == true)
      {
         RTC_Set_System_Date (&t_date);
         LCD_Flash_System_Date_Updated_Message();
         SSD_command = false;   
      }
      if (SST_command == true)
      {
         RTC_Set_System_Time (&t_time);
         LCD_Flash_System_Time_Updated_Message();
         SST_command = false;
      }   
      if (SET_command == true)
      {
         LCD_Flash_Ch_ET_Set_Message(t_channel);
         SET_command = false;
      }
      if (SDT_command == true)
      {
         LCD_Flash_Ch_DT_Set_Message(t_channel);
         SDT_command = false;
      }
      if (RCS_command == true)
      {
         LCD_Flash_Ch_Info_Message(t_channel);
         RCS_command = false;
      }
      if (SAE_command == true)
      {
         LCD_Flash_Ch_AE_Message(t_channel);
         SAE_command = false;
      }
      if (SAD_command == true)
      {
         LCD_Flash_Ch_AD_Message(t_channel);
         SAD_command = false;
      }
      if (SCS_command == true)
      {
         LCD_Flash_Ch_State_Message(t_channel);
         SCS_command = false;
      }
      if (SCC_command == true)
      {
         LCD_Flash_Ch_Cycle_Message(t_channel);
         SCC_command = false;
      }
      if (RSI_command == true)
      {   
         LCD_Flash_System_Info_Message();
         RSI_command = false;
      }
      if (WP_ON_command == true)
      {
         Water_Channel_Timer_ON(&Water_pump);
         WP_ON_command = false;
      }
   }
}


Добавлено after 56 minutes 7 seconds:
Но я ставлю на то что у Вас неверная работа с дисплеем/и2си.

Судя по отладке Вы правы, так как ложится только дисплей а шестеренки МК продолжают крутиться. осталось понять где я сворачиваю не туда


Вернуться наверх
 
Выбираем схему BMS для заряда литий-железофосфатных (LiFePO4) аккумуляторов

Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.

Подробнее>>
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Пт ноя 17, 2017 09:22:29 
Мудрый кот

Карма: 20
Рейтинг сообщений: 145
Зарегистрирован: Вс дек 25, 2016 08:34:54
Сообщений: 1849
Рейтинг сообщения: 0
Блин одна и та же ошибка
Спойлер
Код:
uint8_t I2C_Read_Data_NACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR = (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}
Надо так
Спойлер
Код:
uint8_t I2C_Read_Data_NACK(void)
{
   cli();
   uint8_t Result = 0;
   TWCR &= (~(1<<(TWEA)));
   TWCR |= (1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   Result = TWDR;
   return Result;
   sei();
}
У вас NASK не формируется, а бит TWEA остается поднятым, смотрите в отладчике.
И еще при передаче байта не надо формировать NASK, передачу закончили командой СТОП.
Если вы формируете NASK при передаче
Спойлер
Код:
void I2C_Send_Data_NACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR =(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
то сбрасывайте бит TWEA
Спойлер
Код:
void I2C_Send_Data_NACK(uint8_t data)
{
   cli();
   TWDR = data;
   TWCR &= (~(1<<(TWEA)));
   TWCR |=(1<<TWINT)|(1<<TWEN);
   I2C_TWINT_Wait();
   sei();
}
И за чем это I2C_TWINT_Wait();, лишний void (void), вы этим не сэкономите, пишите нормально while(!(TWCR &(1<<TWINT))) { } , и еще вроде как цикл while () {} должен заканчиваться этим знаком ;
Жесть
Спойлер
Код:
   return Result;
   sei();


Вернуться наверх
 
Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.

Подробнее>>
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Пт ноя 17, 2017 10:03:39 
Поставщик валерьянки для Кота

Карма: 16
Рейтинг сообщений: 329
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Сообщений: 2222
Откуда: Tashkent
Рейтинг сообщения: 3
Цитата:
Тут дело не в дисплее, а в размере сообщения выводимом на него,


И что? Сообщение(строка) состоит из символов и записывается в такой дисплей только по одной букве. Меж двух букв переключаемся на ту задачу какую надо.
Но не так варварски как это реализовано сейчас.

Какое я вижу решение.
Отказаться от почти всех delay_ms. Ну разве что за исключением задержек отправки команд ЖКИ, которые сильно работу прибора не тормозят с точки зрения пользователя.
Полностью отделить задачи выполнения логики от задач индикации и считывания времени.
Всё это реализовывается на конечных автоматах. Функции между собой общаются посредством передачи буферов и флагов. Но не тормозят работу системы, а возвращают управление автомату так скоро, как это станет возможным. К примеру через 100-200 мкс, а не через 500-1000мс как сейчас.

Чтоб сильно не ломать существующую программу можно написать логику обмена по I2C через прерывания. Там где вызывается слишком большой delay и нет обмена по I2C разрешать запросы таймера для обновления времени.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Пт ноя 17, 2017 10:19:41 
Родился
Аватар пользователя

Зарегистрирован: Чт ноя 16, 2017 17:04:14
Сообщений: 15
Откуда: Сургут
Рейтинг сообщения: 0
ну delay в функциях вывода там для "красивого" выбегания букв. вашу идею понял, попробую реализовать


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Atmega328p работа с TWI в прерываниях (AtmelStudio 6)
СообщениеДобавлено: Вс ноя 19, 2017 01:44:21 
Родился
Аватар пользователя

Зарегистрирован: Чт ноя 16, 2017 17:04:14
Сообщений: 15
Откуда: Сургут
Рейтинг сообщения: 0
Разобрался. Если на шину подряд запихонить несколько стартов то получаем висяк.

Что сделал:
- запретил прерывания таймера во время выполнения атомарных функций TWI (чтение байта, запись байта, старт и стоп)
- прикрутил флаг выдачи старта в шину (после STA взводится постле STO снимается)
- в прерывании проверяю, если старт был то выдаю в шину стоп и опрашиваю RTC, если был стоп то просто опрашиваю RTC.
- если не было стопа то перед выходом из прерывания выдаю в шину старт и возвращаюсь к последнему использованному устройству если такое было

кстати мух от котлет тоже поделил. спасибо за советы


Вернуться наверх
 
Показать сообщения за:  Сортировать по:  Вернуться наверх
Начать новую тему Ответить на тему  [ Сообщений: 8 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 42


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB
Extended by Karma MOD © 2007—2012 m157y
Extended by Topic Tags MOD © 2012 m157y