/* RTC rutiny vyuzivajici interni RTC mikrokontroleru */
#include "My_rtc.h"
//#include "main.h"
//#include "stm32f10x.h"
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_tim.h"
#include "stm32f10x.h"
#include "my_pwm.h"
#include "PIN_config.h"

#include "My_usart.h"

//#define DEBUG

/* globalni promenne RTC */
extern uint8_t Time_Hodiny;
extern uint8_t Time_Minuty;
extern uint8_t Time_Vteriny;
extern uint8_t Time_Den;
extern uint8_t Time_Mesic;
extern uint16_t Time_Rok;
extern uint8_t Time_DenVTydnu;
extern uint16_t SummerTimeCorrect;

extern uint32_t RunTime;
extern uint32_t LED_Time[6];

u8 *JmenaMesicu[]={"Leden   ","Unor    ","Brezen  ","Duben   ","Kveten  ","Cerven  ","Cervenec","Srpen   ",\
"Zari    ","Rijen   ","Listopad","Prosinec"};
const char *JmenaDni[]={"Pondeli","Utery  ","Streda ","Ctvrtek","Patek  ","Sobota ","Nedele "};

static uint8_t PredchoziVterina=0;
/* --------------------------------------
   Inicializace interniho RTC
   ----------------------------------- */
void RTC_Init()
{  
  #ifdef DEBUG
  	USART1_Send("RTC - Init", 1);
  	USART1_Send("> clock on", 1);
  #endif
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | \
  						 RCC_APB1Periph_BKP, ENABLE);	// Pusteni hodin
  #ifdef DEBUG
  	USART1_Send("> BKP acces on", 1);
  #endif
  PWR_BackupAccessCmd(ENABLE);							// Povolime si pristup do BKP
  
  // RTC hodiny vychazeji z frekvence externiho oscilátoru 32 kHz  
  #ifdef DEBUG
  	USART1_Send("> LSE on", 1);
  #endif
  RCC_LSEConfig(RCC_LSE_ON);							// povolime LSE oscilator
  #ifdef DEBUG
  	USART1_Send("> while - ready", 1);
  #endif
  while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);	// pockame na spusteni oscilatoru 32kHz
  #ifdef DEBUG
  	USART1_Send("> LSE source", 1);
  #endif
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);				// nyni muzeme priradit hodinam jako zdroj kmitoctu LSE oscilator
  #ifdef DEBUG
  	USART1_Send("> RTC CLK on", 1);
  #endif
  RCC_RTCCLKCmd(ENABLE);								// povolime vstup kmitocku do RTC
  #ifdef DEBUG
  	USART1_Send("> Wait for synch.", 1);
  #endif
  RTC_WaitForSynchro();									// Cekej na synchronizaci
  #ifdef DEBUG
  	USART1_Send("> Wait for last task", 1);
  #endif
  RTC_WaitForLastTask();								// Pockame na dokonceni zapisu
  #ifdef DEBUG
  	USART1_Send("> Sec IT off", 1);
  #endif
  RTC_ITConfig(RTC_IT_SEC,DISABLE);						// Deaktivace preruseni na kazdou vterinu (prepocet se musi spoustet manualne v kodu)
  #ifdef DEBUG
  	USART1_Send("> Set prescaler", 1);
  #endif
  RTC_SetPrescaler(32767); 								// Nastaveni rychlosti pro LSE
  #ifdef DEBUG
  	USART1_Send("> Wait for last task", 1);
  #endif
  RTC_WaitForLastTask();								// Pockame na dokonceni zapisu
//  RCC_ClearFlag();										// Vymazeme reset flagy
	
  #ifdef DEBUG
  	USART1_Send("> BKP init", 1);
  #endif
  // Inicializace zalohove pameti RTC
  if(BKP_ReadBackupRegister(Backup_Set)==CONFIGURATION_RESET)
  {
  	BKP_DeInit();										// Vymaze zalohovanou pamet !!!
    Time_Mesic=DEFAULT_MONTH ;
    Time_Den=DEFAULT_DAY;
    Time_Rok=DEFAULT_YEAR;
    SummerTimeCorrect = OCTOBER_FLAG_SET;
	#ifdef DEBUG
  		USART1_Send("> BKP write LC", 1);
  	#endif
    BKP_WriteBackupRegister(Backup_LetniCas,SummerTimeCorrect);
    #ifdef DEBUG
  		USART1_Send("> BKP write Day", 1);
  	#endif
	BKP_WriteBackupRegister(Backup_Den,Time_Mesic);
	#ifdef DEBUG
  		USART1_Send("> BKP write Month", 1);
  	#endif
    BKP_WriteBackupRegister(Backup_Mesic,Time_Den);
	#ifdef DEBUG
  		USART1_Send("> BKP write Year", 1);
  	#endif
    BKP_WriteBackupRegister(Backup_Rok,Time_Rok);
	#ifdef DEBUG
  		USART1_Send("> BKP write set mkr", 1);
  	#endif
    BKP_WriteBackupRegister(Backup_Set, CONFIGURATION_DONE);
	
	BKP_WriteBackupRegister(Backup_LED1_time,0);
	BKP_WriteBackupRegister(Backup_LED2_time,0);
	BKP_WriteBackupRegister(Backup_LED3_time,0);
	BKP_WriteBackupRegister(Backup_LED4_time,0);
	BKP_WriteBackupRegister(Backup_LED5_time,0);
	BKP_WriteBackupRegister(Backup_LED6_time,0);	
  }

  #ifdef DEBUG
  	USART1_Send("> Check for elapsed days", 1);
  #endif
  CheckForDaysElapsed();								// Posune pocet dni podle toho kolik uplynulo vterin od posledniho vypnuti
  Time_Mesic=BKP_ReadBackupRegister(Backup_Den);
  Time_Rok=BKP_ReadBackupRegister(Backup_Rok);
  Time_Den=BKP_ReadBackupRegister(Backup_Mesic);
  
  LED_Time[0]=BKP_ReadBackupRegister(Backup_LED1_time)*60;
  LED_Time[1]=BKP_ReadBackupRegister(Backup_LED2_time)*60;
  LED_Time[2]=BKP_ReadBackupRegister(Backup_LED3_time)*60;
  LED_Time[3]=BKP_ReadBackupRegister(Backup_LED4_time)*60;
  LED_Time[4]=BKP_ReadBackupRegister(Backup_LED5_time)*60;
  LED_Time[5]=BKP_ReadBackupRegister(Backup_LED6_time)*60;
  #ifdef DEBUG
  	USART1_Send("> Exiting RTC init", 1);
  #endif
}

/* --------------------------------------
   Prepocitani RTC citace na datum a cas
   ----------------------------------- */
void RTC_Update()
{
	uint32_t RTC_Hodnota = RTC_GetCounter();	

	Time_Hodiny = RTC_Hodnota / 3600;
	Time_Minuty = (RTC_Hodnota % 3600) / 60;
	Time_Vteriny = (RTC_Hodnota % 3600) % 60;
	
	if(((uint8_t)RTC_Hodnota>PredchoziVterina) | ((uint8_t)RTC_Hodnota)==0)
	{
		RunTime++;											// Pocitani doby behu zarizeni (ve vterinach)
		if(PWM_Get_LED_1()>0)
		{
			LED_Time[0]++;			
			BKP_WriteBackupRegister(Backup_LED1_time,LED_Time[0]/60);			
		}
		if(PWM_Get_LED_2()>0)
		{
			LED_Time[1]++;			
			BKP_WriteBackupRegister(Backup_LED2_time,LED_Time[1]/60);
		}
		if(PWM_Get_LED_3()>0)
		{
			LED_Time[2]++;
			BKP_WriteBackupRegister(Backup_LED3_time,LED_Time[2]/60);
		}
		if(PWM_Get_LED_4()>0)
		{
			LED_Time[3]++;
			BKP_WriteBackupRegister(Backup_LED4_time,LED_Time[3]/60);
		}
		if(PWM_Get_LED_5()>0)
		{
			LED_Time[4]++;
			BKP_WriteBackupRegister(Backup_LED5_time,LED_Time[4]/60);
		}
		if(PWM_Get_LED6()==1)
		{
			LED_Time[5]++;
			BKP_WriteBackupRegister(Backup_LED6_time,LED_Time[5]/60);
		}
	}
	PredchoziVterina = (uint8_t)RTC_Hodnota;
}
/* --------------------------------------
   Nacte datum z backup registru a opravi
   v nem preteceni (dalsi mesic, rok)
   ----------------------------------- */
void DateUpdate(void)
{
  Time_Mesic=BKP_ReadBackupRegister(Backup_Den);
  Time_Rok=BKP_ReadBackupRegister(Backup_Rok);
  Time_Den=BKP_ReadBackupRegister(Backup_Mesic);
  
  if(Time_Mesic == 1 || Time_Mesic == 3 || Time_Mesic == 5 || Time_Mesic == 7 ||\
     Time_Mesic == 8 || Time_Mesic == 10 || Time_Mesic == 12)
  {
    if(Time_Den < 31)
    {
      Time_Den++;
    }
    /* Date structure member: Time_Mesic = 31 */
    else
    {
      if(Time_Mesic != 12)
      {
        Time_Mesic++;
        Time_Den = 1;
      }
     /* Date structure member: Time_Den = 31 & Time_Mesic =12 */
      else
      {
        Time_Mesic = 1;
        Time_Den = 1;
        Time_Rok++;
      }
    }
  }
  else if(Time_Mesic == 4 || Time_Mesic == 6 || Time_Mesic == 9 ||Time_Mesic == 11)
  {
    if(Time_Den < 30)
    {
      Time_Den++;
    }
    /* Date structure member: Time_Den = 30 */
    else
    {
      Time_Mesic++;
      Time_Den = 1;
    }
  }
  else if(Time_Mesic == 2)
  {
    if(Time_Den < 28)
    {
      Time_Den++;
    }
    else if(Time_Den == 28)
    {
      /* Korekce prestupneho roku */
      if(CheckLeap(Time_Rok))
      {
        Time_Rok++;
      }
      else
      {
        Time_Mesic++;
        Time_Den = 1;
      }
    }
    else if(Time_Den == 29)
    {
      Time_Mesic++;
      Time_Den = 1;
    }
  }
  
  Time_DenVTydnu=WeekDayEurope(Time_Rok,Time_Mesic,Time_Den);					// Vypocet dne v tydnu na zaklade datumu

  BKP_WriteBackupRegister(Backup_Den,Time_Mesic);
  BKP_WriteBackupRegister(Backup_Mesic,Time_Den);
  BKP_WriteBackupRegister(Backup_Rok,Time_Rok);
}

/* ------------------------------------------
   Funkce pro overeni jestli je rok prestupny
   nebo ne
   --------------------------------------- */
uint8_t CheckLeap(uint16_t Year)
{
  if((Year%400)==0)
  {
    return LEAP;
  }
  else if((Year%100)==0)
  {
    return NOT_LEAP;
  }
  else if((Year%4)==0)
  {
    return LEAP;
  }
  else
  {
    return NOT_LEAP;
  }
}
/* -----------------------------------------
   Vrati den v tydnu na zakale zadaneho data   
   0 = Pondeli
   1 = Utery ...
   6 = Nedele
   -------------------------------------- */
uint8_t WeekDayEurope(uint16_t CurrentYear,uint8_t CurrentMonth,uint8_t CurrentDay)
{	
	uint8_t DenVTydnu = WeekDay( CurrentYear, CurrentMonth, CurrentDay);
	if (DenVTydnu==0) DenVTydnu=6;
	else if (DenVTydnu>0) DenVTydnu--;
	return DenVTydnu;
}

/* -----------------------------------------
   Vrati den v tydnu na zakale zadaneho data
   0 = Nedele
   1 = Pondeli ...
   6 = Sobota
   -------------------------------------- */
uint8_t WeekDay(uint16_t CurrentYear,uint8_t CurrentMonth,uint8_t CurrentDay)
{
  uint16_t Temp1,Temp2,Temp3,Temp4,CurrentWeekDay;
  
  if(CurrentMonth < 3)
  {
    CurrentMonth=CurrentMonth + 12;
    CurrentYear=CurrentYear-1;
  }
  
  Temp1=(6*(CurrentMonth + 1))/10;
  Temp2=CurrentYear/4;
  Temp3=CurrentYear/100;
  Temp4=CurrentYear/400;
  CurrentWeekDay=CurrentDay + (2 * CurrentMonth) + Temp1 \
     + CurrentYear + Temp2 - Temp3 + Temp4 +1;
  CurrentWeekDay = CurrentWeekDay % 7;
  
  return(CurrentWeekDay);
}

/* ----------------------------------------
   Rutina pro korekci letniho casu
   ------------------------------------- */
void SummerTimeCorrection(uint8_t NastavitCas)
{
  uint8_t CorrectionPending=0;
  uint8_t CheckCorrect=0;
  
  if((SummerTimeCorrect & OCTOBER_FLAG_SET)!=0)
  {
    if((Time_Mesic==10) && (Time_Den >24 ))
    {
      for(CheckCorrect = 25;CheckCorrect <=Time_Den;CheckCorrect++)
      {
        if(WeekDay(Time_Rok,Time_Mesic,CheckCorrect )==0)
        {
          if(CheckCorrect == Time_Den)
          {
            /* Check if Time is greater than equal to 1:59:59 */
            if(RTC_GetCounter()>=7199)
            {
              CorrectionPending=1;
            }
          }
          else
          {
            CorrectionPending=1;
          }
         break;
       }
     }
   }
   else if((Time_Mesic > 10))
   {
     CorrectionPending=1;
   }
   else if(Time_Mesic < 3)
   {
     CorrectionPending=1;
   }
   else if(Time_Mesic == 3)
   {
     if(Time_Den<24)
     {
       CorrectionPending=1;
     }
     else
     {
       for(CheckCorrect=24;CheckCorrect<=Time_Den;CheckCorrect++)
       {
         if(WeekDay(Time_Rok,Time_Mesic,CheckCorrect)==0)
         {
           if(CheckCorrect == Time_Den)
           {
             /*Check if Time is less than 1:59:59 and year is not the same in which
                March correction was done */
             if((RTC_GetCounter() < 7199) && ((SummerTimeCorrect & 0x3FFF) != \
                                   Time_Rok))
             {
               CorrectionPending=1;
             }
             else
             {
               CorrectionPending=0;
             }
             break;
            }
            else
            {
              CorrectionPending=1;
            }
          }
        }
      }
    }
  }
  else if((SummerTimeCorrect & MARCH_FLAG_SET)!=0)
  {
    if((Time_Mesic == 3) && (Time_Den >24 ))
    {
      for(CheckCorrect = 25;CheckCorrect <=Time_Den;\
         CheckCorrect++)
      {
        if(WeekDay(Time_Rok,Time_Mesic,\
           CheckCorrect )==0)
        {
          if(CheckCorrect == Time_Den)
          {
            /*Check if time is greater than equal to 1:59:59 */
            if(RTC_GetCounter()>=7199)
            {
              CorrectionPending=1;
            }
          }
          else
          {
            CorrectionPending=1;
          }
        break;
        }
      }
    }
    else if((Time_Mesic > 3) && (Time_Mesic < 10 ))
    {
      CorrectionPending=1;
    }
    else if(Time_Mesic ==10)
    {
      if(Time_Den<24)
      {
        CorrectionPending=1;
      }
      else
      {
        for(CheckCorrect=24;CheckCorrect<=Time_Den;\
          CheckCorrect++)
        {
          if(WeekDay(Time_Rok,Time_Mesic,\
            CheckCorrect)==0)
          {
            if(CheckCorrect == Time_Den)
            {
              /*Check if Time is less than 1:59:59 and year is not the same in
              which March correction was done */
              if((RTC_GetCounter() < 7199) && \
                ((SummerTimeCorrect & 0x3FFF) != Time_Rok))
              {
                CorrectionPending=1;
              }
              else
              {
                CorrectionPending=0;
              }
            break;
            }
          }
        }
      }
    }
  }

  if(CorrectionPending==1)
  {
    if((SummerTimeCorrect & OCTOBER_FLAG_SET)!=0)
    {
      /* Subtract 1 hour from the current time */
      if(NastavitCas)
	  {
	  	RTC_SetCounter(RTC_GetCounter() - 3599);
	  }
      /* Reset October correction flag */
      SummerTimeCorrect &= 0xBFFF;
      /* Set March correction flag  */
      SummerTimeCorrect |= MARCH_FLAG_SET;
      SummerTimeCorrect |= Time_Rok;
      BKP_WriteBackupRegister(BKP_DR7,SummerTimeCorrect);
    }
    else if((SummerTimeCorrect & MARCH_FLAG_SET)!=0)
    {
     /* Add 1 hour to current time */
     if(NastavitCas)
	 {
	 	RTC_SetCounter(RTC_GetCounter() + 3601);
     }
	 /* Reset March correction flag */
     SummerTimeCorrect &= 0x7FFF;
     /* Set October correction flag  */
     SummerTimeCorrect |= OCTOBER_FLAG_SET;
     SummerTimeCorrect |= Time_Rok;
     BKP_WriteBackupRegister(BKP_DR7,SummerTimeCorrect);
    }
  }
}

/* --------------------------------------------
   Zkontroluje kolik uplynulo dni od posledniho
   vypnuti a podle toho posune datum
   ----------------------------------------- */
void CheckForDaysElapsed(void)
{
  u8 DaysElapsed;
 
  if((RTC_GetCounter() / SECONDS_IN_DAY) != 0)
  {
    for(DaysElapsed = 0; DaysElapsed < (RTC_GetCounter() / SECONDS_IN_DAY); DaysElapsed++)
    {
      DateUpdate();
    }
	RTC_WaitForLastTask();
    RTC_SetCounter(RTC_GetCounter() % SECONDS_IN_DAY);
	RTC_WaitForLastTask();
  }
}

/* -------------------------------------------
   Nastaveni casu do citace RTC
   ---------------------------------------- */
void SetTime(uint8_t Hour,uint8_t Minute,uint8_t Seconds)
{
  uint32_t CounterValue;

  CounterValue=(((uint32_t)Hour * 3600)+ ((uint32_t)Minute * 60)+(uint32_t)Seconds);

  #ifdef DEBUG
  	USART1_Send(" > Wait", 1);
  #endif
  RTC_WaitForLastTask();
  #ifdef DEBUG
  	USART1_Send(" > Set RTC counter:", 0);
	USART1_SendInteger(CounterValue);
	USART1_Send_Enter();
  #endif
  RTC_SetCounter(CounterValue);
  #ifdef DEBUG
  	USART1_Send(" > RTC counter: ", 0);
	USART1_SendInteger(RTC_GetCounter());
	USART1_Send_Enter();
  #endif
  #ifdef DEBUG
  	USART1_Send(" > Wait", 1);
  #endif
  RTC_WaitForLastTask();
  RTC_Update();
}

/* -------------------------------------------
   Nastaveni casu do citace RTC
   ---------------------------------------- */
void SetTimeRAW(uint32_t timeRAW)
{
  #ifdef DEBUG
  	USART1_Send(" > Wait", 1);
  #endif
  RTC_WaitForLastTask();
  #ifdef DEBUG
  	USART1_Send(" > Set RTC counter:", 0);
	USART1_SendInteger(timeRAW);
	USART1_Send_Enter();
  #endif
  RTC_SetCounter(timeRAW);
  #ifdef DEBUG
  	USART1_Send(" > RTC counter: ", 0);
	USART1_SendInteger(RTC_GetCounter());
	USART1_Send_Enter();
	USART1_Send(" > Wait", 1);
  #endif
  RTC_WaitForLastTask();  
  RTC_Update(); 
}

/* -------------------------------------------
   Vrati cas v RAW formatu
   ---------------------------------------- */
uint32_t GetTimeRAW(void)
{
	return(RTC_GetCounter());
}

/* --------------------------------------------
 Nastaveni datumu
 ------------------------------------------- */
void RTC_SetDate(uint8_t Den, uint8_t Mesic, uint16_t Rok)
{       
	if(Mesic!=0)
	{
		#ifdef DEBUG
	  		USART1_Send("> BKP write Month: ", 0);
			USART1_SendInteger(Mesic);
			USART1_Send_Enter();
	  	#endif
		BKP_WriteBackupRegister(Backup_Den,Mesic);
		Time_Mesic=Mesic;
	}
	if(Den!=0)
	{
		#ifdef DEBUG
	  		USART1_Send("> BKP write Day: ", 0);
			USART1_SendInteger(Den);
			USART1_Send_Enter();
	  	#endif
	    BKP_WriteBackupRegister(Backup_Mesic,Den);
		Time_Den=Den;
	}
	if(Rok!=0)
	{
		#ifdef DEBUG
	  		USART1_Send("> BKP write Year: ", 0);
			USART1_SendInteger(Rok);
			USART1_Send_Enter();
	  	#endif
	    BKP_WriteBackupRegister(Backup_Rok,Rok);
		Time_Rok=Rok;
		#ifdef DEBUG
  			USART1_Send("> BKP write LC", 1);
	  	#endif
		SummerTimeCorrection(0); 
	}   
}
/* --------------------------------------------
  
 ------------------------------------------- */
void RTC_UpdateSec(void)
{
	uint8_t Month,Day;
  	uint16_t Year;
  
  	Month = BKP_ReadBackupRegister(Backup_Den);
    Day = BKP_ReadBackupRegister(Backup_Mesic);
    Year = BKP_ReadBackupRegister(Backup_Rok);

	/* If counter is equal to 86399: one day was elapsed */
	/* This takes care of date change and resetting of counter in case of
	power on - Run mode/ Main supply switched on condition*/
	if(RTC_GetCounter() == 86399)
	{
	  RTC_WaitForLastTask();		    // Wait until last write operation on RTC registers has finished    
	  RTC_SetCounter(0x0);			    // Reset counter value
	  RTC_WaitForLastTask();			// Wait until last write operation on RTC registers has finished
	
	  /* Increment the date */
	  DateUpdate();
	}
	/* Osetreni letniho casu - v breznu prida hodinu, v rijnu ji zase odebere */
	if((RTC_GetCounter()/3600 == 1)&&(((RTC_GetCounter()%3600)/60) == 59)&&
     (((RTC_GetCounter()%3600)%60) == 59))
	{
	  /* March Correction */
	  if((Month == 3) && (Day >24))
	  {
	    if(WeekDay(Year,Month,Day)==0)
	    {
	      if((SummerTimeCorrect & 0x8000) == 0x8000)
	      {
	        RTC_SetCounter(RTC_GetCounter() + 3601);
	       
	        /* Reset March correction flag */
	        SummerTimeCorrect &= 0x7FFF;
	       
	        /* Set October correction flag  */
	        SummerTimeCorrect |= 0x4000;
	        SummerTimeCorrect |= Year;
	        BKP_WriteBackupRegister(Backup_LetniCas,SummerTimeCorrect);
	      }
	    }
	  }
	  /* October Correction */
	  if((Month == 10) && (Day >24))
	  {
	    if(WeekDay(Year,Month,Day)==0)
	    {
	      if((SummerTimeCorrect & 0x4000) == 0x4000)
	      {
	        RTC_SetCounter(RTC_GetCounter() - 3599);
	        
	        /* Reset October correction flag */
	        SummerTimeCorrect &= 0xBFFF;
	        
	        /* Set March correction flag  */
	        SummerTimeCorrect |= 0x8000;
	        SummerTimeCorrect |= Year;
	        BKP_WriteBackupRegister(BKP_DR7,SummerTimeCorrect);
	      }
	    }
	  }
	}
	
	RTC_Update();						// Prepocita z citace do casu HH:MM:SS
	Time_DenVTydnu=WeekDayEurope(Time_Rok,Time_Mesic,Time_Den);			// Aktualizace dne v tydnu
}
/* Konec souboru*/
